[笔记/简译]XAML揭秘(3)

扩展标记( Markup Extensions

    扩展标记和类型转换器类似,它使我们可以扩展 XAML 的表现力。它既可以在运行时计算字符串属性(除了某些因性能原因而在编译时计算的内建扩展标记以外) 的值,又可以基于字符串值来产生合适的变量。就像类型转换器, WPF 提供了许多内建的扩展标记,它们都从 MarkupExtension 类派生。

    不像类型转换器,扩展标记在 XAML 中拥有显式且 一致的语法。正因为如此,扩展标记是扩展 XAML 的良好途径。此外,扩展标记还使我们 能够克服现有类型转换器的潜在限制,而这些限制我们又无力修改。比如,在我们想要使用简单的字符串值来将某个控件的背景设置为渐变画刷,则可以编写支持这 个功能的自定义扩展标记,即使内建的 BrushConverter 不支持这个想 法。

    XAML 编译器 / 解析器会把写入一对花括号中的属性当作扩 展标记来处理,而非字符串常量(或者一些需要类型转换器的东西)。下面的 Button 使用了三 个不同的扩展标记:

< Button xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "

          xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "

         Background = " {x:Null} "

         Height = " {x:Static SystemParameters.IconHeight} "

Content = " {Binding Path=Height, RelativeSource={RelativeSource Self}} " />

    每个花括号中的第一个标识符是扩展标记类 的名称。习惯上,这些 类以后缀 Extension 结尾,然而在 XAML 中我们也可以 不加。在上面的示例中, NullExtension (见 x:Null )和 StaticExtension (见 x:Static )都是 System.Windows.Markup 命名空间中的类,因此需要添加 x 前缀。 Binding (没有 x 前缀)是在 System.Windows.Data 命名空间中的,因此可以在默认的 XML 命名空间中找到它。

    如果扩展标记支持,则可以使用逗号分隔多个扩展标记。位置参数 (如 SystemParameters.IconHeight )被当作扩展类相应构造函数的字符串参数( StaticExtension 类包括一个重载的构造函数,以一个 System.String 为参数)。命名参数 (如 Path RelativeSource )使得我们可以 在构造的扩展对象上设置指定的属性。这些属性的值可是扩展标记本身的值(使用内嵌的花括号,如 RelativeSource )或者是能够通过一般类型转换处理的字符串常量。扩展标记与 .NET 自定义特性很相似,设置和使用扩展标记的意图正是如此。

    (对位置参数的处理方式,实际上就是(以伪 码表示)

StaticExtension ext = new StaticExtension ( SystemParameters .IconHeight );

而命名参数则为(以伪码表示)

       Binding binding = new Binding ();

        binding.Path = Height;

        binding.RelativeSource = new RelativeSource ( RaltiveSourceMode .Self);

    上例中, NullExtension 允许将 Background 属性设置为空画刷,而 BrushConverter 则不允许。 StaticExtension 允许是静态属性、字段、常量和枚举值,而非在 XAML 中硬编码的常量。本例将 Button Height 属性设置为操作系统当前的图标高度,该值由 System.Windows.SystemParameters 类的静态属性 IconHeight 提 供。 Binding 允许将 Button Content 属性设置为与其 Height 属性相同 的值。

    如下例, Button Content 属性会被当作字符串常量,而非扩展标记,因为它包含了转义花括号( {} ):

    < Button xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "

Content = " {}{ 非扩展标记 } " />

    同样的,也可以不用转义花括号而使用属性元素语法来设置字符串常量:

    < Button xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation " >

        < Button.Content > { 非扩展标 记 } </ Button.Content >

</ Button >

    由于扩展标记就是拥有默认构造方法的类,因此它们也可以应用属性元素语法,如下:

    < Button xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "

xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml " >

< 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.RealtiveSource >

</ Binding >

</ Button.Content >

</ Button >

    与扩展标记可以使用参数化的构造方法不同,属性元素语法中的对象元素均由默认的构造方法构造,因此 需要为其设置相应的属性。如 StaticExtension Member 属性,它与之前传递到其参数化构 造方法中的参数具有相同的含义。

    上例等价的程序代码如下:

    System.Windows.Controls. Button b = new System.Windows.Controls. Button ();

     b.Background = null ;

     b.Height = System.Windows.SysteParameters.IconHeight;

     System.Windows.Data. Binding binding = new System.Windows.Data. Binding ();

     binding.Path = new System.Windows. PropertyPath ( "Height" );

     binding.RelativeSource = System.Windows.Data. RelativeSource .Self;

b.SetBinding(System.Windows.Controls. Button .ContentProperty, binding);

    程序代码使用了和 XAML 不同的机制, 对于每种扩展都会有其特定的代码。在运行时,系统实际上是在被设置了合适值的扩展标记身上调用了 ProvideValue 方法,程序代码如果也使用这种机制就会变得复杂。 ProvideValue 定义在 MarkupExtension 类中,派生类 重写这个方法用于对外提供为扩展标记设置的目标属性值( System.Object 类型)。

    (通过 Reflector 查看 MarkupExtension 扩展标记基类,该类包含的 ProvideValue 是一个抽象方法。在 StaticExtension 扩展标记类当中,该方法被重写。虽然反射出 来的代码比较多,但是大致可以看出,该方法实际上对 StaticExtension 类的 Member 属性进行一些内部处理,然后返回处理结果。 在上例中, StaticExtension 类的 Member 属性被设置为 SystemParameters.IconHeight ,系统在处理这个扩展标记时,调用重写的 ProvideValue 方法,将返回值设置给 Button Height 属性。这是 XAML 的处理方式,但在 C# 代码中,大可不必如此。     个人感觉这个扩展标记实际上就是 XAML 中的一种数据绑定方式,简单的绑定如 StaticExtension ,高级的绑定就需要使用 Binding 类了。当然也包含作者说的,这个机制也是为了更好地使 XAML .NET 对象协作,并且增强了 XAML 的表现力。)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值