CompositionLight 对象与 SceneLightingEffect 结合使用来模拟动态照明和反射性。
可以将灯光应用于 视觉对象 和 XAML UIElements。
将灯光应用于 XAML UIElements
XamlLight 对象用于应用 CompositionLights 以动态照亮 XAML UIElements。 XamlLight 提供了针对 UIElements 或 XAML 画笔的方法,将灯光应用于 UIElements 树,并基于它们当前是否正在使用,帮助管理 CompositionLight 资源的生存期。
- 如果使用 XamlLight 照射一个 画笔 ,则任何使用该画笔的 UIElement 部分都会被光线点亮。
- 如果将 UIElement 定位为 XamlLight 的目标,则整个 UIElement 及其子 UIElement元素都被这束光线照亮。
创建和使用 XamlLight
XamlLight 是可用于创建自定义灯的基类。
此示例展示了自定义 XamlLight 的定义,该 XamlLight 将多色聚光灯效果应用于 WinUI 中的目标 UI元素和画笔。
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 的结果如下所示。
重要
只有最低版本等于 Windows 10 创作者更新或更高版本的应用程序才支持在标记中设置 UIElement.Lights,如上例所示。 对于面向早期版本的应用程序,必须在后台代码中创建灯。
其他资源
- WindowsCompositionSamples GitHub 中的高级 UI 和合成示例。