WPF,Silverlight与XAML读书笔记(3) - 标记扩展
WPF,Silverlight与XAML读书笔记(3) - 标记扩展
|
说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。 |
标记扩展的作用同类型转换器(见本系列上一篇文章),都是将字符串转换为相应类型的对象。WPF/Silverlight内建的标记扩展都派生自MarkupExtension。其应用场景如下,当我们想设定一个属性的值为一些特定的静态属性值,但我们在编译时不知道这个值(如一个由用户的配置来决定的颜色),这时候就可以使用标记扩展。简单说标记扩展是一种用来设置属性值的类。
与类型转换器不同的是,标记扩展通过XAML显式的,一致的调用,因此这是更好的扩展XAML的方法。另外标记扩展也可以完成一些类型转换器所不能完成的功能。例如,通过自定义一个标记扩展可以实现使用一个简单的字符串将控件的背景色设置为渐变笔刷,而使用类型转换器是无法完成的。
标记扩展的语法及组成
XAML分析器将由"{ }"括起来的Attribute值认作一个标记扩展。
花括号中第一个标识符被识别符为标记扩展的名称,即定义标记扩展的类的名称,按照惯例这样的扩展常以Extension后缀结尾,但当在XAML中使用时可以省略该后缀。XAML分析器会自动添加并进行进一步处理。
第二个标识符是标记扩展接受的参数。如果标记扩展接受传入参数,可以为其指定参数值,并以逗号分隔各参数。标记扩展接受的参数分为两种:
l 定位参数,其被作为字符串参数传入扩展类的相应的构造函数。
l 命名参数,可以用来在已构造好的扩展对象上设置相应名字的属性。这些属性的值也可以是标记扩展(即标记扩展允许嵌套),也可以是文本值,通过类型转换器在运行时转换为相应的类型。
在XAML编译时,标记扩展的参数将被传入标记扩展类的重载的构造函数中来创建构造函数的一个新实例。在构造函数内部使用ProvideValue方法得到参数表示的实际值并提供给XAML的Attribute(即标记扩展的所服务的特性)。
我们通过下面的例子来看这个标记扩展参数处理过程可由:
|
<Style TargetType="{x:Type Button}"></Style> |
如上的标记扩展(其中的参数为定位参数),在XAML编译时会使用类似如下的C#代码来给TargetType赋值:
|
TypeExtension te = new TypeExtension(); object val = te.ProvideValue(s, Style.TargetTypeProperty); |
XAML对扩展标记类的验证有两种方式:编译时验证与运行时验证。XAML编译器挑选出部分扩展标记在编译时进行验证(这些标记扩展对于特定的参数,总是返回相同的值),另外大多数扩展(包括自定义扩展等)都在运行时进行测试。
上面例子中的TypeExtension是属于编译时验证的扩展标记类。验证代码形如:
|
Style s = new Style(); s.TargetType = typeof(Button); |
标记扩展的设计与.NET Framework的扩展机制 - 特性(Attribute)的设计是一致的。
标记扩展示例:
|
<Button Background="{x:Null}" Height="{x:Static SystemParameters.IconHeight}" Content="{Binding Path=Height,RelativeSource={RelativeSource Self}}" /> |
在上面的例子中,NullExtension与StaticExtension位于System.Windows.Markup命名空间,所以需要使用x前缀来定位。即x:Null与x:Static,而Binding(无Extension后缀)位于System.Windows.Data命名空间下,在XAML导入的主命名空间中,所以不用使用前缀。关于这些XAML命名空间内容可参见本系列第一篇文章中所介绍内容。
例子中SystemParameters.IconHeight与嵌套标记扩展中Self属于定位参数。而Path与RelativeSource属于命名参数。
StaticExtension允许使用静态属性,字段,常量及枚举值,不使用XAML中硬编码的值(如使用硬编码值则无需使用标记扩展)。
WPF中的标记扩展
WPF提供了一些内置的扩展标记,大部分被定义于XAML的XML命名空间,小部分位于XAML的WPF命名空间,前者需要通过x:访问,后者可以直接访问。下面的列表给出了这些内置标记扩展。
|
类 型 |
XAML |
用途 |
|
NullExtension |
x:Null |
用来表示null() |
|
TypeExtension |
x:Type |
得到Type对象 |
|
StaticExtension |
x:Static |
得到静态属性值 |
|
StaticResource |
StaticResource |
执行一次性的资源查找 |
|
DynamicResource |
DynamicResource |
设定动态资源绑定 |
|
ArrayExtension |
x:Array |
建立数组 |
|
Binding |
Binding |
建立数据绑定 |
|
TemplateBinding |
TemplateBinding |
模板绑定 |
下面逐一分析这些标记扩展:
- NullExtension
NullExtension提供设置属性为空值的方法。在部分情况下,不设置属性值与显式设置为null的区别很大,尤其是属性已经被设置为某值,这时候设置为空相当于清除之前的设置。
上面的例子中对Button的Background属性的设置就展示了NullExtension的使用,将Background设置为null就可以清除之前所设置的背景,这是一个很好的例子。
- TypeExtension
TypeExtension将返回一个System.Type对象给标记扩展所服务的Attribute。其接受一个定位参数,表示类型的名称。XAML将通过TypeExtension把这个字符串表示的类型名转化为相应的类型,同时这个类型名也不需要提供其完整命名空间(.NET),默认的命名空间就是该XAML的主命名空间与x:命名空间。
前文给TargetType属性设置值的标记扩展就是TypeExtension的一个例子。
- StaticExtension
StaticExtension在前文有所提及,其将对象的属性设置为特定的静态值。其接受一个参数,确定属性的来源,参数格式为ClassName.PropertyName。
前文示例中设置Button的Height的代码演示了StaticExtension的使用。
StaticExtension存在的问题在于当属性(Property)变化时不能自动修改属性(Attribute)的值。另外单独使用StaticExtension不能很好的结合系统设置与程序设置。在实际应用中往往将StaticExtension与StaticResource扩展结合使用。
- StaticResource
StaticResource返回一个指定资源的值。等效于调用元素的FindResource方法。StaticResource与下面将介绍的DynamicResource两个标记扩展位于WPF的命名空间,使用时无须x:前缀。下面是一段示例:
XAML:
|
<TextBlock Name="myText" Background="{StaticResource {x:Static SystemColors.ActiveCaptionBrushKey}}"/> |
等效C#:
|
myText.Background = (Brush)myText.FindResource(SystemColors.ActiveCaptionBrushKey); |
这段代码对资源进行一次性查找,属性(Property)值将会在初始化期间被设置为资源值。但资源值的变化不会引起属性值的变化,所以,如当改变系统颜色主题时,元素的背景不会随之更新。有效的解决方法就是使用下面介绍的DynamicResource。
- DynamicResource
DynamicResource将属性(Property)值与资源值联系起来,其使用方式与StaticResource相似,但可以跟踪资源改变。
|
<TextBlock Name="myText" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"/> |
等效的C#:
|
myText.SetResourceReference(TextBlock.BackgroundProperty, SystemColors.ActiveCaptionBrushKey); |
由代码可以看出,在DynamicResource中,将StaticResource中资源赋值的方式改为设置引用,这样资源的值的改变可以被跟踪。这样当系统资源改变时,控件的背景也会随之改变。
- ArrayExtension
ArrayExtension用来将元素值设置为一个元素的数组,其需要一个数组类型变量作为指定类型的属性值。由于这种类型的标记扩展所接受的参数往往很长,所以通常其内容不采用"{ }"的形式来表示,而是采用属性元素这种语法,其中每个数组的值都被表现为ArrayExtension元素的子项。(当然对于空数组可以使用"{ }"以使代码更简洁)。参见如下示例:
|
<Grid> <Grid.Resources> <x:ArrayExtension Type="{x:Type Brush}" x:Key="brushes"> <SolidColorBrush Color="Blue"/> <LinearGradientBrush StartPoint="0,0" EndPoint=" 0.8,1.5"> <LinearGradientBrush.GradientStops> <GradientStop Color="Green" Offset="0"/> <GradientStop Color="Cyan" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> <LinearGradientBrush StartPoint="0,0" EndPoint=" 0,1"> <LinearGradientBrush.GradientStops> <GradientStop Color="Black" Offset="0"/> <GradientStop Color="Red" Offset="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </x:ArrayExtension> </Grid.Resources> <ListBox ItemsSource=" {StaticResource brushes}" Name="myListBox"> <ListBox.ItemTemplate> <DataTemplate> <Rectangle Fill="{Binding}" Width="100" Height="40" Margin="2"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> |
等效C#:
|
Brush[] brushes = new Brush[3]; brushes[0] = Brushes.Blue; brushes[1] = new LinearGradientBrush(Colors.Green, Colors.Cyan, new Point(0, 0), new Point(0.8, 1.5)); brushes[2] = new LinearGradientBrush(Colors.Black, Colors.Red, new Point(0, 0),new Point(0, 1)); myGrid.Resources["brushes"] = brushes; myListBox.ItemsSource = myListBox.Resources["brushes"]; |
这段代码中ArrayExtension中建立一个数组作为资源,数组中的每一项又使用了TypeExtension,从而建立一个Brush类型的资源数组,最后将资源设置给列表框。
- Binding
Binding标记扩展用来进行数据绑定。示例代码:
|
<TextBlock Text="{Binding Foo}" x:Name="txt"/> |
这段代码在数据上下文将对象的Text属性绑定到Foo。
|
BindingOperations.SetBinding(txt, TextBlock.TextProperty, b); |
- TemplateBinding
模板绑定用在控件模板中,用于将源对象属性映射到模板中的对象属性。示例:
|
<Rectangle Width="100" Height="200" Fill="{TemplateBinding Background}"/> |
等效C#:
|
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Rectangle)); factory.SetValue(Rectangle.WidthProperty, 100); factory.SetValue(Rectangle.HeightProperty, 200); TemplateBindingExpression tb = new TemplateBindingExpression(Button.BackgroundProperty); factory.SetValue(Rectangle.FillProperty, tb); |
模板绑定用在模板的上下文。模板元素使用FrameworkElementFactory来建立其内容。这是因为模板可以被实例化很多次。
"{ }"的"转义"
如果你需要设置的一个属性值的字面值以"{"开头,则需要特殊的方法对其转义,以免其被当作标记扩展处理,转意方法是在"{"之前加上一对"{ }"。
代码示例:
|
<Button Content="{}{This is not a markup extension!}"/> |
或者使用属性元素实现同样的目的,等价代码:
|
<Button> {This is not a markup extension!} </Button> |
这段代码使用了隐式属性元素这个语法,这得益于内容属性这种语法。完整写法如下:
|
<Button> <Button.Content> {This is not a markup extension!} </Button.Content> </Button> |
因为标记扩展是有默认构造函数的类,其可以与属性元素一起使用,前文示例的标记扩展的代码等价于如下XAML:
|
<Button> <Button.Background> <x:Null/> </Button.Background> <Button.Height> <x:Static Member="SystemParameters.IconHeight"/> </Button.Height> <Button.Content> <Binding Path="Height"> <Binding.RelativeSource> <RelativeSource Mode="Self"/> </Binding.RelativeSource> </Binding> </Button.Content> </Button> |
代码中StaticExtension有一个Member属性与传入标记扩展x:Static的形参的实参含义相同,同理,RelativeSource有一个对应于的其构造函数参数的Mode属性。
自定义标记扩展
通过编写继承自MarkupExtention的类可以创建自己的标记扩展,要确保XAML编译器可以找到你的扩展类型,并将参数恰当的传入,最主要要做的就是提供恰当的重载构造函数来接收参数(适用于定位参数)或建立合适的属性(适用于命名参数)。下面的C#示例代码展示了怎样用标记扩展中的命名参数给属性赋值,通过这可以看出为什么自定义标记扩展时要定义参数。
|
<TextBlock TextContent="{Binding Path=SimpleProperty, Mode=OneTime}"/> |
C#初始化标记扩展的方法:
|
Binding b = new Binding(); b.Path = new PropertyPath("SimpleProperty"); b.Mode = BindingMode.OneTime; |
参考:
《WPF揭秘》
WPF,Silverlight与XAML读书笔记(3) - 标记扩展的更多相关文章
- WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形
原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...
- WPF,Silverlight与XAML读书笔记第四十八 - Silverlight网络与通讯
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这一部分我们重点讨论下Silverlight ...
- WPF,Silverlight与XAML读书笔记第四十五 - 外观效果之模板
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 模板允许用任何东西完全替换一个元素的可视树, ...
- WPF,Silverlight与XAML读书笔记第四十四 - 外观效果之样式
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 如果你有Web编程的经验,你会知道使用Sty ...
- WPF,Silverlight与XAML读书笔记第四十七 - Silverlight与浏览器
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这部分内容主要介绍Silverlight与浏 ...
- WPF,Silverlight与XAML读书笔记第四十六 - 外观效果之三皮肤与主题
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 皮肤 皮肤是应用程序中样式与模板的集合,可以 ...
- WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档
说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. Glyphs对象(WPF,Silverlig ...
- XAML实例教程系列 - 标记扩展(Markup Extensions) 六
XAML实例教程系列 - 标记扩展(Markup Extensions) 分类: Windows 8 Silverlight2012-06-21 13:00 1139人阅读 评论(0) 收藏 举报 扩 ...
- 《WPF程序设计指南》读书笔记——第1章 应用程序与窗口
1.空白WPF项目的创建: 1)新建项目:在VS2010中,文件-新建-项目-visual c#-windows-空项目: 2)添加引用:PresentationFramework,Presentat ...
随机推荐
- 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理
1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...
- CAD得到ImageMark数据(com接口VB语言)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 ...
- JavaScipt30(第七个案例)(主要知识点:数组some,every,findIndex方法)
承接上文,这是第7个案例,这个案例没什么说的,主要有三个注意点: 附上项目链接: https://github.com/wesbos/JavaScript30 // 1. slice(begin, e ...
- datagrid总条数
1.getData var data=$("#dg").datagrid("getData");alert('总数据量:' + data.total)//注意你 ...
- 【Redis】四、Redis设计原理及相关问题
(六)Redis设计原理及相关问题 通过前面关于Redis五种数据类型.相关高级特性以及一些简单示例的使用,对Redis的使用和主要的用途应该有所掌握,但是还有一些原理性的问题我们在本部分做一个探 ...
- Java基础——二分法
BinarySearch 二分法查找,顾名思义就是要将数据每次都分成两份然后再去找到你想要的数据,我们可以这样去想,二分法查找很类似与我们平时玩的猜价格游戏,当你报出一个价格时裁判会告诉你价格相对于真 ...
- 洛谷——P3919 【模板】可持久化数组(可持久化线段树/平衡树)
P3919 [模板]可持久化数组(可持久化线段树/平衡树) 题目背景 UPDATE : 最后一个点时间空间已经放大 标题即题意 有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集 ...
- 洛谷——P2846 [USACO08NOV]光开关Light Switching
P2846 [USACO08NOV]光开关Light Switching 题目大意: 灯是由高科技——外星人鼠标操控的.你只要左击两个灯所连的鼠标, 这两个灯,以及之间的灯都会由暗变亮,或由亮变暗.右 ...
- 微信小程序开发过程中tabbar页面显示的相关问题及解决办法!
在微信小程序的开发过程中如果有使用过tabbar的同学,我相信一定会遇到一些困扰.为什么有些时候代码中明明已经在app.json里面增加了tabbar,可以页面中就是不显示呢?可不可以有些页面显示ta ...
- odoo domain详解
参考的以下文档: luohuayong:Odoo domain写法及运用 baimo:odoo domain表达式 1.domain 表达式规则 最简单的格式:[('字段名','操作符',值)] 例: ...