使用視覺層搭配 XAMLUsing 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 會傳回「交出」Visual,它用來轉譯指定的 UIElementElementCompositionPreview.GetElementVisual returns a “handout” Visual that is used to render the given UIElement. 屬性 (例如 Visual.OpacityVisual.OffsetVisual.Size) 是根據 UIElement 狀態為基礎,透過 XAML 架構設定。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).

請注意,由於 OffsetSize 會設定做為 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. 當配置中元素的左上角與其父項的位置相同時,開發人員應僅對 Offset 進行修改或產生動畫效果。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 通常不應修改,但存取此屬性可能會很有用。Size should generally not be modified, but accessing the property may be useful. 例如,以下的「陰影」和「毛玻璃」範例就使用交出 Visual 的 Size 作為動畫的輸入。For example, the Drop Shadow and Frosted Glass samples below use Size of a handout Visual as input to an animation.

另請留意,更新的交出 Visual 屬性將不會反映在對應的 UIElement 中。As an additional caveat, updated properties of the handout Visual will not be reflected in the corresponding UIElement. 例如,將 UIElement.Opacity 設定為 0.5 會將所對應交出 Visual 的 Opacity 設定為 0.5。So for example, setting UIElement.Opacity to 0.5 will set the corresponding handout Visual’s Opacity to 0.5. 不過,將交出 Visual 的 Opacity 設定為 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 可讓開發人員提供將顯示為元素視覺化樹狀結構成員的「交入」Visual。ElementCompositionPreview.SetElementChildVisual allows the developer to supply a “handin” Visual that will appear as part of an element’s Visual Tree. 這可讓開發人員建立「組合島」,其中以 Visual 為主的內容可顯示於 XAML UI 內。This allows developers to create a “Composition Island” where Visual-based content can appear inside a XAML UI. 開發人員應該謹慎使用這項技術,因為以 Visual 為主的內容將不會有 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 的方法,它會傳回代表灰階影像與元素形狀的 CompositionBrushImage, 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. 這可讓您使用 Alpah 和形狀,針對文字和影像建立完美像素、以輪廓為主的陰影。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. 例如,如果您變更下方按鈕的 Margin,則它會自動對其新的配置位置產生動畫。For example, if you change the Margin of the button below, it will automatically animate to its new layout position.

實作概觀Implementation overview

  1. 取得目標元素的交出 VisualGet the handout Visual for the target element
  2. 建立 ImplicitAnimationCollection,它可以自動對 Offset 屬性中的變更產生動畫效果Create an ImplicitAnimationCollection that automatically animates changes in the Offset property
  3. ImplicitAnimationCollection 與支援的 Visual 產生關聯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,例如包含圖片的 EllipseApply a pixel-perfect drop shadow to a UIElement, for example an Ellipse containing a picture. 因為陰影需要由 app 建立的 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. 取得裝載元素的交出 VisualGet the handout Visual for the host element
  2. 建立 Windows.UI.Composition DropShadowCreate a Windows.UI.Composition DropShadow
  3. 透過遮罩,從目標元素取得其形狀來設定 DropShadowConfigure 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. 使用 ExpressionAnimation,繫結 SpriteVisual 的大小與裝載的大小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);
}

以下兩個清單顯示 C + + / WinRTC + + / CX 對等的上一個使用相同 XAML 結構的 C# 程式碼。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. 取得裝載元素的交出 VisualGet 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. 使用 ExpressionAnimation,繫結 SpriteVisual 的大小與裝載的大小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