随笔- 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. Pytorch 加载保存模型【直播】2019 年县域农业大脑AI挑战赛---(三)保存结果

    在模型训练结束,结束后,通常是一个分割模型,输入 1024x1024 输出 4x1024x1024. 一种方法就是将整个图切块,然后每张预测,但是有个不好处就是可能在边界处断续. 由于这种切块再预测很 ...

  2. Java变量及数据类型

    变量及数据类型 变量 变量定义格式:数据类型 变量名 = 初始化值; 基本数据类型 整形数据 package com.ahabest.demo; //输出整形数据的最小值,默认值,最大值,二进制位数 ...

  3. orb slam2

  4. ubuntu14.04 fcitx安装

    先卸载ibus sudo apt-get remove ibus (也可尝试不卸载ibus,直接安装fcitx) 添加源 sudo add-apt-repository ppa:fcitx-team/ ...

  5. uva 1444 Knowledge for the masses

    uva 1444 Description   You are in a library equipped with bookracks that move on rails. There are ma ...

  6. eclipse如何能快速找到某个文件夹或者文件

    eclipse如何能快速找到某个文件夹或者文件 直接在editor中右键 Show in -> Package Explorer 在package explore 中选中link就可以了 (魔芋 ...

  7. springboot学习-jdbc操作数据库--yml注意事项--controller接受参数以及参数校验--异常统一管理以及aop的使用---整合mybatis---swagger2构建api文档---jpa访问数据库及page进行分页---整合redis---定时任务

    springboot学习-jdbc操作数据库--yml注意事项--controller接受参数以及参数校验-- 异常统一管理以及aop的使用---整合mybatis---swagger2构建api文档 ...

  8. C++ - new delete 高维数组小结

    借鉴:http://www.cnblogs.com/beyondstorm/archive/2008/08/26/1276278.html http://www.cnblogs.com/platero ...

  9. Javaweb中文乱码问题

    request.setCharacterEncoding("utf-8");必须写在获得参数之前,即request.getParameter();之前

  10. memory management in oracle 11G R2

    When we talking about memory management in Oracle, we are refering to SGA and PGA. The management me ...