[.NET] XAML(2)--标签扩充
前言
在前一个章节[.NET] XAML(1)--对象生成,介绍了「XAML对象生成」这个简单却强大的对象生成模式。透过这个XAML对象生成配合面向对象的对象设计,可以让开发人员依照XAML内容生成近乎无限组合的对象。但是光只有前一个章节介绍的内容,在实际的开发应用上,很快就会遇到不足的地方。
假设现在要使用Property-Element的设定,将TextBlock 的Text设定为.NET里的Null,开发人员可能会写出下面范例的XAML。
<TextBlock x:Name="ShowTextBlock" FontSize="72"> <TextBlock.Text> Null </TextBlock.Text> </TextBlock>
这段XAML乍看之下很合理,但是照前一章的运作逻辑去做分析。.NET剖析XAML Element来产生对象的时候,会将"Null"转换为字符串,而不会是开发人员预期的.NET里面的Null。由这一个简单的范例来理解,就可以发现Property-Attribute、Property-Element…等等,的确是有语意不足的地方。
Markup Extensions
为了补足Property-Attribute、Property-Element这些设定语意不足的地方,XAML另外提供了「标记延伸」(Markup Extensions)这个设定,来满足开发人员的需求。Markup Extensions的运作逻辑,主要是围绕在MarkupExtension这个Class身上。
当程序使用.NET剖析XAML Object-Element建立对象的时候,会特别去检查建立出来的对象,是不是继承自MarkupExtension的子对象型别。当发现建立的对象是继承自MarkupExtension的子对象型别,.NET不会用这个对象当作Object-Element建立的结果,而是改呼叫这个对象继承与覆写的MarkupExtension的ProvideValue方法。并且使用ProvideValue方法的回传值,当作Object-Element建立的结果对象。
我们可以建立范例,来验证Markup Extensions的运作逻辑。首先建立一个继承自MarkupExtension的子对象类别ClarkExtension,并且覆写继承自MarkupExtension的ProvideValue方法。在这个范例里,ClarkExtension的ProvideValue方法只是单纯的回传一个"Clark"字符串。
using System; using System.Windows.Markup; using System.Windows.Media; namespace WpfApplication2 { public class ClarkExtension : MarkupExtension { public ClarkExtension() { } public override object ProvideValue(IServiceProvider serviceProvider) { return "Clark"; } } }
接着建立XAML范例,来使用ClarkExtension这个Class。下面这段XAML,采用Property-Element来将ClarkExtension,设定为TextBlock对象的Text属性。
<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sample="clr-namespace:WpfApplication2" Title="MainWindow" Height="400" Width="240"> <TextBlock x:Name="ShowTextBlock" FontSize="72"> <TextBlock.Text> <sample:ClarkExtension /> </TextBlock.Text> </TextBlock> </Window>
将这个范例编译执行,并且在程序代码里加入断点来检视对象执行的顺序。可以发现的确是执行了ClarkExtension的建构子之后,就呼叫ProvideValue、回传Clark字符串。并且在执行之后,将Clark字符串设定为TextBlock 的Text内容,这样最终程序呈现的执行结果就会如下图。透过这个范例,开发人员可以简单验证与理解XAML的 Markup Extensions运作逻辑。
相关数据可以参考:
http://msdn.microsoft.com/zh-tw/library/ee855815.aspx
http://msdn.microsoft.com/zh-tw/library/system.windows.markup.markupextension.aspx
简化语法
上一个章节XAML范例里的Markup Extensions设定,看起来跟一般常见的XAML上的Markup Extensions有所出入,一整个复杂了许多,这对于开发人员来说并不是很友善的设计。为了降低复杂度及提高可读性,XAML另外加入了简化的语法设定,让XAML的设计可以变得更简洁。
首先XAML定义了MarkupExtension对象,可以省略撰写Extension后置字符。以上一个章节的范例来说,可以将范例XAML里的TextBlock简化设定为:
<TextBlock x:Name="ShowTextBlock" FontSize="72"> <TextBlock.Text> <sample:Clark /> </TextBlock.Text> </TextBlock>
另外使用Property-Attribute设定的时候,XAML也定义使用大括号 ({...}),来识别MarkupExtension对象。以上一个章节的范例来说,可以将范例XAML里的TextBlock简化设定为:
<TextBlock x:Name="ShowTextBlock" FontSize="72" Text="{sample:ClarkExtension}"/>
当然啦,这两个简化的规则是可以互相组合的。以上一个章节的范例来说,可以将范例XAML里的TextBlock简化设定为如下的结果。这样的简化结果,就是一般常见的XAML设定。
<TextBlock x:Name="ShowTextBlock" FontSize="72" Text="{sample:Clark}"/>
相关数据可以参考:
http://msdn.microsoft.com/zh-tw/library/ee855815.aspx#naming_the_support_type
http://msdn.microsoft.com/zh-tw/library/ms788723.aspx#markup_extensions
XAML 定义标记延伸
在XAML内对于一些常见的的使用情景,预先实做了几个MarkupExtension子对象。这些预设的MarkupExtension子对象,通常放在System.Xaml组件的System.Windows.Markup命名空间内。在使用Visual Studio建立XAML相关页面的时候,要引用这些MarkupExtension子对象,预设是以「x: 前导符」来加以引用。几个比较常见的XAML MarkupExtension如下:
x:Type (TypeExtension)
TypeExtension会剖析设定的参数数据,回传对应的 Type对象。
x:Static(StaticExtension)
StaticExtension会剖析设定的参数数据,回传对应的对象的静态属性。
x:Null(NullExtension)
NullExtension没有需要设定的参数数据,就只是单纯的回传.NET的Null物件。
更多的数据可以参考:
http://msdn.microsoft.com/zh-tw/library/ms747254.aspx#XAML_Defined_Markup_Extensions
后记
XAML的Markup Extensions这个设定,是一些WPF、Silverlight、WP7功能(数据系结、资源参考)的运作核心。先理解Markup Extensions这个运作核心的职责及工作内容,再去学习数据系结、资源参考功能。这样从运作核心本身开始学习的路线,会比较正确而且快速、并且不会被过多繁杂的变化所迷惑。
补充
XAML在WPF、Silverlight、WP7都可以使用,但不同目标平台所开放、支持的函式库却不一定相同。以本章说明的StaticExtension来说,这个MarkupExtension类别在WPF上有支持,但在WP7上却是不支持的(撰写本文时,开发环境安装的是Windows Phone SDK 7.1)。开发人员在撰写XAML的时候,先确认目标平台支持的函式库相关内容,可以减少一些开发上不必要的困扰。
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。