通过


将视觉层与 WinUI XAML 配合使用

大多数使用视觉层功能的 WinUI 和 Windows 应用 SDK 应用都将使用 XAML 来定义主要 UI 内容。 WinUI 在 XAML 框架和视觉层中提供了一些功能,可以更轻松地组合这两种技术来创建令人惊叹的用户体验。 XAML 和视觉层互操作功能可用于创建不能单独使用 XAML API 的高级动画和效果。 这包括:

  • 画笔效果,如模糊和霜冻玻璃
  • 动态照明效果
  • 滚动驱动的动画和视差
  • 自动布局动画
  • 像素级精确阴影

这些效果和动画可以应用于现有的 XAML 内容,因此无需大幅调整 WinUI 应用以利用该功能。 下面的“食谱”部分介绍了布局动画、阴影和模糊效果。 有关实现视差的代码示例,请参阅 ParallaxingListItems 示例WindowsCompositionSamples 存储库还包含用于实现动画、阴影和效果的其他几个示例。

XamlCompositionBrushBase 类

Microsoft.UI.Xaml.Media.XamlCompositionBrushBase 为使用 CompositionBrush 绘制区域的 XAML 画笔提供了基类。 这可用于轻松地将合成效果(如模糊或霜冻玻璃)应用于 XAML UI 元素。

有关将画笔与 XAML UI 配合使用的详细信息,请参阅 “画笔 ”部分。

有关代码示例,请参阅 XamlCompositionBrushBase 的参考页。

XamlLight 类

Microsoft.UI.Xaml.Media.XamlLight 为 XAML 照明效果提供了一个基类,该基类可动态照亮 包含 CompositionLight 的区域。

有关使用灯光的详细信息,请参阅 “照明 ”部分,包括照明 XAML UI 元素。

有关代码示例,请参阅 XamlLight 的参考页。

使用 WinUI XAML

ElementCompositionPreview 是一个静态类,提供 XAML 和 Visual Layer 互操作功能。 有关视觉层及其功能的概述,请参阅 视觉层ElementCompositionPreview 类提供以下 WinUI 互操作方法:

GetElementVisual

ElementCompositionPreview.GetElementVisual 返回一个用于呈现给定 UIElement 的辅助视觉对象。 Visual.Opacity、Visual.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);

SetElementChildVisual

ElementCompositionPreview.SetElementChildVisual 允许开发人员提供“handin”视觉对象,该视觉对象将作为元素可视化树的一部分显示。 这样,开发人员就可以创建一个“合成岛”,其中基于视觉的内容可以显示在 XAML UI 中。 开发人员应该保守地使用此技术,因为基于视觉对象的内容不会具有相同的 XAML 内容的辅助功能和用户体验保证。 因此,通常建议仅在必要时使用此技术来实现自定义效果,如下面的“食谱”部分中找到的效果。

GetAlphaMask 方法

图像TextBlockShape 均实现一个名为 GetAlphaMask 的方法,该方法返回一个 CompositionBrush ,表示具有元素形状的灰度图像。 此 CompositionBrush 可用作合成 DropShadow 的输入,因此阴影可以反映元素的形状,而不是矩形。 这使文字内容、具有 alpha 通道的图像和形状能够实现像素级精确和基于轮廓的阴影效果。 有关此 API 的实例,请参阅下面的阴影投射

食谱

重新定位动画

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

实现概述

  1. 获取目标元素的资料Visual
  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. 获取主机元素的资料Visual
  2. 创建 Microsoft.UI.Composition DropShadow
  3. DropShadow 配置为通过掩码从目标元素中获取其形状
    • DropShadow 默认为矩形,因此,如果目标为矩形,则不需要这样做
  4. 将阴影附加到新的 SpriteVisual,并将 SpriteVisual 设置为主机元素的子元素
  5. 使用 ExpressionAnimationSpriteVisual 的大小绑定到主机的大小
<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

#include <winrt/Microsoft.UI.Composition.h>
#include <winrt/Microsoft.UI.Xaml.h>
#include <winrt/Microsoft.UI.Xaml.Hosting.h>
#include <winrt/Microsoft.UI.Xaml.Shapes.h>
...
MainPage()
{
    InitializeComponent();
    InitializeDropShadow(ShadowHost(), CircleImage());
}

int32_t MyProperty();
void MyProperty(int32_t value);

void InitializeDropShadow(Microsoft::UI::Xaml::UIElement const& shadowHost, Microsoft::UI::Xaml::Shapes::Shape const& shadowTarget)
{
    auto hostVisual{ Microsoft::UI::Xaml::Hosting::ElementCompositionPreview::GetElementVisual(shadowHost) };
    auto compositor{ hostVisual.Compositor() };

    // Create a drop shadow
    auto dropShadow{ compositor.CreateDropShadow() };
    dropShadow.Color(Microsoft::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
    Microsoft::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);
}

霜化玻璃

创建模糊和淡化背景内容的效果。 请注意,开发人员需要安装 Win2D NuGet 包才能使用效果。 有关安装说明,请参阅 Win2D 主页

实现概述

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

其他资源