none
XAML 语法术语 RRS feed

  • 常规讨论

  • 日期:2008-9-30 来源:MSDN 制作:yoling group

    本主题定义用来描述可扩展应用程序标记语言 (XAML) 语法的各个元素的术语。这些术语将在本软件开发工具包 (SDK) 的其余部分中经常用到。本主题扩展了 XAML 概述中所介绍的基本术语。
    XAML 语法术语的起源

    此处定义的 XAML 语法术语在 XAML 语言规范中也有定义或引用。XAML 是一种基于 XML 且遵循 XML 结构规则的语言。其中的术语共享自或基于描述 XML 语言或 XML 文档对象模型 (DOM) 时的常用术语。

     对象元素语法

    “对象元素语法”是一种 XAML 标记语法,它通过声明 XML 元素来实例化公共语言运行时 (CLR) 类或结构。此语法与其他标记语言(如 HTML)的元素语法相似。对象元素语法以左尖括号 (<) 开头,其后紧跟正进行实例化的类或结构的类型名称。类型名称后面可以有零个或多个空格,对于对象元素还可以声明零个或多个属性,并用一个或多个空格来分隔每个“属性名="值"”对。最后,必须存在下列一种情况:

    • 元素和标记必须用正斜杠 (/) 和紧跟的右尖括号 (>) 结尾。

    • 开始标记必须以右尖括号 (>) 结尾。其他对象元素、属性元素或内部文本可以跟在开始标记后面。此处可以包含的确切内容通常会受到元素对象模型的约束;请参见本主题中的“内容语法”一节。对象元素还必须存在等效的结束标记,并与其他开始标记/结束标记对形成正确的嵌套和平衡。

    例如,下面的示例是一个对象元素语法,该语法实例化 Button 类的一个新实例,而且还指定了一个 Name 属性及其值:

    C#
    <Button Name="CheckoutButton"/>

    下面的示例是一个还包括可扩展应用程序标记语言 (XAML) 内容属性语法的对象元素语法。其中包含的内部文本将用来设置 TextBox 可扩展应用程序标记语言 (XAML) 内容属性 Text

    C#
    <TextBox>This is a Text Box</TextBox>
     属性语法

    属性语法是一种 XAML 标记语法,该语法通过针对元素声明属性 (Attribute) 来设置属性 (Property) 值或者命名事件的事件处理程序。元素总是通过对象元素语法来声明。属性名必须与属性或事件的 CLR 成员名称相匹配。属性名后面是赋值运算符 (=)。属性值必须是一个用双引号 (") 引起来的字符串。

    为了能够通过属性语法进行设置,属性必须是公共的、可读写的,而且必须具有一个可以由 XAML 处理器实例化或引用的属性值类型。对于事件来说,事件必须是公共的而且必须具有一个公共委托。属性或事件必须是由包含对象元素实例化的类或结构的成员。

    属性值由下面的操作之一,按照如下处理顺序进行填充:

    1. 如果 XAML 处理器遇到一个大括号,或者遇到一个从 MarkupExtension 派生的对象元素,则将首先计算所引用的标记扩展(而不是将该扩展作为字符串来处理),而且将使用由标记扩展返回的对象。在许多情况下,由标记扩展返回的对象将是对现有对象的引用,或者是一个将计算推迟到运行时的表达式,而不是一个新对象。

    2. 如果该属性 (Property) 是用指定的 TypeConverter 声明的,或者该属性 (Property) 的值类型是用属性 (Attribute) 化 TypeConverter 声明的,则该属性 (Attribute) 的字符串值将作为转换输入提交到类型转换器,该转换器将返回一个新的对象实例。

    3. 如果没有 TypeConverter,则将尝试直接转换为属性类型。最后一个级别是直接在基元类型之间转换,或者在枚举中检查名称(这将返回匹配的值)。

    例如,在使用上面所显示的标记时,可以使用下面的属性 (Attribute) 语法示例为 Name 属性 (Property) 赋予字符串值:

    C#
    <Button Name="CheckoutButton"/>

    Name 属性是 Button 类的成员表的成员。Button 是用来定义 NameFrameworkElement 类的派生类。

    属性值的处理

    包含在左引号和右引号之间的字符串值是由 XAML 处理器处理的。对于属性来说,默认处理行为是由基础 CLR 属性的类型确定的。如果该属性 (Property) 是基元类型,则会基于字符串到相关基元类型的隐式转换来赋予属性 (Attribute) 值。如果该属性是一个枚举,则字符串会被视为由该枚举定义的名称,而且将从枚举中返回匹配的值。如果该属性 (Property) 既不是基元类型又不是枚举,则属性 (Attribute) 值必须由针对该属性 (Property) 本身或者目标类型声明的类型转换器来处理。类型转换器必须提供一个能够接受字符串的转换机制,该转换机制必须生成基础 CLR 属性类型的实例。还可以通过标记扩展来推迟转换步骤。

    枚举属性值

    XAML 中的枚举值由 Enum 结构的本机方法在内部处理。

    对于无标志的枚举值,本机行为是处理属性值的字符串并将它解析为某个枚举值。您不必像在代码中那样指定格式为枚举.值 的枚举,而是仅指定值,枚举 将从所设置属性的类型推断。如果您指定格式为枚举.值 的属性,它将无法正确解析。

    对于按标志枚举,该行为基于 Enum..::.Parse 方法。您可以通过用逗号分隔每个值来为按标志枚举指定多个值。但是,您不能合并不按标志的枚举值。例如,不能试图使用逗号语法来创建作用于无标志枚举多个条件的 Trigger

    <!--This will not compile, because Visibility is not a flagwise enumeration.--> ... <Trigger Property="Visibility" Value="Collapsed,Hidden"> <Setter ... /> </Trigger> ...

    在 WPF 中,能够支持 XAML 中可设置属性的按标志枚举极为罕见。但是,StyleSimulations 就是这样的一个枚举。例如,可以使用逗号分隔的按标志属性语法来修改在 Glyphs 类的“Remarks”(备注)部分中提供的示例;StyleSimulations = "BoldSimulation" 可能会变成 StyleSimulations = "BoldSimulation"。KeyBinding..::.Modifiers 是另一个属性,在该属性中可以指定多个枚举值。但是,此属性是一个特例,因为 ModifierKeys 枚举支持其自身的类型转换器。修饰符的类型转换器使用加号 (+) 而不是逗号 (,) 作为分隔符,因此在标记中支持用更传统的语法来表示组合键(如“Ctrl+Alt”)。

    属性引用和事件成员名称引用

    在指定属性 (Attribute) 时,可以引用您已经为包含的对象元素实例化的作为 CLR 类型成员存在的任何属性 (Property) 或事件。

    或者,可以独立于包含对象元素来引用附加属性或附加事件。

    对于可通过默认命名空间访问的任何对象中的任何事件,还可以通过使用“类型名称.事件”部分限定名来命名;此语法支持为路由事件附加处理程序,在路由事件中,处理程序旨在处理子元素中的事件路由,但是父元素在其成员表中并不拥有该事件。此语法与附加事件语法相似,但此处的事件不是真正的附加事件。相反,您引用的是具有限定名称的事件。有关更多信息,请参见路由事件概述

    属性 (Property) 名有时作为属性 (Attribute) 值提供,而不是作为属性 (Attribute) 名提供,属性 (Property) 名还可以包括限定符,例如以所有者类型.依赖项属性名称 格式指定的属性。在 XAML 中编写样式或模板时,此情况较为常见。以属性 (Attribute) 值形式提供的属性 (Property) 名具有不同的处理规则,这些规则由所设置的属性 (Property) 类型以及某些上下文因素(如样式或模板是否具有目标类型)来控制。有关详细信息,请参见样式设置和模板化

    当属性 (Attribute) 值描述属性 (Property) 之间的关系时,也可以使用属性 (Property) 名。此功能可用于数据绑定和演示图板目标,而且由 PropertyPath 类及其类型转换器启用。有关查找语义的更完整说明,请参见 PropertyPath XAML 语法

     属性元素语法

    属性元素语法是一种与基本 XML 语法稍有偏离的语法。在 XML 中,属性值是一个实际的字符串,唯一可能的变化是使用哪种字符串编码格式。在 XAML 中,可以指定其他对象元素作为属性值。此功能由属性元素语法来启用。不将属性 (Property) 指定为元素标记中的一个属性 (Attribute),而是使用元素的开始标记指定格式为元素类型名称.属性名 的属性 (Property),再指定属性 (Property) 值,然后结束属性元素。

    具体而言,该语法以左尖括号 (<) 开头,其后紧跟包含属性元素语法的类或结构的类型名称。类型名称后面紧跟一个点 (.),再后面是必须在指定类型的成员表中存在的属性名,最后面是一个右尖括号 (>)。要赋给属性的值包含在相应的属性元素中。通常,值作为一个或多个对象元素提供,因为将对象指定为值正是属性元素语法应当实现的方案。最后,必须提供一个等效的结束标记来指定同一个元素类型名称.属性名称 组合,并与其他元素标记对形成正确的嵌套和平衡。例如,下面的属性元素语法针对的是 ButtonContextMenu 属性。

    C#
    <Button> <Button.ContextMenu> <ContextMenu> <MenuItem Header="1">First item</MenuItem> <MenuItem Header="2">Second item</MenuItem> </ContextMenu> </Button.ContextMenu> Right-click me!</Button>

    当指定的属性类型是基元值类型(如 String)或者是指定了名称的枚举时,属性元素中的值也可以作为内部文本提供。这两个用法不太常见,因为这两种情况都还支持属性语法。用字符串填充属性元素的一个方案是,对于不是 XAML 内容属性但是仍用于表示 UI 文本的属性,UI 文本中必须出现特定的空白元素(如换行符)。属性 (Attribute) 语法不能保留换行符,但是属性 (Property) 元素语法可以保留换行符,不过前提是用来保留大量空白的功能处于活动状态(有关详细信息,请参见 XAML 中的空白处理)。

    属性元素不能以逻辑树的形式表示。属性元素不是由实例或对象支持的元素,而只是一个用来设置属性的特定语法。

    集合类型的属性元素语法

    XAML 规范要求所实现的 XAML 处理器能够标识值类型是集合的属性。WPF 实现基于托管代码,它的 XAML 处理器通过下列操作之一来标识集合类型:

    如果属性的类型是集合,则不必在标记中指定所推断的集合类型。相反,应当成为集合中项的元素将被指定为集合类型属性元素的一个或多个子元素。在加载每个这样的项并通过调用隐式集合的 Add 方法将其添加到集合的过程中,会将该项计算为一个对象。例如,StyleTriggers 属性采用专用集合类型 TriggerCollection。但是,在标记中不必实例化 TriggerCollection,而是需要在 Style.Triggers 属性元素中指定一个或多个 Trigger 项作为元素,其中 Trigger(或派生类)是一个类型,应当作为隐式强类型 TriggerCollection 的项类型。

    C#
    <Style x:Key="SpecialButton" TargetType="{x:Type Button}"> <Style.Triggers> <Trigger Property="Button.IsMouseOver" Value="true"> <Setter Property = "Background" Value="Red"/> </Trigger> <Trigger Property="Button.IsPressed" Value="true"> <Setter Property = "Foreground" Value="Green"/> </Trigger> </Style.Triggers> </Style>

    属性可以既是一个集合类型,又是该类型和派生类型的 XAML 内容属性。

    隐式集合元素会在逻辑树中创建一个成员,即使它在标记中不显示为元素也是如此。通常,所拥有类型的构造函数针对作为其属性之一的集合执行实例化,这会将该集合添加到树中。

    说明:

    由 WPF XAML 处理器执行的集合检测功能不支持泛型 List 和 Dictionary 接口(IList<(Of <(T>)>)IDictionary<(Of <(TKey, TValue>)>))。但是,可以将 List<(Of <(T>)>) 类用作基类(因为它直接实现 IList),或者将 Dictionary<(Of <(TKey, TValue>)>) 用作基类(因为它直接实现 IDictionary)。

     XAML 内容语法

    XAML 内容语法仅在将 ContentPropertyAttribute 指定为其类声明一部分的类上启用。ContentPropertyAttribute 需要一个按名称指定属性的参数,而该属性名称被定义为这种类型元素(包括派生的类)的内容属性。为此指定的属性是元素的 XAML 内容属性。在由 XAML 处理器处理时,在元素的开始标记和结束标记之间找到的任何子元素或内部文本将被指定为该 XAML 内容属性的值。如果愿意的话,您可以指定内容属性的属性元素,并明确显示其标记。这一技术对于清晰显示标记或作为一种标记样式来说具有特殊的价值,但内容属性的作用通常是简化标记,以便直观关联的父/子关系的元素可以直接嵌套。元素上其他属性的属性元素标记不能作为“内容”进行赋值;它们以前是在分析器工作流中进行处理的,不会被视为“内容”。

    正如对于任何其他属性一样,对象的 XAML 内容属性将属于特定类型。该类型可以是 Object 类型。该内容属性的类型可帮助定义对象的内容模型。例如,鉴于任何对象都可以变为内容,Object 的类型是松散的,但是即使这种松散类型也要求内容必须是单个对象。该单个对象可以是集合对象,但是即便如此,也只能将一个这样的集合对象指定为内容。

    在 WPF 文档中,特定类型的内容模型在该类型的类页面上进行描述,或者编写成类型系列的单独概念性主题并与每个相关的类型引用建立链接。

    集合类型的内容语法

    为了接受多个对象元素(或内部文本)作为内容,内容属性的类型必须是明确的集合类型。与集合类型的属性元素语法相似,XAML 处理器必须标识作为集合类型的类型。如果某一元素具有一个 XAML 内容属性,并且 XAML 内容属性的类型为集合,则不需要在标记中将隐含的集合类型指定为对象元素,也不需要将 XAML 内容属性指定为属性元素。因此,标记中明显的内容模型现在可以将多个子元素作为指定为内容。下面是 Panel 派生类的内容语法。所有的 Panel 派生类都建立要成为 Children 的 XAML 内容属性,这需要一个类型为 UIElementCollection 的值。

    C#
    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <StackPanel> <Button>Button 1</Button> <Button>Button 2</Button> <Button>Button 3</Button> </StackPanel> </Page>

    请注意,标记中既不需要 Children 的属性元素也不需要 UIElementCollection 的元素。这是 XAML 的设计特征,其目的在于,使用直接的父-子元素关系将那些用来定义 UI 的递归包含的元素更直观地表示为嵌套元素树,而不必对属性元素标记或集合对象进行外部干预。实际上,按照设计,UIElementCollection 在标记中不能指定为对象元素。由于 UIElementCollection 唯一的用途就是作为隐式集合,因此它不公开公共的默认构造函数,因此不能实例化为对象元素。

    在具有内容属性的对象中混合使用属性元素和对象元素

    XAML 规范声明 XAML 处理器可以进行如下强制:用来填充某个对象元素中 XAML 内容属性的对象元素必须是连续的,而且不得混合使用。对于混合使用属性元素和内容的这一限制是由 WPF XAML 处理器强制的。

    可以将子对象元素作为某个对象元素中的第一个直接标记,然后可以引入属性元素。也可以指定一个或多个属性元素,接着指定内容,然后指定多个属性元素。但是,一旦内容后面跟有属性元素,您就不能进一步引入任何内容,而只能添加属性元素。

    这个内容/属性元素顺序要求不适用于用作内容的内部文本。然而,这仍然是使内部文本保持连续的不错的标记样式,原因是,如果属性元素与内部文本交错分布,则很难直观地检测标记中的大量空白。

     附加属性

    附加属性是 XAML 中引入的一个编程概念,借此,属性可以由类型拥有和定义,但可以在任何元素上设置。附加属性所面向的主要方案就是,允许元素树中的子元素向父元素报告信息,而不要求使用在所有的元素之间广泛共享的对象模型。相反,附加属性可以由任何父元素用来向子元素报告信息。有关附加属性的用途以及如何创建您自己的附加属性的更多信息,请参见附加属性概述

    附加属性使用的语法在表面上与属性元素语法非常相似,因为您还需要指定类型名.属性名 组合。二者有两个重要的差异:

    • 即使在通过属性语法设置附加属性时,也可以使用类型名.属性名 组合。只有附加属性 (Property) 才要求属性 (Attribute) 语法中使用限定属性 (Property) 名。

    • 对于附加属性还可以使用属性元素语法。但是,对于典型的属性元素语法,您指定的类型名 是包含属性元素的对象元素。如果您引用的是附加属性,则类型名 是用来定义附加属性的类,而不是包含对象元素。

     附加事件

    附加事件是 XAML 中引入的另一个编程概念,事件可以由类型定义,但是处理程序可以附加到任何对象上。用来定义附加事件的类型通常是用来定义服务的静态类型,这些附加事件有时由用来公开服务的类型中的路由事件别名公开。附加事件的处理程序是通过属性语法指定的。正如对于附加事件一样,可以扩展附加事件的属性语法,以便允许使用“类型名称.事件名称”,其中“类型名称”是为附加事件基础结构提供 AddRemove 事件处理程序访问器的类,“事件名称”是事件名称。

     XML 命名空间

    上面的所有语法示例均未指定默认命名空间以外的命名空间。在典型的 WPF 应用程序中,默认命名空间被指定为 WPF 命名空间。您可以指定默认命名空间以外的命名空间,而且仍使用实质上同类的语法,但是,只要命名了无法在默认命名空间中访问的类,该类的名称就必须以用来映射对应的 CLR 命名空间的 XML 命名空间的前缀作为开头。例如,<custom:MyElement/> 是一种用来实例化 MyElement 类的实例的对象元素语法,其中包含该类的 CLR 命名空间(可能还有包含该命名空间的外部程序集)以前映射到 custom 前缀。

    有关 XML 命名空间和 XAML 的更多信息,请参见 XAML 命名空间和命名空间映射

     标记扩展

    XAML 定义了一个标记扩展编程实体,该实体允许从 XAML 处理器对属性或对象元素的常规处理中进行转义,将该处理转给支持类。WPF 对 XAML 处理器的实现将 MarkupExtension 抽象类用作由 WPF 支持的所有标记扩展的基础。在使用属性语法时,用来标识 XAML 处理器的标记扩展的字符是左大括号 ({),其后是右大括号 (}) 以外的任何字符。左大括号后面的第一个字符串必须引用用来提供特定扩展行为的类,如果子字符串“Extension”是实际类名的一部分,则该引用可以省略这个子字符串。该类后面可能会出现一个空格,该空格后面的每个字符都可以由所实现的扩展用作输入,直到遇到右大括号。在使用属性语法时,WPF 中标记扩展的主要用途是提供一种方法来引用其他已经存在的对象,或者将引用转给将在运行时计算的对象。例如,可以指定用 {Binding} 标记扩展来代替给定的属性通常将使用的值类型,从而完成简单的数据绑定。对于无法以其他方式使用属性 (Attribute) 语法的属性 (Property),许多标记扩展都允许使用属性 (Attribute) 语法。例如,Style 对象是一个相对复杂的引用类型,其中包含几个其他属性,每个属性都还采用 byref 对象(而非基元)。但是样式通常作为资源来创建,之后将通过请求资源的两个标记扩展之一来引用。该扩展将对属性 (Property) 值的计算推迟到资源查找时,允许在属性 (Attribute) 语法中提供 Style 属性 (Property) 的值并采用 Style 类型,如下所示:

    <Button Style="{StaticResource MyStyle}">My button</Button>

    在这里,StaticResource 用来标识 StaticResourceExtension 类,该类提供标记扩展实现。下一个字符串 MyStyle 用作非默认 StaticResourceExtension 构造函数的输入,在该构造函数中,从扩展字符串提取的参数用来声明所请求的 ResourceKey。MyStyle 应当是定义为资源的 Stylex:Key 属性值。StaticResource 标记扩展用法要求使用该资源,在加载时通过静态资源查找逻辑来提供 Style 属性值。

    有关标记扩展的更多信息,请参见标记扩展和 XAML。有关标记扩展和其他 XAML 编程功能的参考资料,请参见 XAML 命名空间 (x:) 语言功能WPF 命名空间 XAML 扩展

     可选的和不建议的 XAML 用法

    属性元素的可选用法

    属性元素的可选用法包括,具体地“拼出”由 XAML 处理器视为隐式的元素内容属性。例如,当您声明 Menu 的内容时,可以选择将 MenuItems 集合显式声明为 <Menu.Items> 属性元素标记,并将每个 MenuItem 放在 <Menu.Items> 中,而不是使用隐式的 XAML 处理器行为(即,Menu 的所有子元素都必须是 MenuItem 而且放在 Items 集合中)。有时,这个可选用法可以帮助以可视方式阐明标记中所表示的对象结构。或者,属性元素的隐式用法有时可以避免使用在技术上具有功能但是在视觉上容易引起混淆(如在属性值中嵌套标记扩展)的标记。

    typeName.memberName 全限定属性

    使用属性的类型名.成员名 格式实际上比仅仅使用路由事件的情况更为普遍,但是,在其他应用程序中,如果只是为了实现标记样式和可读性,则该格式是多余的,您应当避免使用它。在下面的示例中,对 Background 属性的三个引用是完全等效的:

    C#
    <Button Background="Blue">Background</Button> <Button Button.Background="Blue">Button.Background</Button> <Button Control.Background="Blue">Control.Background</Button>

    Button.Background 之所以适用,是因为在 Button 上对该属性的限定查找是成功的(Background 是从 Control 继承的),而且 Button 是对象元素的类或者是基类。Control.Background 之所以适用,是因为 Control 类实际定义了 Background,而且 Control 是一个 Button 基类。

    但是,下面的类型名.成员名 格式示例并不适用,因此显示为已注释掉:

    C#
    <!--<Button Label.Background="Blue">Does not work</Button> -->

    LabelControl 的另一个派生类,而且,如果在 Label 对象元素中指定了 Label.Background,则该用法将适用。但是,由于 Label 不是 Button 的类或基类,因此指定的 XAML 处理器行为是随后以附加属性形式处理 Label.Background。Label.Background 不是附加属性,因此该用法将失败。

    baseTypeName.memberName 属性元素

    与“类型名称.成员名称”格式如何适用于属性语法相似,“基类型名称.成员名称”语法适用于属性元素语法。例如,下面的语法适用:

    C#
    <Button>Control.Background PE <Control.Background> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Yellow" Offset="0.0" /> <GradientStop Color="LimeGreen" Offset="1.0" /> </LinearGradientBrush> </Control.Background> </Button>

    在这里,即使属性元素包含在 Button 中,属性元素也会以 Control.Background 形式提供。

    但是,正如属性的类型名.成员名 格式一样,基类型名称.成员名 在标记中是很差的样式,您应当避免将其用于设置样式。

    2009年6月19日 2:33