CompositionLight 物件與 SceneLightingEffect 結合使用,以模擬動態光照與反射率。
你可以把燈光套用到 Visuals 和 XAML UIElements。
將燈光應用於 XAML UIElements
XamlLight 物件用於將 CompositionLights 動態地套用到 XAML UIElement,以點亮它們。 XamlLight 提供針對 UIElement 或 XAML 筆刷的方法,將燈光套用到 UIElement 的樹狀結構,並根據 CompositionLight 資源是否正在使用中來協助管理其生命週期。
- 如果你用 XamlLight 鎖定一個 刷子 ,那麼使用該刷子的任何 UIElement 部分都會被該光照亮。
- 如果你用 XamlLight 鎖定一個 UIElement ,那麼整個 UIElement 及其子 UIElement 都會被燈光照亮。
創建與使用 XamlLight
XamlLight 是一個基底類別,可以用來建立自訂燈具。
這個範例展示了自訂 XamlLight 的定義,該程式在 WinUI 中為目標的 UIElement 和筆刷套用多色聚光燈。
public sealed class OrangeSpotLight : XamlLight
{
// Register an attached property that lets you set a UIElement
// or Brush as a target for this light type in markup.
public static readonly DependencyProperty IsTargetProperty =
DependencyProperty.RegisterAttached(
"IsTarget",
typeof(bool),
typeof(OrangeSpotLight),
new PropertyMetadata(null, OnIsTargetChanged)
);
public static void SetIsTarget(DependencyObject target, bool value)
{
target.SetValue(IsTargetProperty, value);
}
public static Boolean GetIsTarget(DependencyObject target)
{
return (bool)target.GetValue(IsTargetProperty);
}
// Handle attached property changed to automatically target and untarget UIElements and Brushes.
private static void OnIsTargetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var isAdding = (bool)e.NewValue;
if (isAdding)
{
if (obj is UIElement)
{
XamlLight.AddTargetElement(GetIdStatic(), obj as UIElement);
}
else if (obj is Brush)
{
XamlLight.AddTargetBrush(GetIdStatic(), obj as Brush);
}
}
else
{
if (obj is UIElement)
{
XamlLight.RemoveTargetElement(GetIdStatic(), obj as UIElement);
}
else if (obj is Brush)
{
XamlLight.RemoveTargetBrush(GetIdStatic(), obj as Brush);
}
}
}
protected override void OnConnected(UIElement newElement)
{
if (CompositionLight == null)
{
// OnConnected is called when the first target UIElement is shown on the screen.
// This lets you delay creation of the composition object until it's actually needed.
var spotLight = CompositionTarget.GetCompositorForCurrentThread().CreateSpotLight();
spotLight.InnerConeColor = Colors.Orange;
spotLight.OuterConeColor = Colors.Yellow;
spotLight.InnerConeAngleInDegrees = 30;
spotLight.OuterConeAngleInDegrees = 45;
CompositionLight = spotLight;
}
}
protected override void OnDisconnected(UIElement oldElement)
{
// OnDisconnected is called when there are no more target UIElements on the screen.
// The CompositionLight should be disposed when no longer required.
if (CompositionLight != null)
{
CompositionLight.Dispose();
CompositionLight = null;
}
}
protected override string GetId()
{
return GetIdStatic();
}
private static string GetIdStatic()
{
// This specifies the unique name of the light.
// In most cases you should use the type's FullName.
return typeof(OrangeSpotLight).FullName;
}
}
// For the C++/WinRT code example below, you'll need to add a Midl File (.idl) file to your project.
// OrangeSpotLight.idl
namespace MyApp
{
[default_interface]
runtimeclass OrangeSpotLight : Microsoft.UI.Xaml.Media.XamlLight
{
OrangeSpotLight();
static Microsoft.UI.Xaml.DependencyProperty IsTargetProperty{ get; };
static Boolean GetIsTarget(Microsoft.UI.Xaml.DependencyObject target);
static void SetIsTarget(Microsoft.UI.Xaml.DependencyObject target, Boolean value);
}
}
// OrangeSpotLight.h
struct OrangeSpotLight : OrangeSpotLightT<OrangeSpotLight>
{
OrangeSpotLight() = default;
winrt::hstring GetId();
static Microsoft::UI::Xaml::DependencyProperty IsTargetProperty() { return m_isTargetProperty; }
static bool GetIsTarget(Microsoft::UI::Xaml::DependencyObject const& target)
{
return winrt::unbox_value<bool>(target.GetValue(m_isTargetProperty));
}
static void SetIsTarget(Microsoft::UI::Xaml::DependencyObject const& target, bool value)
{
target.SetValue(m_isTargetProperty, winrt::box_value(value));
}
void OnConnected(Microsoft::UI::Xaml::UIElement const& newElement);
void OnDisconnected(Microsoft::UI::Xaml::UIElement const& oldElement);
static void OnIsTargetChanged(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e);
inline static winrt::hstring GetIdStatic()
{
// This specifies the unique name of the light. In most cases you should use the type's full name.
return winrt::xaml_typename<MyApp::OrangeSpotLight>().Name;
}
private:
static Microsoft::UI::Xaml::DependencyProperty m_isTargetProperty;
};
// OrangeSpotLight.cpp
Microsoft::UI::Xaml::DependencyProperty OrangeSpotLight::m_isTargetProperty =
Microsoft::UI::Xaml::DependencyProperty::RegisterAttached(
L"IsTarget",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<MyApp::OrangeSpotLight>(),
Microsoft::UI::Xaml::PropertyMetadata{ winrt::box_value(false), Microsoft::UI::Xaml::PropertyChangedCallback{ &OrangeSpotLight::OnIsTargetChanged } }
);
void OrangeSpotLight::OnConnected(Microsoft::UI::Xaml::UIElement const& /* newElement */)
{
if (!CompositionLight())
{
// OnConnected is called when the first target UIElement is shown on the screen. This enables delaying composition object creation until it's actually necessary.
auto spotLight{ Microsoft::UI::Xaml::Media::CompositionTarget::GetCompositorForCurrentThread().CreateSpotLight() };
spotLight.InnerConeColor(Microsoft::UI::Colors::Orange());
spotLight.OuterConeColor(Microsoft::UI::Colors::Yellow());
spotLight.InnerConeAngleInDegrees(30);
spotLight.OuterConeAngleInDegrees(45);
CompositionLight(spotLight);
}
}
void OrangeSpotLight::OnDisconnected(Microsoft::UI::Xaml::UIElement const& /* oldElement */)
{
// OnDisconnected is called when there are no more target UIElements on the screen.
// Dispose of composition resources when no longer in use.
if (CompositionLight())
{
CompositionLight(nullptr);
}
}
winrt::hstring OrangeSpotLight::GetId()
{
return OrangeSpotLight::GetIdStatic();
}
void OrangeSpotLight::OnIsTargetChanged(Microsoft::UI::Xaml::DependencyObject const& d, Microsoft::UI::Xaml::DependencyPropertyChangedEventArgs const& e)
{
auto uie{ d.try_as<Microsoft::UI::Xaml::UIElement>() };
auto brush{ d.try_as<Microsoft::UI::Xaml::Media::Brush>() };
auto isAdding = winrt::unbox_value<bool>(e.NewValue());
if (isAdding)
{
if (uie)
{
Microsoft::UI::Xaml::Media::XamlLight::AddTargetElement(OrangeSpotLight::GetIdStatic(), uie);
}
else if (brush)
{
Microsoft::UI::Xaml::Media::XamlLight::AddTargetBrush(OrangeSpotLight::GetIdStatic(), brush);
}
}
else
{
if (uie)
{
Microsoft::UI::Xaml::Media::XamlLight::RemoveTargetElement(OrangeSpotLight::GetIdStatic(), uie);
}
else if (brush)
{
Microsoft::UI::Xaml::Media::XamlLight::RemoveTargetBrush(OrangeSpotLight::GetIdStatic(), brush);
}
}
}
// MainPage.h
...
#include "OrangeSpotLight.h"
...
struct MainPage : MainPageT<MainPage>
{
MainPage()
{
InitializeComponent();
OrangeSpotLight::SetIsTarget(spotlitBrush(), true);
OrangeSpotLight::SetIsTarget(spotlitUIElement(), true);
}
...
};
接著你可以把這些光源套用到任何 XAML UIElement 或 Brush 上,進行照明。 這個例子展示了不同的潛在用法。
這很重要
對於 C++/WinRT,請從下方標記中移除local:OrangeSpotLight.IsTarget="True"的兩個出現。 附加的屬性已經在程式碼後面設定好了。
<StackPanel Width="100">
<StackPanel.Lights>
<local:OrangeSpotLight/>
</StackPanel.Lights>
<!-- This border is lit by the OrangeSpotLight, but its content is not. -->
<Border BorderThickness="4" Margin="2">
<Border.BorderBrush>
<SolidColorBrush x:Name="spotlitBrush" Color="White" local:OrangeSpotLight.IsTarget="True"/>
</Border.BorderBrush>
<Rectangle Fill="LightGray" Height="20"/>
</Border>
<!-- This border and its content are lit by the OrangeSpotLight. -->
<Border x:Name="spotlitUIElement" BorderThickness="4" BorderBrush="PaleGreen" Margin="2"
local:OrangeSpotLight.IsTarget="True">
<Rectangle Fill="LightGray" Height="20"/>
</Border>
<!-- This border and its content are not lit by the OrangeSpotLight. -->
<Border BorderThickness="4" BorderBrush="PaleGreen" Margin="2">
<Rectangle Fill="LightGray" Height="20"/>
</Border>
</StackPanel>
這個 XAML 的結果如下。
這很重要
如上述範例所示,將 UIElement.Lights 設定為標記,僅支援最低版本等同於 Windows 10 創作者更新或更新版本的應用程式。 針對早期版本的應用程式,燈光必須在後端程式碼中建立。
其他資源
- WindowsCompositionSamples GitHub 上的進階使用者介面與構圖範例。