【Win 10 应用开发】UI Composition 札记(八):用 XamlLight 制作灯光效果
前面老周已介绍过灯光的使用,如果你忘了,请用九牛二虎之力猛点击这里去复习一下。本篇老周再介绍另一种添加灯光的方法,这种方法是专为 XAML 元素而设计的,可以很方便地为可视化元素添加灯光效果。
不知道大伙伴是否发现,UIElement 类公开了一个 Lights 属性(15063,v1703,或更高版本),它是一个列表,可以添加若干个 XamlLight 对象。通过这个属性,我们也能为 XAML 可视化元素设置灯光。
虽然我们看到 XamlLight 类有构造函数,但是,它不是直接使用的,因为它一些重要成员都声明为 protected,这在外部类是无法访问的。所以,你必须自己派生出一个自定义的类型,然后为这些成员设置相关的内容。为什么这样做呢?你想想啊,如果只实例化这个类,运行时根本不可能知道你要用啥灯光,故你得自己去动手写。
先看看这些重要成员是啥。
protected virtual void OnConnected(UIElement newElement); protected virtual void OnDisconnected(UIElement oldElement); protected virtual string GetId(); // 这个属性用来设置要用的灯光对象
protected CompositionLight CompositionLight { get; set; }
先介绍一下长得最帅的那个—— CompositionLight,我们在代码中根据自己的需要,使用 Compositor 来创建灯光实例,还记得前面学过的内容吧,然后把灯光实例赋值给 CompositionLight 属性。
那么,在啥时候赋值呢?我们看到,有两个对应的方法—— OnConnected 和 OnDisconnected。当咱们把自定义的类型实例添加到 UIElement 元素的 Lights 列表中时,就会调用 OnConnected 方法,而跟随方法参数传递的就是这个可视化元素的实例。比如,你把这个自定义类实例添加到 Grid 的 Lights 列表中,那么传给 newElement 参数的就是这个 Grid。相反地,当我们自定义的XamlLight对象从 Lights 列表中被移除时,会调用 OnDisconnected 方法,这时候我们就应该把 CompositionLight 属性所占用的资源清理掉,如调用 Dispose 方法,然后把属性设置为 null。
估计你也看到了,XamlLight 类还有一个成员—— GetId 方法,你也必须实现这个成员,然后你要返回一个字符串,这个字符串必须能够唯一地代表你实现的这个类,最好的办法是返回这个类的类名,因为这个一般都能唯一的(我说的是包括命名空间名字的)。那这个字符串又在哪里用呢?你再看,XamlLight 类有两对静态方法:
AddTargetElement 与 RemoveTargetElement:用来指定哪些元素能被灯光照见,Add 进去的可视化对象就能被照亮的,而 Remove 后的对象是不会被灯光照亮的,你会看到,方法的第一个参数是一个字符串类型的 lightId,对的,这就是上面我们实现 GetId 方法的作用了。
AddTargetBrush 与 RemoveTargetBrush:使灯光照射到画刷上,而不是照到可视化对象上,用法也一样,Add了的画刷会被照亮,Remove后的画刷是不被照亮的。
在应用灯光对象时你要记住,把它添加到 UIElement 对象的 Lights 列表中,仅仅说明为这个灯光安排了照射空间。这个前面在介绍灯光时老周讲过的,比如 PointLight ,它是点状光,你必须安排一个 CoordinateSpace 对象,作为灯光的参数,这好比你把蜡烛放在一个小房子中,你不能把蜡烛放在野外,因为空矿的环境会严重削弱光线,所以,你得安排一个参照空间。此处,把自定义 XamlLight 放进 Lights 列表中,仅仅相当于你安排了这个 UI 元素作为参照空间而已,而这个空间内的子元素并不会真正应用灯光,所以,你必须调用静态的 AddTargetElement 方法,指明里面的哪些子元素会被照亮。
如果你把灯光对象添加到 Grid 的 Lights 列表中,表明灯光是以这个 Grid 为参照空间,然后你调用 AddTargetElement 方法,并把这个 Grid 元素传给方法,这说明整个 Grid 元素包括它的子元素都会被照亮的。如果你只希望 Grid 元素中某个子元素被照亮,就把子元素传给 AddTargetElement 方法。
好,说了那么多 F 话,咱们动手试试。
从 XamlLight 类派生,我们自定义一个灯源,叫 MyCustLight。
class MyCustLight : XamlLight
{
protected override string GetId()
{
return GetType().FullName;
} protected override void OnConnected(UIElement newElement)
{
// 创建灯光
var compositor = Window.Current.Compositor;
PointLight light = compositor.CreatePointLight();
// 设置灯光参数
light.Color = Colors.LightGreen;
light.Offset = new System.Numerics.Vector3(240f, 80f, 20f);
light.Intensity = 5.3f;
// 为属性赋值
CompositionLight = light;
// 这一句很重要
XamlLight.AddTargetElement(GetId(), newElement);
} protected override void OnDisconnected(UIElement oldElement)
{
// 这一句是对应的,Add了之后就要Remove
XamlLight.RemoveTargetElement(GetId(), oldElement);
// 释放资源
CompositionLight.Dispose();
CompositionLight = null;
}
}
GetId 方法我就不解释了,你能看懂的。说说其他成员,调用 OnConnected 方法时,咱们创建一个 PointLight 实例,需要用到的 Compositor 实例可以从 Window.Current.Compositor 属性中获取,同一个窗口下的UI元素都共用的。
设置好点状光的各个参数,然后记得赋值给 CompositionLight 属性,这个一定不要忘了,不然灯光是不起作用的。下面这一行也是重要的。
XamlLight.AddTargetElement(GetId(), newElement);
这意思就是,把 Lights 所属的 UI 元素加入到可被照亮区域,包括其子元素。
当光源从 Lights 列表中移除时,要调用 OnDisconnected 方法,在这个方法中,要把可照亮区域 Remove 掉,还要把 CompositionLight 属性所引用的资源清理掉。
// 这一句是对应的,Add了之后就要Remove
XamlLight.RemoveTargetElement(GetId(), oldElement);
// 释放资源
CompositionLight.Dispose();
CompositionLight = null;
好了,现在,自定义灯源已经做好,咱们用 XAML 对象试试。
<Grid Name="layout" Background="Black">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="Assets/1.jpg" Width="300" Height="300"/>
<StackPanel Grid.Column="1" Margin="13">
<RichTextBlock Foreground="Yellow">
<Paragraph>
<Run Text="姓名:" FontWeight="Bold"/>
<Run Text="八戒" />
</Paragraph>
<Paragraph>
<Run Text="性别:" FontWeight="Bold"/>
<Run Text="男"/>
</Paragraph>
<Paragraph>
<Run Text="民族:" FontWeight="Bold"/>
<Run Text="猪"/>
</Paragraph>
<Paragraph>
<Run Text="优点:" FontWeight="Bold"/>
<Run Text="勤劳、憨厚、和善"/>
</Paragraph>
</RichTextBlock>
</StackPanel>
</Grid>
</Grid>
为了方便看到灯光效果,我把 Grid 的背景弄成黑色。子元素中有文字,也有二师兄的头像,有关二师兄的照片,你可以网上找,二师兄那么出名,网上有他的照片。
首先,让大家看看,没有应用灯光效果时,二师兄的身份证。
二师兄还是那么帅。然后,转到代码视图,为 Grid 应用灯光。
public MainPage()
{
this.InitializeComponent();
MyCustLight light = new MyCustLight();
layout.Lights.Add(light);
}
如果你觉得用 XAML 添加更好,可以用 XAML 来添加。
<Grid Name="layout" Background="Black">
<Grid.Lights>
<local:MyCustLight/>
</Grid.Lights>
……
</Grid>
注意,两种方法取一即可,不要重复加。
现在,我们看看,二师兄被灯光照射后会变成什么样的。
二师兄依旧很帅。
这时候你又在想,我只想让灯光照射二师兄的头像,而不希望照射到右边的文本,那咱办呢?上面老周说过了,决定元素是否被照射,就是 AddTargetElement 方法在起作用。我们把 MyCustLight 类改一下就行了,弄一个附加属性,值为 bool 类型,如果某个子元素设为 true,就照亮它,如果为 false 就不照亮了。
来,咱们把 MyCustLight 类改一下。
因为这个类我在声明时没有加 public 修饰符,默认就成为 internal ,非 public 类无法在 XAML 中使用附加属性,所以,得先把它改为公共类。
public class MyCustLight : XamlLight
然后,GetId 方法的实现也要改,咱们知道,依赖项属性或附加属性都是静态成员,而我们在响应附加属性更改的回调方法中要用到 XamlLight 的 AddTargetElement 或 RemoveTargetElement 方法,这样是要提供这个 ID 字符串的,所以,我们可以定义一个静态属性,返回这个 ID,然后在 GetId 方法访问一下就可以了。
protected override string GetId()
{
return LightID;
}
// 通过静态属性返回
private static string LightID => typeof(MyCustLight).FullName;
下面是重点,我们为这个类注册一个名为 IsLightEnabled 的附加属性。
public static readonly DependencyProperty IsLightEnabledProperty = DependencyProperty.RegisterAttached("IsLightEnabled", typeof(bool), typeof(MyCustLight), new PropertyMetadata(false, OnIsLightEnabledPropertyChanged));
附加属性封装比较特殊,用 Get**** 和 Set**** 两个静态方法,也必须是 public 的。
public static void SetIsLightEnabled(DependencyObject obj, bool val)
{
obj.SetValue(IsLightEnabledProperty, val);
}
public static bool GetIsLightEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsLightEnabledProperty);
}
注意,Get*** 和 Set*** 中的 *** 一定要与你刚刚注册的附加属性的名字一致,不要写错了。
当这个附加属性被修改后,我们通过回调方法来判断属性值是否为 true,若为真,就应用灯光效果,若为假,就移除灯光效果。
private static void OnIsLightEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool b = (bool)e.NewValue;
UIElement targetUiele = d as UIElement;
if (b) //如要照亮
{
XamlLight.AddTargetElement(LightID, targetUiele);
}
else //如果不想照亮
{
XamlLight.RemoveTargetElement(LightID, targetUiele);
}
}
在 OnConnected 和 OnDisconnected 方法的实现中,把刚才的 AddTargetElement 和 RemoveTargetElement 方法的调用代码删除掉,因为咱们已经用附加属性处理了。
现在,MyCustLight 类应该变成这个样子。
public class MyCustLight : XamlLight
{
protected override string GetId()
{
return LightID;
}
// 通过静态属性返回
private static string LightID => typeof(MyCustLight).FullName; public static readonly DependencyProperty IsLightEnabledProperty = DependencyProperty.RegisterAttached("IsLightEnabled", typeof(bool), typeof(MyCustLight), new PropertyMetadata(false, OnIsLightEnabledPropertyChanged)); public static void SetIsLightEnabled(DependencyObject obj, bool val)
{
obj.SetValue(IsLightEnabledProperty, val);
}
public static bool GetIsLightEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsLightEnabledProperty);
} private static void OnIsLightEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool b = (bool)e.NewValue;
UIElement targetUiele = d as UIElement;
if (b) //如要照亮
{
XamlLight.AddTargetElement(LightID, targetUiele);
}
else //如果不想照亮
{
XamlLight.RemoveTargetElement(LightID, targetUiele);
}
} protected override void OnConnected(UIElement newElement)
{
// 创建灯光
var compositor = Window.Current.Compositor;
PointLight light = compositor.CreatePointLight();
// 设置灯光参数
light.Color = Colors.LightGreen;
light.Offset = new System.Numerics.Vector3(240f, 80f, 20f);
light.Intensity = 5.3f;
// 为属性赋值
CompositionLight = light;
} protected override void OnDisconnected(UIElement oldElement)
{
// 释放资源
CompositionLight.Dispose();
CompositionLight = null;
}
}
最后,在 XAML 代码中,让 Image 元素接收光源,而右边的文本不接收光源。
<Grid Margin="10">
……
<Image Source="Assets/1.jpg" Width="300" Height="300" local:MyCustLight.IsLightEnabled="True"/>
<StackPanel Grid.Column="1" Margin="13" local:MyCustLight.IsLightEnabled="False">
<RichTextBlock Foreground="Yellow">
……
</RichTextBlock>
</StackPanel>
</Grid>
再次运行,就可以看到,只有二师兄的猪头上有灯光,而右边的文本不会被灯光照亮。
OK,今天的内容就扯到这里,88。
【Win 10 应用开发】UI Composition 札记(八):用 XamlLight 制作灯光效果的更多相关文章
- 【Win 10 应用开发】UI Composition 札记(一):视图框架的实现
在开始今天的内容之前,老周先说一个问题,这个问题记得以前有人提过的. 设置 Windows.ApplicationModel.Core.CoreApplicationView.TitleBar.Ext ...
- 【Win 10 应用开发】UI Composition 札记(二):基本构件
在上一篇中,老周用一个示例,演示了框架视图的创建过程,在本篇中,老周将给大伙伴们说一下 Composition 构建 UI 的一些“零件”. UI Composition 有一个核心类——对,就是 C ...
- 【Win 10 应用开发】UI Composition 札记(三):与 XAML 集成
除了 DirectX 游戏开发,我们一般很少单独使用 UI Composition ,因此,与 XAML 互动并集成是必然结果.这样能够把两者的优势混合使用,让UI布局能够更灵活. 说到与 XAML ...
- 【Win 10 应用开发】UI Composition 札记(四):绘制图形
使用 Win 2D 组件,就可以很轻松地绘制各种图形,哪怕你没有 D2D 相关基础,也不必写很复杂的 C++ 代码. 先来说说如何获取 Win 2D 组件.很简单,创建 UWP 应用项目后,你打开“解 ...
- 【Win 10 应用开发】UI Composition 札记(五):灯光
UI Composition 除了能够为 UI 元素建立三维空间外,还有相当重要的一个部件——灯光.宇宙万物的精彩缤纷,皆源于光明,光,使我们看到各种东西,除了黑洞之外的世界都是五彩斑谰的.故而,真要 ...
- 【Win 10 应用开发】UI Composition 札记(六):动画
动画在 XAML 中也有,而且基本上与 WPF 中的用法一样.不过,在 UWP 中,动画还有一种表现方式—— 通过 UI Composition 来创建. 基于 UI Composition 的动画, ...
- 【Win 10 应用开发】在代码中加载文本资源
记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...
- 【Win 10 应用开发】启动远程设备上的应用
这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...
- 【Win 10应用开发】认识一下UAP项目
Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...
- 【Win 10 应用开发】导入.pfx证书
这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...
随机推荐
- 【转载】jQuery全屏滚动插件fullPage.js
文章转载自dowebok http://www.dowebok.com/ 原文链接:http://www.dowebok.com/77.html 简介 如今我们经常能见到全屏网站,尤其是国外网站.这些 ...
- 【笔记】【VSCode】Windows下VSCode编译调试c/c++
转载自http://m.2cto.com/kf/201606/516207.html 首先看效果 设置断点,变量监视,调用堆栈的查看: 条件断点的使用: 下面是配置过程: 总体流程: 下载安装vsco ...
- 使用 Newtonsoft.Json 操作 JSON 字符串
一.把实体类转化为 JSON 字符串 1. 为实体类赋值 SenderFromMQSearch senderFromMQSearch = new SenderFromMQSearch(); sende ...
- Python学习笔记(十)
Python学习笔记(十): 装饰器的应用 列表生成式 生成器 迭代器 模块:time,random 1. 装饰器的应用-登陆练习 login_status = False # 定义登陆状态 def ...
- 使用getCurrentPosition方法实时获取当前Geolocation信息(赋源码文件)--html5、JavaScript
使用getCurrentPosition方法实时获取当前Geolocation信息: 1.typeof 运算符返回一个用来表示表达式的数据类型的字符串 <script> alert(typ ...
- HTML5中表单中新增加元素
新增input 内属性 1,required:若文本内容为空,阻止表单提交格式,写入单词即可 如:<input type="text" name="username ...
- jQuery 常用操作(转)
一.书写规则 支持链式操作: 在变量前加"$"符号(var $variable = jQuery 对象): 注:此规定并不是强制要求. 二.寻找元素 选择器 基本选择器 层级选择器 ...
- ASP.NET没有魔法——ASP.NET MVC使用Area开发一个管理模块
之前的My Blog提供了列表的展示和文章显示功能,但是所有数据仍然只能通过数据库录入,为了完成最初的角色“作者”的用例,本章将介绍如何使用“Area”实现My Blog的管理功能. 根据功能分离代码 ...
- LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal (用先序和中序树遍历来建立二叉树)
Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume that ...
- idea 创建多模块依赖Maven项目
本来网上的教程还算多,但是本着自己有的才是自己的原则,还是自己写一份的好,虽然可能自己也不会真的用得着. 1. 创建一个新maven项目 2. 3. 输入groupid和artifactid,后面步骤 ...