随笔- 103  文章- 0  评论- 107 

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

模板绑定

下面逐一分析这些标记扩展:

  1. NullExtension

NullExtension提供设置属性为空值的方法。在部分情况下,不设置属性值与显式设置为null的区别很大,尤其是属性已经被设置为某值,这时候设置为空相当于清除之前的设置。

上面的例子中对Button的Background属性的设置就展示了NullExtension的使用,将Background设置为null就可以清除之前所设置的背景,这是一个很好的例子。

  1. TypeExtension

TypeExtension将返回一个System.Type对象给标记扩展所服务的Attribute。其接受一个定位参数,表示类型的名称。XAML将通过TypeExtension把这个字符串表示的类型名转化为相应的类型,同时这个类型名也不需要提供其完整命名空间(.NET),默认的命名空间就是该XAML的主命名空间与x:命名空间。

前文给TargetType属性设置值的标记扩展就是TypeExtension的一个例子。

  1. StaticExtension

StaticExtension在前文有所提及,其将对象的属性设置为特定的静态值。其接受一个参数,确定属性的来源,参数格式为ClassName.PropertyName。

前文示例中设置Button的Height的代码演示了StaticExtension的使用。

StaticExtension存在的问题在于当属性(Property)变化时不能自动修改属性(Attribute)的值。另外单独使用StaticExtension不能很好的结合系统设置与程序设置。在实际应用中往往将StaticExtension与StaticResource扩展结合使用。

  1. 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。

  1. DynamicResource

DynamicResource将属性(Property)值与资源值联系起来,其使用方式与StaticResource相似,但可以跟踪资源改变。

<TextBlock Name="myText" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"/>

等效的C#:

myText.SetResourceReference(TextBlock.BackgroundProperty, SystemColors.ActiveCaptionBrushKey);

由代码可以看出,在DynamicResource中,将StaticResource中资源赋值的方式改为设置引用,这样资源的值的改变可以被跟踪。这样当系统资源改变时,控件的背景也会随之改变。

  1. 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类型的资源数组,最后将资源设置给列表框。

  1. Binding

Binding标记扩展用来进行数据绑定。示例代码:

<TextBlock Text="{Binding Foo}" x:Name="txt"/>

这段代码在数据上下文将对象的Text属性绑定到Foo。

BindingOperations.SetBinding(txt, TextBlock.TextProperty, b);

  1. 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) - 标记扩展的更多相关文章

  1. WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形

    原文:WPF,Silverlight与XAML读书笔记第三十九 - 可视化效果之3D图形 说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘> ...

  2. WPF,Silverlight与XAML读书笔记第四十八 - Silverlight网络与通讯

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这一部分我们重点讨论下Silverlight ...

  3. WPF,Silverlight与XAML读书笔记第四十五 - 外观效果之模板

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 模板允许用任何东西完全替换一个元素的可视树, ...

  4. WPF,Silverlight与XAML读书笔记第四十四 - 外观效果之样式

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 如果你有Web编程的经验,你会知道使用Sty ...

  5. WPF,Silverlight与XAML读书笔记第四十七 - Silverlight与浏览器

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 这部分内容主要介绍Silverlight与浏 ...

  6. WPF,Silverlight与XAML读书笔记第四十六 - 外观效果之三皮肤与主题

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. 皮肤 皮肤是应用程序中样式与模板的集合,可以 ...

  7. WPF,Silverlight与XAML读书笔记第四十三 - 多媒体支持之文本与文档

    说明:本系列基本上是<WPF揭秘>的读书笔记.在结构安排与文章内容上参照<WPF揭秘>的编排,对内容进行了总结并加入一些个人理解. Glyphs对象(WPF,Silverlig ...

  8. XAML实例教程系列 - 标记扩展(Markup Extensions) 六

    XAML实例教程系列 - 标记扩展(Markup Extensions) 分类: Windows 8 Silverlight2012-06-21 13:00 1139人阅读 评论(0) 收藏 举报 扩 ...

  9. 《WPF程序设计指南》读书笔记——第1章 应用程序与窗口

    1.空白WPF项目的创建: 1)新建项目:在VS2010中,文件-新建-项目-visual c#-windows-空项目: 2)添加引用:PresentationFramework,Presentat ...

随机推荐

  1. VMware vCenter 6.5 安装及群集配置介绍

    一.介绍 VMware vCenter Server 提供了一个可伸缩.可扩展的平台,为虚拟化管理奠定了基础.可集中管理VMware vSphere环境,与其他管理平台相比,极大地提高了 IT 管理员 ...

  2. CAD控件:COM接口实现自定义实体

    1. 实现步骤: 3 1. 实现步骤: 参考例子 :Src\MxDraw5.2\samples\ie\iedemoTest.htm 1) 增加自定义实体对象 调用DrawCustomEntity函数, ...

  3. c++通过CMake实现debug开关

    刚学cmake,很多东西还不是很懂,不过今天刚刚实现了通过CMake控制debug的开关,兴奋之余记录一下. 背景介绍: 最近参与到了一个大的C++项目,很多代码已经非常成熟,我来添加一些辅助功能,但 ...

  4. Android开发技巧一--weight属性实现视图的居中(半)显示

    面试时,一位面试官问到:“如果我想讲按钮居中显示,并且占据其父视图宽度的一半,应该怎么做到呢?”即实现这种效果: 我们使用weightSum属性和layout_weight属性实现这一要求: < ...

  5. 前端安全 xss

    整体的 XSS 防范是非常复杂和繁琐的,不仅需要在全部需要转义的位置,对数据进行对应的转义.而且要防止多余和错误的转义,避免正常的用户输入出现乱码. 虽然很难通过技术手段完全避免 XSS,但可以总结以 ...

  6. WIndows 系统下的常用命令 和 检测方法

    ### 一.检测硬盘速度(Windows 自带工具) #### 使用windows 系统自带的工具测试硬盘读写速度 > 在使用下面命令前,需要获得管理员权限,才会在Dos窗口上显示(否则,一闪而 ...

  7. typora_test

    加粗标题 加下标线 <!--aba--> #Include ![](C:\Users\123\Pictures\Saved Pictures\1.jpg) ![](http://gyz.g ...

  8. UVA-127 "Accordian" Patience(模拟)

    题目: 把52张牌从左到右排好,每张牌自成一个牌堆.当某张牌与它左边那张牌或者左边第三张牌匹配时(花色或者点数相同)时,就把这张牌移到那张牌上面. 移动之后还要查看是否可以进行其他移动.只有位于牌堆顶 ...

  9. 安装ubuntu系统空间分配问题

    以下是我安装linux系统(ubuntu)时的系统空间配置,以50G为例: 挂载点 大小 格式 分区类型 / 15G Ext4 主分区 /home 30G Ext4 逻辑分区 /boot 1G Ext ...

  10. python爬虫24 | 搞事情了,用 Appium 爬取你的微信朋友圈。

    昨天小帅b看到一些事情不顺眼 有人偷换概念 忍不住就写了一篇反讽 996 的 看不下去了,我支持996,年轻人就该996! 没想到有些人看不懂 这就算了 还来骂我 早些时候关注我的小伙伴应该知道我第一 ...