本主题介绍 XAML 的标记扩展概念,包括其语法规则、用途以及它们所依据的类对象模型。 标记扩展是 XAML 语言和 XAML 服务的 .NET 实现的一般功能。 本主题专门详细介绍了在 WPF XAML 中使用的标记扩展。
XAML 处理器和标记扩展
一般来说,XAML 分析程序可以将属性值解释为可以转换为基元的文本字符串,也可以通过某种方式将其转换为对象。 一种这样的方式是引用类型转换器;本主题介绍了 TypeConverters 和 XAML。 但是,在某些情况下,需要不同的行为。 例如,可以指示 XAML 处理器属性的值不应导致对象图中的新对象。 相反,该特性应生成一个对象图,该图引用图形的另一部分或静态对象中已构造的对象。 另一种情况是,可以指示 XAML 处理器使用向对象的构造函数提供非默认参数的语法。 这些是标记扩展可以提供解决方案的方案类型。
基本标记扩展语法
可以实现标记扩展,以便为属性用法中的属性、属性元素用法中的属性或两者提供值。
当用于提供属性值时,能够让 XAML 处理器识别标记扩展序列的语法特征是起始和结束的花括号({ 和 })的出现。 然后,标记扩展的类型由紧跟在左大括号后面的字符串标识符确定。
在属性元素语法中使用时,标记扩展在视觉上与用于提供属性元素值的任何其他元素相同:将标记扩展类引用为元素的 XAML 元素声明(括在尖括号内<>)。
XAML-Defined 标记扩展
存在多个标记扩展,这些扩展并不特定于 XAML 在 WPF 中的实现,而是作为语言的 XAML 的内在功能或特性的实现。 这些标记扩展在 System.Xaml 程序集中作为常规 .NET Framework XAML 服务的一部分实现,并且位于 XAML 语言 XAML 命名空间中。 在常见标记用法方面,这些标记扩展通常可以通过用法中的 x:
前缀来识别。
MarkupExtension基类(也在 System.Xaml 中定义)提供了所有标记扩展都应使用的模式,以便在 XAML 读取器和 XAML 编写器(包括 WPF XAML 中)中受支持。
x:Type
提供 Type 命名类型的对象。 此设施最常用于样式和模板。 有关详细信息,请参阅 x:Type 标记扩展。x:Static
生成静态值。 这些值来自值类型代码实体,这些实体不是目标属性值的直接类型,但可以被评估为该类型。 有关详细信息,请参阅 x:Static Markup Extension。x:Null
指定null
为属性的值,可用于属性或属性元素值。 有关详细信息,请参阅 x:Null 标记扩展。x:Array
支持在 XAML 语法中创建常规数组,适用于有意不使用 WPF 基础元素和控件模型所提供的集合支持的情况。 有关详细信息,请参阅 x:Array 标记扩展。
注释
前缀 x:
用于 XAML 文件或生成环境根元素中 XAML 语言内在属性的典型 XAML 命名空间映射。 例如,WPF 应用程序的 Visual Studio 模板使用此 x:
映射启动 XAML 文件。 可以在自己的 XAML 命名空间映射中选择不同的前缀标记,但本文档将假定默认 x:
映射是标识 XAML 语言 XAML 命名空间中定义部分的实体,而不是 WPF 默认命名空间或其他与特定框架无关的 XAML 命名空间。
WPF-Specific 标记扩展
WPF 编程中使用的最常见标记扩展是支持资源引用(StaticResource
和 DynamicResource
)的标记扩展,以及支持数据绑定的标记扩展(Binding
)。
StaticResource
通过替换已定义资源的值来提供属性的值。StaticResource
评估最终是在 XAML 加载时进行的,在运行时无权访问对象图。 有关详细信息,请参阅 StaticResource 标记扩展。DynamicResource
通过将该值延迟为对资源的运行时引用来提供属性的值。 动态资源引用在每次访问此类资源时强制进行新的查找,并在运行时允许访问对象图。 为了获取此访问权限,DynamicResource
概念由 WPF 属性系统中的依赖属性和计算表达式支持。 因此,只能将DynamicResource
用于依赖属性目标。 有关详细信息,请参阅 DynamicResource 标记扩展。Binding
使用运行时应用于父对象的数据上下文为属性提供数据绑定值。 此标记扩展相对复杂,因为它可实现大量内联语法来指定数据绑定。 有关详细信息,请参阅 绑定标记扩展。RelativeSource
提供对可在运行时对象树中导航多种可能关系的 Binding 的源信息。 这提供了在多用途模板中创建的绑定关系的专业资源获取,或是在代码中创建,且无需对周围的对象树有完整了解。 有关详细信息,请参阅 RelativeSource MarkupExtension。TemplateBinding
使控件模板能够使用模板化属性的值,这些属性来自将使用模板的类的对象模型定义属性。 换句话说,模板定义中的属性可以访问仅应用模板后才存在的上下文。 有关详细信息,请参阅 TemplateBinding 标记扩展。 有关TemplateBinding
实际应用的更多信息,请参阅使用 ControlTemplates 样例进行样式设置。ColorConvertedBitmap
支持相对高级的成像场景。 有关详细信息,请参阅 ColorConvertedBitmap 标记扩展。ComponentResourceKey
和ThemeDictionary
支持资源查找的具体方面,特别是与自定义控件一起打包的资源和主题。 有关详细信息,请参阅 ComponentResourceKey 标记扩展、 ThemeDictionary 标记扩展或 控件创作概述。
*扩展类
对于普通 XAML 语言和 WPF 特定的标记扩展,通过一个派生自 MarkupExtension 的类来告知 XAML 处理器每个标记扩展的行为,并提供 ProvideValue 方法的实现。 每个扩展上的此方法都提供计算标记扩展时返回的对象。 返回的对象通常基于传递给标记扩展的各种字符串标记进行评估。
例如,该 StaticResourceExtension 类提供实际资源查找的图面实现,以便其 ProvideValue 实现返回所请求的对象,该特定实现的输入是用于按其 x:Key
查找资源的字符串。 如果使用的是现有标记扩展,此实现详细信息的大部分内容并不重要。
某些标记扩展不使用字符串标记参数。 这是因为它们返回静态值或一致值,或者因为应返回的值的上下文可通过通过参数传递 serviceProvider
的服务之一获得。
*Extension
命名模式是为了方便和一致性。 XAML 处理器不必将该类标识为对标记扩展的支持。 只要你的代码库包括 System.Xaml 并使用 .NET Framework XAML 服务的实现,成为一个 XAML 标记扩展的必要条件只是从MarkupExtension派生并支持构造语法。 WPF 定义了一些用于启用标记扩展的类,这些类不遵循*Extension
命名模式,例如Binding。 通常情况下,这是因为该类除了支持纯标记扩展外,还支持其他方案。 在这种情况下 Binding,对于与 XAML 无关的方案,该类支持对对象的方法和属性的运行时访问。
扩展类对初始化文本的解释
标记扩展名称后面的字符串标记,仍在大括号内,可由 XAML 处理器通过以下方式之一进行解释:
逗号始终表示标记的分隔符。
如果单独的分隔标记不包含任何等号,则每个令牌被视为构造函数参数。 每个构造函数参数都必须作为该签名预期的类型提供,并且按照该签名的预期顺序提供。
注释
XAML 处理器必须调用与参数对数匹配的构造函数。 因此,如果要实现自定义标记扩展,请不要提供具有相同参数计数的多个构造函数。 如果 XAML 处理器遇到具有相同参数个数的多个标记扩展构造函数路径时,其行为未定义,但在标记扩展类型定义中出现这种情况时,您应该预期 XAML 处理器在使用时可能会引发异常。
如果单独的分隔标记包含等号,则 XAML 处理器首先调用标记扩展的无参数构造函数。 然后,每个 name=value 对被解释为标记扩展上存在的属性名称,以及要分配给该属性的值。
如果构造函数行为与标记扩展中的属性设置行为之间存在并行结果,则使用哪种行为无关紧要。 对具有多个可设置属性的标记扩展使用属性值
=
对更为常见,前提是它使标记更有意,并且不太可能意外转置构造函数参数。 在指定 property=value 对时,那些属性可以按任意顺序排列。此外,不能保证标记扩展提供一个构造函数参数来设置其每个可设置的属性。 例如,Binding是一个标记扩展,具有许多属性,可通过属性值=
形式的扩展进行设置,但Binding仅支持两个构造函数:一个无参数构造函数,一个用于设置初始路径。在没有转义的情况下,无法将文本逗号传递给标记扩展。
转义序列和标记扩展
XAML 处理器中的属性处理使用大括号作为标记扩展序列的指示器。 如有必要,可以输入转义序列,通过空大括号对后接原义大括号,从而生成原义大括号字符属性值。 请参阅 {} 转义序列 - 标记扩展。
在 XAML 用法中嵌套标记扩展
支持嵌套多个标记扩展,并且每个标记扩展将从最深层开始进行评估。 例如,请考虑以下用法:
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
在此用法中,x:Static
语句首先被计算,并返回一个字符串。 然后,该字符串用作参数 DynamicResource
。
标记扩展和属性元素语法
当用作填充属性值的对象元素时,标记扩展类在视觉上与可在 XAML 中使用的典型类型支持的对象元素不一样。 典型对象元素和标记扩展之间的实际区别是,标记扩展要么计算为一个类型化值,要么推迟为一个表达式。 因此,标记扩展的属性值的任何可能类型错误的机制将有所不同,类似于在其他编程模型中处理后期绑定属性的方式。 一个普通对象元素将在解析 XAML 时根据它所设置的目标属性进行类型匹配评估。
大多数标记扩展在对象元素语法中使用以填充属性元素时,不会包含内容或任何进一步的属性元素语法。 因此,你将关闭对象元素标记,并且不提供子元素。 每当 XAML 处理器遇到任何对象元素时,将调用该类的构造函数,该构造函数实例化从分析的元素创建的对象。 标记扩展类没有区别:如果希望标记扩展在对象元素语法中可用,则必须提供无参数构造函数。 一些现有标记扩展至少有一个必需属性值,必须为有效初始化指定。 如果是这样,该属性值通常作为对象元素的属性属性提供。 在 XAML 命名空间 (x:) 语言特性 和 WPF XAML 扩展 的参考页中,将会标注具有必需属性的标记扩展及其属性名称。 如果特定标记扩展不允许使用对象元素语法或属性语法,则引用页也会注意。 一个值得注意的情况是 x:Array 标记扩展,它不支持属性语法,因为该数组的内容必须在标记中指定为内容。 数组内容作为常规对象进行处理,因此该属性的默认类型转换器不可行。 此外, x:Array 标记扩展 需要参数 type
。