将可视化层与 XAML 结合使用Using the Visual Layer with XAML

使用可视化层功能的大多数应用都将使用 XAML 定义主要 UI 内容。Most apps that consume Visual Layer capabilities will use XAML to define the main UI content. 在 Windows 10 周年更新中,XAML 框架和可视化层中有新功能,可使结合这两种技术来创建精美的用户体验变得更简单。In the Windows 10 Anniversary Update, there are new features in the XAML framework and the Visual Layer that make it easier to combine these two technologies to create stunning user experiences. XAML 和可视化层互操作功能可用于创建单独使用 XAML API 时无法实现的高级动画和效果。XAML and Visual Layer interop functionality can be used to create advanced animations and effects not available using XAML APIs alone. 这包括:This includes:

  • 画笔效果(如模糊和毛玻璃)Brush effects like blur and frosted glass
  • 动态照明效果Dynamic lighting effects
  • 滚动驱动的动画和视差Scroll driven animations and parallax
  • 自动布局动画Automatic layout animations
  • 像素完美投影Pixel-perfect drop shadows

这些效果和动画可应用于现有 XAML 内容,因此你无需大幅重构 XAML 应用即可充分利用新功能。These effects and animations can be applied to existing XAML content, so you don't have to dramatically restructure your XAML app to take advantage of the new functionality. 布局动画、阴影和模糊效果将在下面的秘诀部分进行介绍。Layout animations, shadows, and blur effects are covered in the Recipes section below. 有关代码示例实现视差,请参阅 ParallaxingListItems 示例For a code sample implementing parallax, see the ParallaxingListItems sample. WindowsUIDevLabs 存储库还有用于实现动画、阴影和效果的其他示例。The WindowsUIDevLabs repository also has several other samples for implementing animations, shadows and effects.

XamlCompositionBrushBase 类The XamlCompositionBrushBase class

XamlCompositionBrush 为使用 CompositionBrush 绘制某个区域 XAML 画笔提供基类。XamlCompositionBrush provides a base class for XAML brushes that paint an area with a CompositionBrush. 这可以用于方便地向 XAML UI 元素应用合成效果(如模糊或毛玻璃)。This can be used to easily apply composition effects like blur or frosted glass to XAML UI elements.

有关将画笔与 XAML UI 结合使用的详细信息,请参阅画笔部分。See the Brushes section for more info on using brushes with XAML UI.

有关代码示例,请参阅 XamlCompositionBrushBase 的参考页面。For code examples, see the reference page for XamlCompositionBrushBase.

XamlLight 类The XamlLight class

XamlLight 为使用 CompositionLight 对某个区域动态照明的 XAML 照明效果提供基类。XamlLight provides a base class for XAML lighting effects that dynamically light an area with a CompositionLight.

有关使用光(包括对 XAML UI 元素进行照明)的详细信息,请参阅照明部分。See the Lighting section for more info on using lights, including lighting XAML UI elements.

有关代码示例,请参阅 XamlLight 的参考页面。For code examples, see the reference page for XamlLight.

ElementCompositionPreview 类The ElementCompositionPreview class

ElementCompositionPreview 是一个静态类,可提供 XAML 和可视化层互操作功能。ElementCompositionPreview is a static class that provides XAML and Visual Layer interop functionality. 有关可视化层及其功能的概述,请参阅可视化层For an overview of the Visual Layer and its functionality, see Visual Layer. ElementCompositionPreview 类提供以下方法:The ElementCompositionPreview class provides the following methods:

关于 ElementCompositionPreview.GetElementVisual 的备注Remarks on ElementCompositionPreview.GetElementVisual

ElementCompositionPreview.GetElementVisual 返回用于呈现给定 UIElement 的“handout”视觉对象。ElementCompositionPreview.GetElementVisual returns a “handout” Visual that is used to render the given UIElement. 诸如 Visual.OpacityVisual.OffsetVisual.Size 之类的属性由 XAML 框架基于 UIElement 的状态设置。Properties such as Visual.Opacity, Visual.Offset, and Visual.Size are set by the XAML framework based on the state of the UIElement. 这将支持隐式重新定位动画等技术(请参阅秘诀)。This enables techniques such as implicit reposition animations (see Recipes).

请注意,由于偏移大小根据 XAML 框架布局设置,因此开发人员应在修改或对这些属性进行动画处理时小心。Note that since Offset and Size are set as the result of XAML framework layout, developers should be careful when modifying or animating these properties. 开发人员应仅在元素的左上角在布局中与其父项的左上角位置相同时修改偏移或对其进行动画处理。Developers should only modify or animate Offset when the element’s top-left corner has the same position as that of its parent in layout. 通常不应该修改大小,但访问属性可能很有用。Size should generally not be modified, but accessing the property may be useful. 例如,下面的投影和毛玻璃示例使用 handout 视觉对象的大小作为对动画的输入。For example, the Drop Shadow and Frosted Glass samples below use Size of a handout Visual as input to an animation.

作为附加警告,handout 视觉对象的更新属性将不会反映在相应的 UIElement 中。As an additional caveat, updated properties of the handout Visual will not be reflected in the corresponding UIElement. 因此,例如,将 UIElement.Opacity 设置为 0.5 会将相应的 handout 视觉对象的不透明度设置为 0.5。So for example, setting UIElement.Opacity to 0.5 will set the corresponding handout Visual’s Opacity to 0.5. 但是,将 handout 视觉对象的 Visual 的不透明度设置为 0.5 会导致内容以 50% 的不透明度显示,但不会更改相应 UIElement 的 Opacity 属性的值。However, setting the handout Visual’s Opacity to 0.5 will cause the content to appear at 50% opacity, but will not change the value of the corresponding UIElement’s Opacity property.

Offset 动画的示例Example of Offset animation

错误Incorrect

<Border>
      <Image x:Name="MyImage" Margin="5" />
</Border>
// Doesn’t work because Image has a margin!
ElementCompositionPreview.GetElementVisual(MyImage).StartAnimation("Offset", parallaxAnimation);

正确Correct

<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 方法The ElementCompositionPreview.SetElementChildVisual method

ElementCompositionPreview.SetElementChildVisual 允许开发人员提供将显示为元素的可视化树一部分的“handin”视觉对象。ElementCompositionPreview.SetElementChildVisual allows the developer to supply a “handin” Visual that will appear as part of an element’s Visual Tree. 这允许开发人员创建一个“复合岛”,在其内,基于视觉对象的内容可以显示在 XAML UI 内。This allows developers to create a “Composition Island” where Visual-based content can appear inside a XAML UI. 开发人员应谨慎使用此技术,因为基于视觉对象的内容将不具有与 XAML 内容相同的辅助功能和用户体验保证。Developers should be conservative about using this technique because Visual-based content will not have the same accessibility and user experience guarantees of XAML content. 因此,通常建议,仅在有必要实现自定义效果(如下面的秘诀部分中所找到的效果)时使用此技术。Therefore, it is generally recommended that this technique only be used when necessary to implement custom effects such as those found in the Recipes section below.

GetAlphaMask 方法GetAlphaMask methods

ImageTextBlockShape 各自实现一个称为 GetAlphaMask 的方法,该方法返回一个 CompositionBrush,用于表示带有元素形状的灰度图像。Image, TextBlock, and Shape each implement a method called GetAlphaMask that returns a CompositionBrush representing a grayscale image with the shape of the element. CompositionBrush 可充当复合 DropShadow 的输入,因此阴影可以反映元素的形状,而不是矩形。This CompositionBrush can serve as an input for a Composition DropShadow, so the shadow can reflect the shape of the element instead of a rectangle. 这将为文本、带有 alpha 的图像和形状启用像素完美、基于轮廓的阴影。This enables pixel perfect, contour-based shadows for text, images with alpha, and shapes. 有关此 API 的示例,请参阅下面的投影See Drop Shadow below for an example of this API.

秘诀Recipes

重新定位动画Reposition animation

使用复合隐式动画,开发人员可以在元素的布局中对相对于其父项的更改自动进行动画处理。Using Composition Implicit Animations, a developer can automatically animate changes in an element’s layout relative to its parent. 例如,如果你更改下面的按钮的边距,它将自动动画处理到它的新布局位置。For example, if you change the Margin of the button below, it will automatically animate to its new layout position.

实现概述Implementation overview

  1. 获取目标元素的 handout 视觉对象Get the handout Visual for the target element
  2. 创建可对 Offset 属性中的更改自动进行动画处理的 ImplicitAnimationCollectionCreate an ImplicitAnimationCollection that automatically animates changes in the Offset property
  3. ImplicitAnimationCollection 与后备视觉对象关联Associate the ImplicitAnimationCollection with the backing Visual
<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;
}

投影Drop shadow

将像素完美投影应用到 UIElement,例如包含图片的椭圆Apply a pixel-perfect drop shadow to a UIElement, for example an Ellipse containing a picture. 由于阴影需要由应用创建的 SpriteVisual,因此我们需要使用 ElementCompositionPreview.SetElementChildVisual 创建一个“主机”元素,该元素将包含 SpriteVisualSince the shadow requires a SpriteVisual created by the app, we need to create a “host” element which will contain the SpriteVisual using ElementCompositionPreview.SetElementChildVisual.

实现概述Implementation overview

  1. 获取主机元素的 handout 视觉对象Get the handout Visual for the host element
  2. 创建一个 Windows.UI.Composition DropShadowCreate a Windows.UI.Composition DropShadow
  3. 配置 DropShadow 以通过蒙板从目标元素中获取其形状Configure the DropShadow to get its shape from the target element via a mask
    • DropShadow 默认为矩形,因此如果目标是矩形,则这不是必需的DropShadow is rectangular by default, so this is not necessary if the target is rectangular
  4. 将阴影附加到新的 SpriteVisual,并将该 SpriteVisual 设置为主机元素的子元素Attach shadow to a new SpriteVisual, and set the SpriteVisual as the child of the host element
  5. 使用 ExpressionAnimationSpriteVisual 的大小绑定到主机的大小Bind size of the SpriteVisual to the size of the host using an ExpressionAnimation
<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 等效内容。The following two listings show the C++/WinRT and C++/CX equivalents of the previous C# code using the same XAML structure.

#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);
}

毛玻璃Frosted glass

创建使背景内容模糊和带色彩的效果。Create an effect that blurs and tints background content. 请注意,开发人员需要安装 Win2D NuGet 程序包才能使用效果。Note that developers need to install the Win2D NuGet package to use effects. 有关安装说明,请参阅 Win2D 主页See the Win2D homepage for installation instructions.

实现概述Implementation overview

  1. 获取主机元素的 handout 视觉对象Get handout Visual for the host element
  2. 使用 Win2D 和 CompositionEffectSourceParameter 创建模糊效果树Create a blur effect tree using Win2D and CompositionEffectSourceParameter
  3. 基于效果树创建 CompositionEffectBrushCreate a CompositionEffectBrush based on the effect tree
  4. CompositionEffectBrush 的输入设置为 CompositionBackdropBrush,从而允许将效果应用到 SpriteVisual 背后的内容Set input of the CompositionEffectBrush to a CompositionBackdropBrush, which allows an effect to be applied to the content behind a SpriteVisual
  5. CompositionEffectBrush 设置为新 SpriteVisual 的内容,并将该 SpriteVisual 设置为主机元素的子元素。Set the CompositionEffectBrush as the content of a new SpriteVisual, and set the SpriteVisual as the child of the host element. 可以选择使用 XamlCompositionBrushBase。You could alternative use a XamlCompositionBrushBase.
  6. 使用 ExpressionAnimationSpriteVisual 的大小绑定到主机的大小Bind size of the SpriteVisual to the size of the host using an ExpressionAnimation
<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);
}

其他资源Additional Resources