标记扩展和 WPF XAMLMarkup Extensions and WPF XAML

本主题介绍 XAML 的标记扩展概念,包括其语法规则、用途以及作为其基础的类对象模型。This topic introduces the concept of markup extensions for XAML, including their syntax rules, purpose, and the class object model that underlies them. 标记扩展是 XAML 语言以及 XAML 服务的 .NET 实现的常规功能。Markup extensions are a general feature of the XAML language and of the .NET implementation of XAML services. 本主题专门详细讨论用于 WPF XAML 的标记扩展。This topic specifically details markup extensions for use in WPF XAML.

XAML 处理器和标记扩展XAML Processors and Markup Extensions

通常,XAML 分析器可将特性值解释为可转换成基元的文本字符串,或可通过某种方法将特性值转换为对象。Generally speaking, a XAML parser can either interpret an attribute value as a literal string that can be converted to a primitive, or convert it to an object by some means. 其中一种方法是引用类型转换器;详情请参阅主题 TypeConverters 和 XAMLOne such means is by referencing a type converter; this is documented in the topic TypeConverters and XAML. 不过,也存在要求其他行为的情况。However, there are scenarios where different behavior is required. 例如,可以指示 XAML 处理器,特性的值不应在对象图中生成新对象。For example, a XAML processor can be instructed that a value of an attribute should not result in a new object in the object graph. 相反,特性应生成引用对象图另一部分中的已构造对象或引用静态对象的对象图。Instead, the attribute should result in an object graph that makes a reference to an already constructed object in another part of the graph, or a static object. 另一种情况是,可以指示 XAML 处理器使用向对象的构造函数提供非默认参数的语法。Another scenario is that a XAML processor can be instructed to use a syntax that provides non-default arguments to the constructor of an object. 在这些类型的情况中,标记扩展可以提供解决方案。These are the types of scenarios where a markup extension can provide the solution.

基本标记扩展语法Basic Markup Extension Syntax

可以实现标记扩展来为特性用法中的属性和/或属性元素用法中的属性提供值。A markup extension can be implemented to provide values for properties in an attribute usage, properties in a property element usage, or both.

当用于提供特性值时,将标记扩展序列与 XAML 处理器区分开的语法是左右大括号({ 和 })。When used to provide an attribute value, the syntax that distinguishes a markup extension sequence to a XAML processor is the presence of the opening and closing curly braces ({ and }). 然后,由紧跟在左大括号后面的字符串标记来标识标记扩展的类型。The type of markup extension is then identified by the string token immediately following the opening curly brace.

当用在属性元素语法中时,标记扩展在外观上与任何其他用于提供属性元素值的元素相同,即:一个将标记扩展类作为元素引用并以尖括号 (<>) 括起的 XAML 元素声明。When used in property element syntax, a markup extension is visually the same as any other element used to provide a property element value: a XAML element declaration that references the markup extension class as an element, enclosed within angle brackets (<>).

XAML 定义的标记扩展XAML-Defined Markup Extensions

存在这么几种标记扩展,它们并非特定于 XAML 的 WPF 实现,而是语言形式的 XAML 的内部函数或功能的实现。Several markup extensions exist that are not specific to the WPF implementation of XAML, but are instead implementations of intrinsics or features of XAML as a language. 这些标记扩展在 System.Xaml 程序集中作为常规 .NET Framework XAML 服务的一部分而实现,并且位于 XAML 语言 XAML 命名空间内。These markup extensions are implemented in the System.Xaml assembly as part of the general .NET Framework XAML services, and are within the XAML language XAML namespace. 就常见标记用法而言,这些标记扩展通常可由用法中的 x: 前缀标识。In terms of common markup usage, these markup extensions are typically identifiable by the x: prefix in the usage. MarkupExtension 基类(也在 system.exception 中定义)提供了所有标记扩展应使用的模式,以便在 XAML 读取器和 XAML 编写器(包括在 WPF XAML 中)中受支持。The MarkupExtension base class (also defined in System.Xaml) provides the pattern that all markup extensions should use in order to be supported in XAML readers and XAML writers, including in WPF XAML.

  • x:Type 为命名类型提供 Type 对象。x:Type supplies the Type object for the named type. 此扩展最常用于样式和模板。This facility is used most frequently in styles and templates. 有关详细信息,请参阅 x:Type 标记扩展For details, see x:Type Markup Extension.

  • x:Static 生成静态值。x:Static produces static values. 这些值来自于值类型代码实体,它们不直接是目标属性值的类型,但可以计算为该类型。The values come from value-type code entities that are not directly the type of a target property's value, but can be evaluated to that type. 有关详细信息,请参阅 x:Static 标记扩展For details, see x:Static Markup Extension.

  • x:Nullnull 指定为属性的值,可用于特性或属性元素值。x:Null specifies null as a value for a property and can be used either for attributes or property element values. 有关详细信息,请参阅 x:Null 标记扩展For details, see x:Null Markup Extension.

  • 在特意不使用 WPF 基元素和控件模型提供的集合支持的情况下,x:Array 为 XAML 语法中常规数组的创建提供支持。x:Array provides support for creation of general arrays in XAML syntax, for cases where the collection support provided by WPF base elements and control models is deliberately not used. 有关详细信息,请参阅 x:Array 标记扩展For details, see x:Array Markup Extension.

备注

x: 前缀在 XAML 文件或生成的根元素中用于 XAML 语言内部函数的典型 XAML 命名空间映射。The x: prefix is used for the typical XAML namespace mapping of the XAML language intrinsics, in the root element of a XAML file or production. 例如,用于 WPF 应用程序的 Visual Studio 模板使用此 x: 映射启动 XAML 文件。For example, the Visual Studio templates for WPF applications initiate a XAML file using this x: mapping. 可以在自己的 XAML 命名空间映射中选择不同的前缀标记,但本文档将采用默认的 x: 映射,并通过它来标识属于 XAML 语言的 XAML 命名空间已定义部分的那些实体,这与 WPF 默认命名空间或与特定框架不相关的其他 XAML 命名空间相反。You could choose a different prefix token in your own XAML namespace mapping, but this documentation will assume the default x: mapping as a means of identifying those entities that are a defined part of the XAML namespace for the XAML language, as opposed to the WPF default namespace or other XAML namespaces not related to a specific framework.

特定于 WPF 的标记扩展WPF-Specific Markup Extensions

WPF 编程中最常用的标记扩展是支持资源引用的标记扩展(StaticResourceDynamicResource),和支持数据绑定的标记扩展 (Binding)。The most common markup extensions used in WPF programming are those that support resource references (StaticResource and DynamicResource), and those that support data binding (Binding).

  • StaticResource 通过替换已定义资源的值来为属性提供值。StaticResource provides a value for a property by substituting the value of an already defined resource. StaticResource 计算最终在 XAML 加载时进行,并且在运行时没有访问对象图的权限。A StaticResource evaluation is ultimately made at XAML load time and does not have access to the object graph at run time. 有关详细信息,请访问 StaticResource 标记扩展For details, see StaticResource Markup Extension.

  • DynamicResource 通过将值推迟为对资源的运行时引用来为属性提供值。DynamicResource provides a value for a property by deferring that value to be a run-time reference to a resource. 动态资源引用强制在每次访问此类资源时都进行新查找,且在运行时有权访问对象图。A dynamic resource reference forces a new lookup each time that such a resource is accessed and has access to the object graph at run time. 为了获取此访问权限,WPF 属性系统中的依赖项属性和计算出的表达式支持 DynamicResource 概念。In order to get this access, DynamicResource concept is supported by dependency properties in the WPF property system, and evaluated expressions. 因此,只能对依赖项属性目标使用 DynamicResourceTherefore you can only use DynamicResource for a dependency property target. 有关详细信息,请参阅 DynamicResource 标记扩展For details, see DynamicResource Markup Extension.

  • Binding 使用在运行时应用于父对象的数据上下文来为属性提供数据绑定值。Binding provides a data bound value for a property, using the data context that applies to the parent object at run time. 此标记扩展相对复杂,因为它会启用大量内联语法来指定数据绑定。This markup extension is relatively complex, because it enables a substantial inline syntax for specifying a data binding. 有关详细信息,请参阅 Binding 标记扩展For details, see Binding Markup Extension.

  • RelativeSource 提供了可在运行时对象树中导航几个可能的关系的 Binding 的源信息。RelativeSource provides source information for a Binding that can navigate several possible relationships in the run-time object tree. 对于在多用途模板中创建的绑定,或在未充分了解周围的对象树的情况下以代码创建的绑定,此标记扩展为其提供专用源。This provides specialized sourcing for bindings that are created in multi-use templates or created in code without full knowledge of the surrounding object tree. 有关详细信息,请参阅 RelativeSource 标记扩展For details, see RelativeSource MarkupExtension.

  • TemplateBinding 使控件模板能够使用模板化属性的值,这些属性来自于将使用该模板的类的对象模型定义属性。TemplateBinding enables a control template to use values for templated properties that come from object-model-defined properties of the class that will use the template. 换言之,模板定义中的属性可访问仅在应用了模板之后才存在的上下文。In other words, the property within the template definition can access a context that only exists once the template is applied. 有关详细信息,请参阅 TemplateBinding 标记扩展For details, see TemplateBinding Markup Extension. 有关 TemplateBinding 的实际使用的详细信息,请参阅使用 ControlTemplates 设置样式示例For more information on the practical use of TemplateBinding, see Styling with ControlTemplates Sample.

  • ColorConvertedBitmap 支持相对高级的映像方案。ColorConvertedBitmap supports a relatively advanced imaging scenario. 有关详细信息,请参阅 ColorConvertedBitmap 标记扩展For details, see ColorConvertedBitmap Markup Extension.

  • ComponentResourceKeyThemeDictionary 支持资源查找的各个方面,特别是支持查找与自定义控件一起打包的资源和主题。ComponentResourceKey and ThemeDictionary support aspects of resource lookup, particularly for resources and themes that are packaged with custom controls. 有关详细信息,请参阅 ComponentResourceKey 标记扩展ThemeDictionary 标记扩展控件创作概述For more information, see ComponentResourceKey Markup Extension, ThemeDictionary Markup Extension, or Control Authoring Overview.

*扩展类*Extension Classes

对于常规 XAML 语言和特定于 WPF 的标记扩展,每个标记扩展的行为通过从 MarkupExtension派生的 *Extension 类标识为 XAML 处理器,并提供 ProvideValue 方法的实现。For both the general XAML language and WPF-specific markup extensions, the behavior of each markup extension is identified to a XAML processor through a *Extension class that derives from MarkupExtension, and provides an implementation of the ProvideValue method. 每个扩展上的此方法都会提供在计算标记扩展时返回的对象。This method on each extension provides the object that is returned when the markup extension is evaluated. 通常会基于传递给标记扩展的各个字符串标记来计算返回的对象。The returned object is typically evaluated based on the various string tokens that are passed to the markup extension.

例如,StaticResourceExtension 类提供实际资源查找的表面实现,以便其 ProvideValue 实现返回所请求的对象,并将该特定实现的输入作为用于按其 x:Key查找资源的字符串。For example, the StaticResourceExtension class provides the surface implementation of actual resource lookup so that its ProvideValue implementation returns the object that is requested, with the input of that particular implementation being a string that is used to look up the resource by its x:Key. 如果使用的是现有标记扩展,则其大部分实现详细信息都无关紧要。Much of this implementation detail is unimportant if you are using an existing markup extension.

有些标记扩展不使用字符串标记参数。Some markup extensions do not use string token arguments. 这是因为它们返回静态值或一致的值,或者因为应返回何值的上下文可通过经 serviceProvider 参数传递的服务之一提供。This is either because they return a static or consistent value, or because context for what value should be returned is available through one of the services passed through the serviceProvider parameter.

*Extension 命名模式的目的是为了实现方便和一致。The *Extension naming pattern is for convenience and consistency. XAML 处理器不必将该类标识为支持标记扩展。It is not necessary in order for a XAML processor to identify that class as support for a markup extension. 只要您的基本代码包含 system.exception 并使用 .NET Framework XAML 服务实现,所有需要识别为 XAML 标记扩展的都是从 MarkupExtension 派生,并支持构造语法。So long as your codebase includes System.Xaml and uses .NET Framework XAML Services implementations, all that is necessary to be recognized as a XAML markup extension is to derive from MarkupExtension and to support a construction syntax. WPF 定义了标记扩展-启用不遵循 *Extension 命名模式的类,例如 BindingWPF defines markup extension-enabling classes that do not follow the *Extension naming pattern, for example Binding. 原因通常是该类支持纯标记扩展支持以外的方案。Typically the reason for this is that the class supports scenarios beyond pure markup extension support. Binding的情况下,该类支持对对象的方法和属性的运行时访问,适用于与 XAML 无关的方案。In the case of Binding, that class supports run-time access to methods and properties of the object for scenarios that have nothing to do with XAML.

初始化文本的扩展类解释Extension Class Interpretation of Initialization Text

跟在标记扩展名称后且仍在括号内的字符串标记由 XAML 处理器通过以下方式之一进行解释:The string tokens following the markup extension name and still within the braces are interpreted by a XAML processor in one of the following ways:

  • 逗号始终表示各个标记的分隔符。A comma always represents the separator or delimiter of individual tokens.

  • 如果各个分隔的标记不包含任何等号,则每个标记都将被视为构造函数参数。If the individual separated tokens do not contain any equals signs, each token is treated as a constructor argument. 必须按该签名所期望的类型和该签名所期望的适当顺序给出每个构造函数参数。Each constructor parameter must be given as the type expected by that signature, and in the proper order expected by that signature.

    备注

    XAML 处理器必须调用与对的数量这一参数计数匹配的构造函数。A XAML processor must call the constructor that matches the argument count of the number of pairs. 出于此原因,如果要实现自定义标记扩展,请不要提供具有相同参数计数的多个构造函数。For this reason, if you are implementing a custom markup extension, do not provide multiple constructors with the same argument count. 如果多个标记扩展构造函数路径具有相同的参数计数,则不会定义 XAML 处理器的行为方式,但应预计到如果这种情况存在于标记扩展类型定义中,则会允许 XAML 处理器引发有关使用情况的异常。The behavior for how a XAML processor behaves if more than one markup extension constructor path with the same parameter count exists is not defined, but you should anticipate that a XAML processor is permitted to throw an exception on usage if this situation exists in the markup extension type definitions.

  • 如果单个分隔标记包含等号,则 XAML 处理器首先调用标记扩展的无参数构造函数。If the individual separated tokens contain equals signs, then a XAML processor first calls the parameterless constructor for the markup extension. 之后,每个“名称=值”对会解释为标记扩展上存在的属性名称以及赋给该属性的值。Then, each name=value pair is interpreted as a property name that exists on the markup extension, and a value to assign to that property.

  • 如果标记扩展中的构造函数行为与属性设置行为之间存在并行结果,则使用哪个行为都无关紧要。If there is a parallel result between the constructor behavior and the property setting behavior in a markup extension, it does not matter which behavior you use. 较常见的用法是将“属性=值”对用于具有多个可设置属性的标记扩展,因为这可使标记意图性更强,并减少意外转置构造函数参数的可能性。It is more common usage to use the property=value pairs for markup extensions that have more than one settable property, if only because it makes your markup more intentional and you are less likely to accidentally transpose constructor parameters. (如果指定属性 = 值对,则这些属性可能为任意顺序。)此外,无法保证标记扩展提供了一个构造函数参数,该参数可设置其每个可设置属性。(When you specify property=value pairs, those properties may be in any order.) Also, there is no guarantee that a markup extension supplies a constructor parameter that sets every one of its settable properties. 例如,Binding 是一个标记扩展,可通过属性=窗体中的扩展设置多个属性,但 Binding 只支持两个构造函数:一个无参数的构造函数和一个设置初始路径的构造函数。For example, Binding is a markup extension, with many properties that are settable through the extension in property=value form, but Binding only supports two constructors: a parameterless constructor, and one that sets an initial path.

  • 文本逗号在未转义的情况下无法传递给标记扩展。A literal comma cannot be passed to a markup extension without escapement.

转义序列和标记扩展Escape Sequences and Markup Extensions

XAML 处理器中的特性处理使用大括号作为标记扩展序列的指示符。Attribute handling in a XAML processor uses the curly braces as indicators of a markup extension sequence. 必要时,也可以使用后跟文本大括号的空大括号对输入转义序列,来生成文本大括号字符特性值。It is also possible to produce a literal curly brace character attribute value if necessary, by entering an escape sequence using an empty curly brace pair followed by the literal curly brace. 请参阅{} 转义序列标记扩展See {} Escape Sequence - Markup Extension.

XAML 中的嵌套标记扩展用法Nesting Markup Extensions in XAML Usage

支持多个标记扩展的嵌套,并且将首先计算每个标记扩展的最里层。Nesting of multiple markup extensions is supported, and each markup extension will be evaluated deepest first. 例如,考虑下面的用法:For example, consider the following usage:

<Setter Property="Background"  
  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />  

在此用法中,将首先计算 x:Static 语句并返回字符串。In this usage, the x:Static statement is evaluated first and returns a string. 该字符串随后用作 DynamicResource 的参数。That string is then used as the argument for DynamicResource.

标记扩展和属性元素语法Markup Extensions and Property Element Syntax

当用作填写属性元素值的对象元素时,标记扩展类在外观上与可用在 XAML 中的基于典型类型的对象元素没有区别。When used as an object element that fills a property element value, a markup extension class is visually indistinguishable from a typical type-backed object element that can be used in XAML. 典型对象元素与标记扩展之间的实际差异是,标记扩展要么计算为类型化值,要么延迟为表达式。The practical difference between a typical object element and a markup extension is that the markup extension is either evaluated to a typed value or deferred as an expression. 因此,标记扩展的属性值的任何可能类型错误的机制都将是不同的,这与在其他编程模型中处理后期绑定属性的方式类似。Therefore the mechanisms for any possible type errors of property values for the markup extension will be different, similar to how a late-bound property is treated in other programming models. 普通对象元素将针对分析 XAML 时它设置的目标属性进行类型匹配计算。An ordinary object element will be evaluated for type match against the target property it is setting when the XAML is parsed.

当用在对象元素语法中以填充属性元素时,大多数标记扩展都不会包含任何内容或深层属性元素语法。Most markup extensions, when used in object element syntax to fill a property element, would not have content or any further property element syntax within. 这样你便可以关闭对象元素标记,而不提供任何子元素。Thus you would close the object element tag, and provide no child elements. 不论何时 XAML 处理器遇到任何对象元素,都会调用该类的构造函数来实例化从已分析元素创建的对象。Whenever any object element is encountered by a XAML processor, the constructor for that class is called, which instantiates the object created from the parsed element. 标记扩展类没有区别:如果希望标记扩展可用于对象元素语法,则必须提供无参数的构造函数。A markup extension class is no different: if you want your markup extension to be usable in object element syntax, you must provide a parameterless constructor. 有些现有标记扩展具有至少一个必需的属性值,必须指定该属性值才能使实例化生效。Some existing markup extensions have at least one required property value that must be specified for effective initialization. 如果是这样,该属性值通常会作为对象元素上的属性特性而给出。If so, that property value is typically given as a property attribute on the object element. XAML 命名空间 (x:) 语言功能WPF XAML 扩展参考页中,会指出具有必需属性的标记扩展(以及必需属性的名称)。In the XAML Namespace (x:) Language Features and WPF XAML Extensions reference pages, markup extensions that have required properties (and the names of required properties) will be noted. 参考页还将指出特定标记扩展是否禁止使用对象元素语法或特性语法。Reference pages will also note if either object element syntax or attribute syntax is disallowed for particular markup extensions. 需要注意 x:Array 标记扩展,它无法支持特性语法,因为该数组的内容必须在标记内作为内容指定。A notable case is x:Array Markup Extension, which cannot support attribute syntax because the contents of that array must be specified within the tagging as content. 数组内容的处理方式与常规对象一样,因此特性可以没有默认的类型转换器。The array contents are handled as general objects, therefore no default type converter for the attribute is feasible. 此外,x:Array 标记扩展需要 type 参数。Also, x:Array Markup Extension requires a type parameter.

另请参阅See also