使用視覺層搭配 XAML
大部分使用視覺層功能的應用程式都會使用 XAML 來定義主要 UI 內容。 在 Windows 10 年度更新版中,XAML 架構和視覺層有新功能,可讓您更輕鬆地結合這兩種技術,來建立令人驚豔的使用者體驗。 XAML 和視覺層交互操作功能可用來建立進階動畫和效果,而且是單獨使用 XAML API 無法達到的動畫和效果。 這包括:
- 筆刷效果 (例如模糊和毛玻璃)
- 動態光源效果
- 捲動導向動畫和視差
- 自動版面配置動畫
- 像素完美的投影
這些效果和動畫可以套用至現有的 XAML 內容,因此您不需要大幅重組 XAML 應用程式來利用新功能。 版面配置動畫、陰影和模糊效果都會於下文的「配方」小節說明。 如需實作視差的程式碼範例,請參閱 ParallaxingListItems 範例。 WindowsCompositionSamples 存放庫 也有數個其他範例可供實作動畫、陰影和效果。
XamlCompositionBrushBase 類別
XamlCompositionBrush 提供 XAML 筆刷的基底類別,以 CompositionBrush 繪製區域。 這可以用來輕鬆地將模糊或毛玻璃等組合效果,套用至 XAML UI 元素。
如需搭配 XAML UI 使用筆刷的詳細資訊,請參閱筆刷小節。
如需程式碼範例,請參閱 XamlCompositionBrushBase \(英文\) 的參考頁面。
XamlLight 類別
XamlLight 提供 XAML 光源效果的基底類別,使用 CompositionLight 以動態方式照亮區域。
如需進一步了解如何使用光源,包括光源 XAML UI 元素,請參閱光源一節。
如需程式碼範例,請參閱 XamlLight 的參考頁面。
ElementCompositionPreview 類別
ElementCompositionPreview 是靜態類別,可提供 XAML 和 Visual Layer 交互操作的功能。 如需視覺層及其功能的概觀,請參閱視覺層。 ElementCompositionPreview 類別提供了下列方法:
- GetElementVisual:取得「講義」視覺效果,用來轉譯這個元素
- SetElementChildVisual:將「繳交作業」視覺效果設定為這個元素的視覺化樹狀結構當中最後一個子系。 此視覺效果的繪製將凌駕此元素的其餘效果。
- GetElementChildVisual:使用 SetElementChildVisual 擷取視覺效果集
- GetScrollViewerManipulationPropertySet:取得物件,系統可以根據 ScrollViewer 中的捲動位移,使用該物件來建立 60fps 動畫
ElementCompositionPreview.GetElementVisual 上的備註
ElementCompositionPreview.GetElementVisual 會傳回「講義」視覺效果,用來轉譯指定的 UIElement。 Visual.Opacity、Visual.Offset 和 Visual.Size 等屬性,是由 XAML 架構根據 UIElement 的狀態來設定。 這可啟用隱含重新定位動畫等技術 (請參閱配方)。
請注意,由於 Offset 和 Size 會設定為 XAML 架構版面配置的結果,因此開發人員在修改這些屬性或為它們建立動畫效果時,應該格外小心。 只有當元素的左上角與其父系在版面配置中的位置相同時,開發人員才應該修改 Offset 或為它賦予動畫效果。 通常不應該修改大小,但存取屬性可能很實用。 例如,下文的「投影」和「毛玻璃」範例會使用講義視覺效果的大小,作為動畫的輸入。
此外請特別注意,講義視覺效果的更新屬性將不會反映在對應的 UIElement 中。 例如,將 UIElement.Opacity 設定為 0.5,會將對應講義視覺效果的 Opacity 設定為 0.5。 不過,將講義視覺效果的 Opacity 設定為 0.5 會導致內容以 50% 不透明度顯示,但不會變更對應 UIElement 的 Opacity 屬性值。
Offset 動畫的範例
錯誤
<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 可讓開發人員提供「繳交作業」視覺效果,此效果將顯示為元素視覺化樹狀結構的一部分。 這可讓開發人員建立「組合島」,其中具有視覺效果的內容可以出現在 XAML UI 內。 開發人員應該謹慎使用這項技術,因為具有視覺效果的內容在協助工具和使用者體驗保證方面,與 XAML 內容並不相同。 因此,通常建議只有在必要時才使用這項技術來實作自訂效果,例如在以下「配方」一節中找到的效果。
GetAlphaMask 方法
Image、TextBlock 和 Shape 會實作稱為 GetAlphaMask 的方法,傳回 CompositionBrush 來代表具有元素圖形的灰階影像。 這個 CompositionBrush 可以做為 DropShadow 組合的輸入,因此陰影可以反映元素的圖形,而不是矩形。 這可讓文字、具有透明度的影像以及圖形,能產生像素完美且以輪廓為基礎的陰影。 如需此 API 的範例,請參閱下文的投影。
配方
重新定位動畫
使用組合隱含動畫,開發人員可以自動以動畫顯示元素版面配置中相對於其父系的變更。 例如,如果您變更下方按鈕的 Margin,它會自動以動畫顯示其新的版面配置位置。
實作概觀
- 取得 target 元素的講義視覺效果
- 建立 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,因此我們需要建立一個「host」元素,其中包含使用 ElementCompositionPreview.SetElementChildVisual 的 SpriteVisual。
實作概觀
- 取得 host 元素的講義視覺效果
- 建立 Windows.UI.Composition DropShadow
- 設定 DropShadow 以透過遮罩從 target 元素取得其圖形
- DropShadow 預設為矩形,因此如果 target 為矩形,則不需要這麼做
- 將陰影附加至新的 SpriteVisual,並將 SpriteVisual 設定為 host 元素的子系
- 使用 ExpressionAnimation 將 SpriteVisual 的大小繫結至 host 的大小
<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++/WinRT 和 C++/CX 等同於先前使用相同 XAML 結構之 C# 程式碼。
#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 首頁。
實作概觀
- 取得 host 元素的講義視覺效果
- 使用 Win2D 和 CompositionEffectSourceParameter 建立模糊效果樹狀結構
- 根據效果樹狀結構建立 CompositionEffectBrush
- 將 CompositionEffectBrush 的輸入設定為 CompositionBackdropBrush,讓效果套用至 SpriteVisual 背後的內容
- 將 CompositionEffectBrush 設定為新 SpriteVisual 的內容,並將 SpriteVisual 設定為 host 元素的子系。 您也可以使用 XamlCompositionBrushBase。
- 使用 ExpressionAnimation 將 SpriteVisual 的大小繫結至 host 的大小
<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);
}