将可视化层与 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 类提供以下方法:

ElementCompositionPreview.GetElementVisual 上的备注

ElementCompositionPreview.GetElementVisual 返回用于呈现给定 UIElement 的“讲义”视觉对象。 Visual.OpacityVisual.OffsetVisual.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 方法

图像TextBlockShape 均实现一个名为 GetAlphaMask 的方法,该方法返回一个 CompositionBrush,表示具有元素形状的灰度图像。 此 CompositionBrush 可用作合成 DropShadow 的输入,因此阴影可以反映元素的形状,而不是矩形。 这为文本、带有 alpha 和形状的图像启用像素完美、基于轮廓的阴影。 有关此 API 的示例,请参阅 下面的投影

秘籍

重新定位动画

使用合成隐式动画,开发人员可以相对于元素父元素的布局自动对更改进行动画处理。 例如,如果更改 下面的按钮的边距 ,它将自动对其新布局位置进行动画处理。

实现概述

  1. 获取目标元素的讲义视觉对象
  2. 创建一个 ImplicitAnimationCollection,自动对 Offset 属性中的更改进行动画处理
  3. 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

实现概述

  1. 获取主机元素的讲义视觉对象
  2. 创建 Windows.UI.Composition DropShadow
  3. DropShadow 配置为通过掩码从目标元素中获取其形状
    • DropShadow 默认为矩形,因此,如果目标为矩形,则不需要这样做
  4. 将阴影附加到新的 SpriteVisual,并将 SpriteVisual 设置为主机元素的子元素
  5. 使用 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++/WinRTC++/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 主页

实现概述

  1. 获取主机元素的讲义视觉对象
  2. 使用 Win2D 和 CompositionEffectSourceParameter 创建模糊效果树
  3. 基于效果树创建 CompositionEffectBrush
  4. 将 CompositionEffectBrush 的输入设置为 CompositionBackdropBrush,该输入允许将效果应用于 SpriteVisual 背后的内容
  5. 将 CompositionEffectBrush 设置为新 SpriteVisual 的内容,并将该 SpriteVisual 设置为主机元素的子元素。 也可以使用 XamlCompositionBrushBase。
  6. 使用 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);
}

其他资源