将可视化层与 XAML 结合使用
大多数使用视觉层功能的应用都将使用 XAML 来定义主要 UI 内容。 在 Windows 10 周年更新中,XAML 框架和视觉层中新增了一些新功能,可更轻松地组合这两种技术来创建令人惊叹的用户体验。 XAML 和视觉层互操作功能可用于创建不能单独使用 XAML API 的高级动画和效果。 这包括:
- 画笔效果,如模糊和霜冻玻璃
- 动态照明效果
- 滚动驱动的动画和视差
- 自动布局动画
- 像素完美投影
这些效果和动画可以应用于现有 XAML 内容,因此无需大幅调整 XAML 应用以利用新功能。 下面的“食谱”部分介绍了布局动画、阴影和模糊效果。 有关实现视差的代码示例,请参阅 ParallaxingListItems 示例。 WindowsCompositionSamples 存储库还包含用于实现动画、阴影和效果的其他几个示例。
XamlCompositionBrushBase 类
XamlCompositionBrush 为使用 CompositionBrush 绘制区域的 XAML 画笔提供基类。 这可用于轻松地将合成效果(如模糊或霜冻玻璃)应用于 XAML UI 元素。
有关将画笔与 XAML UI 配合使用的详细信息,请参阅“画笔”部分。
有关代码示例,请参阅 XamlCompositionBrushBase 的参考页面。
XamlLight 类
XamlLight 为 XAML 照明效果提供了基类,该效果使用 CompositionLight 动态照亮区域。
有关使用灯光的详细信息,请参阅“照明”部分,包括照明 XAML UI 元素。
有关代码示例,请参阅 XamlLight 的参考页。
ElementCompositionPreview 类
ElementCompositionPreview 是一个静态类,提供 XAML 和 Visual Layer 互操作功能。 有关视觉层及其功能的概述,请参阅 视觉层。 ElementCompositionPreview 类提供以下方法:
- GetElementVisual:获取用于呈现此元素的“讲义”视觉对象
- SetElementChildVisual:将“handin”视觉对象设置为此元素可视化树的最后一个子级。 此视觉对象将绘制在元素的其余部分之上。
- GetElementChildVisual:使用 SetElementChildVisual 检索视觉对象集
- GetScrollViewerManipulationPropertySet:获取一个对象,该对象可用于基于 ScrollViewer 中的 滚动偏移量创建 60fps 动画
ElementCompositionPreview.GetElementVisual 上的备注
ElementCompositionPreview.GetElementVisual 返回用于呈现给定 UIElement 的“讲义”视觉对象。 Visual.Opacity、Visual.Offset 和 Visual.Size 等属性由 XAML 框架根据 UIElement 的状态设置。 这可实现隐式重新定位动画(请参阅 “食谱”)等技术。
请注意,由于 偏移 量和 大小 设置为 XAML 框架布局的结果,因此在修改或设置这些属性动画时,开发人员应小心。 当元素的左上角与父元素在布局中具有相同的位置时,开发人员才应修改或设置 Offset 动画。 通常不应修改大小,但访问属性可能很有用。 例如,下面的 Drop Shadow 和 Frosted Glass 示例使用讲义视觉对象的大小作为动画的输入。
作为其他注意事项,讲义视觉对象的更新属性不会反映在相应的 UIElement 中。 例如,将 UIElement.Opacity 设置为 0.5 会将相应的讲义视觉对象的 Opacity 设置为 0.5。 但是,将讲义视觉对象的 不透明度 设置为 0.5 将导致内容显示为 50% 不透明度,但不会更改相应 UIElement 的 Opacity 属性的值。
偏移动画示例
不正确
<Border>
<Image x:Name="MyImage" Margin="5" />
</Border>
// Doesn’t work because Image has a margin!
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);
正确
<Border>
<Canvas Margin="5">
<Image x:Name="MyImage" />
</Canvas>
</Border>
// This works because the Canvas parent doesn’t generate a layout offset.
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);
ElementCompositionPreview.SetElementChildVisual 方法
ElementCompositionPreview.SetElementChildVisual 允许开发人员提供“handin”视觉对象,该视觉对象将作为元素可视化树的一部分显示。 这样,开发人员就可以创建一个“合成岛”,其中基于视觉的内容可以显示在 XAML UI 中。 开发人员应该保守地使用此技术,因为基于视觉对象的内容不会具有相同的 XAML 内容的辅助功能和用户体验保证。 因此,通常建议仅在必要时使用此技术来实现自定义效果,如下面的“食谱”部分中找到的效果。
GetAlphaMask 方法
图像、TextBlock 和 Shape 均实现一个名为 GetAlphaMask 的方法,该方法返回一个 CompositionBrush,表示具有元素形状的灰度图像。 此 CompositionBrush 可用作合成 DropShadow 的输入,因此阴影可以反映元素的形状,而不是矩形。 这为文本、带有 alpha 和形状的图像启用像素完美、基于轮廓的阴影。 有关此 API 的示例,请参阅 下面的投影 。
秘籍
重新定位动画
使用合成隐式动画,开发人员可以相对于元素父元素的布局自动对更改进行动画处理。 例如,如果更改 下面的按钮的边距 ,它将自动对其新布局位置进行动画处理。
实现概述
- 获取目标元素的讲义视觉对象
- 创建一个 ImplicitAnimationCollection,自动对 Offset 属性中的更改进行动画处理
- 将 ImplicitAnimationCollection 与后盾视觉对象相关联
<Button x:Name="RepositionTarget" Content="Click Me" />
public MainPage()
{
InitializeComponent();
InitializeRepositionAnimation(RepositionTarget);
}
private void InitializeRepositionAnimation(UIElement repositionTarget)
{
var targetVisual = ElementCompositionPreview.GetElementVisual(repositionTarget);
Compositor compositor = targetVisual.Compositor;
// Create an animation to animate targetVisual's Offset property to its final value
var repositionAnimation = compositor.CreateVector3KeyFrameAnimation();
repositionAnimation.Duration = TimeSpan.FromSeconds(0.66);
repositionAnimation.Target = "Offset";
repositionAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
// Run this animation when the Offset Property is changed
var repositionAnimations = compositor.CreateImplicitAnimationCollection();
repositionAnimations["Offset"] = repositionAnimation;
targetVisual.ImplicitAnimations = repositionAnimations;
}
投影
将像素完美的投影应用于 UIElement,例如 包含图片的椭圆 。 由于阴影需要由应用创建的 SpriteVisual,因此我们需要创建一个包含 SpriteVisual 的“host”元素,该元素将使用 ElementCompositionPreview.SetElementChildVisual。
实现概述
- 获取主机元素的讲义视觉对象
- 创建 Windows.UI.Composition DropShadow
- 将 DropShadow 配置为通过掩码从目标元素中获取其形状
- DropShadow 默认为矩形,因此,如果目标为矩形,则不需要这样做
- 将阴影附加到新的 SpriteVisual,并将 SpriteVisual 设置为主机元素的子元素
- 使用 ExpressionAnimation 将 SpriteVisual 的大小绑定到主机的大小
<Grid Width="200" Height="200">
<Canvas x:Name="ShadowHost" />
<Ellipse x:Name="CircleImage">
<Ellipse.Fill>
<ImageBrush ImageSource="Assets/Images/2.jpg" Stretch="UniformToFill" />
</Ellipse.Fill>
</Ellipse>
</Grid>
public MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost, CircleImage);
}
private void InitializeDropShadow(UIElement shadowHost, Shape shadowTarget)
{
Visual hostVisual = ElementCompositionPreview.GetElementVisual(shadowHost);
Compositor compositor = hostVisual.Compositor;
// Create a drop shadow
var dropShadow = compositor.CreateDropShadow();
dropShadow.Color = Color.FromArgb(255, 75, 75, 80);
dropShadow.BlurRadius = 15.0f;
dropShadow.Offset = new Vector3(2.5f, 2.5f, 0.0f);
// Associate the shape of the shadow with the shape of the target element
dropShadow.Mask = shadowTarget.GetAlphaMask();
// Create a Visual to hold the shadow
var shadowVisual = compositor.CreateSpriteVisual();
shadowVisual.Shadow = dropShadow;
// Add the shadow as a child of the host in the visual tree
ElementCompositionPreview.SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
shadowVisual.StartAnimation("Size", bindSizeAnimation);
}
下面两个列表显示了采用相同 XAML 结构的旧版 C# 代码的 C++/WinRT 和 C++/CX 等效内容。
#include <winrt/Windows.UI.Composition.h>
#include <winrt/Windows.UI.Xaml.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include <winrt/Windows.UI.Xaml.Shapes.h>
...
MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost(), CircleImage());
}
int32_t MyProperty();
void MyProperty(int32_t value);
void InitializeDropShadow(Windows::UI::Xaml::UIElement const& shadowHost, Windows::UI::Xaml::Shapes::Shape const& shadowTarget)
{
auto hostVisual{ Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
auto compositor{ hostVisual.Compositor() };
// Create a drop shadow
auto dropShadow{ compositor.CreateDropShadow() };
dropShadow.Color(Windows::UI::ColorHelper::FromArgb(255, 75, 75, 80));
dropShadow.BlurRadius(15.0f);
dropShadow.Offset(Windows::Foundation::Numerics::float3{ 2.5f, 2.5f, 0.0f });
// Associate the shape of the shadow with the shape of the target element
dropShadow.Mask(shadowTarget.GetAlphaMask());
// Create a Visual to hold the shadow
auto shadowVisual = compositor.CreateSpriteVisual();
shadowVisual.Shadow(dropShadow);
// Add the shadow as a child of the host in the visual tree
Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
auto bindSizeAnimation{ compositor.CreateExpressionAnimation(L"hostVisual.Size") };
bindSizeAnimation.SetReferenceParameter(L"hostVisual", hostVisual);
shadowVisual.StartAnimation(L"Size", bindSizeAnimation);
}
#include "WindowsNumerics.h"
MainPage::MainPage()
{
InitializeComponent();
InitializeDropShadow(ShadowHost, CircleImage);
}
void MainPage::InitializeDropShadow(Windows::UI::Xaml::UIElement^ shadowHost, Windows::UI::Xaml::Shapes::Shape^ shadowTarget)
{
auto hostVisual = Windows::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost);
auto compositor = hostVisual->Compositor;
// Create a drop shadow
auto dropShadow = compositor->CreateDropShadow();
dropShadow->Color = Windows::UI::ColorHelper::FromArgb(255, 75, 75, 80);
dropShadow->BlurRadius = 15.0f;
dropShadow->Offset = Windows::Foundation::Numerics::float3(2.5f, 2.5f, 0.0f);
// Associate the shape of the shadow with the shape of the target element
dropShadow->Mask = shadowTarget->GetAlphaMask();
// Create a Visual to hold the shadow
auto shadowVisual = compositor->CreateSpriteVisual();
shadowVisual->Shadow = dropShadow;
// Add the shadow as a child of the host in the visual tree
Windows::UI::Xaml::Hosting::ElementCompositionPreview::SetElementChildVisual(shadowHost, shadowVisual);
// Make sure size of shadow host and shadow visual always stay in sync
auto bindSizeAnimation = compositor->CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation->SetReferenceParameter("hostVisual", hostVisual);
shadowVisual->StartAnimation("Size", bindSizeAnimation);
}
霜化玻璃
创建模糊和淡化背景内容的效果。 请注意,开发人员需要安装 Win2D NuGet 包才能使用效果。 有关安装说明, 请参阅 Win2D 主页 。
实现概述
- 获取主机元素的讲义视觉对象
- 使用 Win2D 和 CompositionEffectSourceParameter 创建模糊效果树
- 基于效果树创建 CompositionEffectBrush
- 将 CompositionEffectBrush 的输入设置为 CompositionBackdropBrush,该输入允许将效果应用于 SpriteVisual 背后的内容
- 将 CompositionEffectBrush 设置为新 SpriteVisual 的内容,并将该 SpriteVisual 设置为主机元素的子元素。 也可以使用 XamlCompositionBrushBase。
- 使用 ExpressionAnimation 将 SpriteVisual 的大小绑定到主机的大小
<Grid Width="300" Height="300" Grid.Column="1">
<Image
Source="Assets/Images/2.jpg"
Width="200"
Height="200" />
<Canvas
x:Name="GlassHost"
Width="150"
Height="300"
HorizontalAlignment="Right" />
</Grid>
public MainPage()
{
InitializeComponent();
InitializeFrostedGlass(GlassHost);
}
private void InitializeFrostedGlass(UIElement glassHost)
{
Visual hostVisual = ElementCompositionPreview.GetElementVisual(glassHost);
Compositor compositor = hostVisual.Compositor;
// Create a glass effect, requires Win2D NuGet package
var glassEffect = new GaussianBlurEffect
{
BlurAmount = 15.0f,
BorderMode = EffectBorderMode.Hard,
Source = new ArithmeticCompositeEffect
{
MultiplyAmount = 0,
Source1Amount = 0.5f,
Source2Amount = 0.5f,
Source1 = new CompositionEffectSourceParameter("backdropBrush"),
Source2 = new ColorSourceEffect
{
Color = Color.FromArgb(255, 245, 245, 245)
}
}
};
// Create an instance of the effect and set its source to a CompositionBackdropBrush
var effectFactory = compositor.CreateEffectFactory(glassEffect);
var backdropBrush = compositor.CreateBackdropBrush();
var effectBrush = effectFactory.CreateBrush();
effectBrush.SetSourceParameter("backdropBrush", backdropBrush);
// Create a Visual to contain the frosted glass effect
var glassVisual = compositor.CreateSpriteVisual();
glassVisual.Brush = effectBrush;
// Add the blur as a child of the host in the visual tree
ElementCompositionPreview.SetElementChildVisual(glassHost, glassVisual);
// Make sure size of glass host and glass visual always stay in sync
var bindSizeAnimation = compositor.CreateExpressionAnimation("hostVisual.Size");
bindSizeAnimation.SetReferenceParameter("hostVisual", hostVisual);
glassVisual.StartAnimation("Size", bindSizeAnimation);
}
其他资源
- 视觉层概述
- ElementCompositionPreview 类
- WindowsCompositionSamples GitHub 中的 高级 UI 和合成示例
- BasicXamlInterop 示例
- ParallaxingListItems 示例