<StackPanel>
<Button>This example</Button>
<StackPanel.Resources>
<SolidColorBrush x:Key="BlueBrush" Color="Blue"/>
</StackPanel.Resources>
<Button>... is illegal XAML</Button>
</StackPanel>
内容属性和集合语法组合
若要接受多个对象元素作为内容,内容属性的类型必须明确为集合类型。 与集合类型的属性元素语法类似,XAML 处理器必须标识属于集合类型的类型。 如果元素具有 XAML 内容属性,并且 XAML 内容属性的类型是集合,则隐式集合类型不需要在标记中指定为对象元素,并且 XAML 内容属性不需要指定为属性元素。 因此,标记中的明显内容模型现在可以将多个子元素分配为内容。 下面是 Panel 派生类的内容语法。 所有 Panel 派生类都会建立 XAML 内容属性以作为 Children,这需要 UIElementCollection 类型的值。
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 命名空间
前面的语法示例都未指定默认 XAML 命名空间以外的 XAML 命名空间。 在典型 WPF 应用程序中,默认 XAML 命名空间指定为 WPF 命名空间。 可以指定默认 XAML 命名空间以外的 XAML 命名空间,不过仍使用类似的语法。 但是,只要命名了在默认 XAML 命名空间中不可访问的类,则该类名之前必须有映射到对应 CLR 命名空间的 XAML 命名空间的前缀。 例如,<custom:Example/>
是用于实例化 Example
类的实例的对象元素语法,其中包含该类(并且可能有包含后备类型的外部程序集信息)的 CLR 命名空间以前映射到 custom
前缀。
有关 XAML 命名空间的详细信息,请参阅 WPF XAML 的 XAML 命名空间和命名空间映射。
XAML 定义了一个标记扩展编程实体,该实体可实现跳过字符串特性值或对象元素的正常 XAML 处理器处理,将处理延迟到后备类。 在使用特性语法时向 XAML 处理器标识标记扩展的字符是左大括号 ({),后跟右大括号 (}) 以外的任何字符。 左大括号后面的第一个字符串必须引用提供特定扩展行为的类,其中的引用可能会在子字符串“Extension”是真实类名的一部分时省略该子字符串。 此后可能会出现单个空格,随后扩展实现会将每个后续字符用作输入,直到遇到右大括号为止。
.NET XAML 实现使用 MarkupExtension 抽象类作为 WPF 以及其他框架或技术支持的所有标记扩展的基础。 WPF 专门实现的标记扩展通常旨在提供一种方法来引用其他现有对象,或是对将在运行时计算的对象进行延迟引用。 例如,通过指定 {Binding}
标记扩展来代替特定属性通常采用的值,可完成简单的 WPF 数据绑定。 许多 WPF 标记扩展为本来无法使用特性语法的属性实现了特性语法。 例如,Style 对象是一种相对复杂的类型,其中包含一系列嵌套的对象和属性。 WPF 中的样式通常定义为 ResourceDictionary 中的资源,然后通过请求资源的两个 WPF 标记扩展之一进行引用。 标记扩展将属性值的计算延迟到资源查找,并且可以在特性语法中提供 Style 属性的值(采用类型 Style),如以下示例所示:
<Button Style="{StaticResource MyStyle}">My button</Button>
此处,StaticResource
标识提供标记扩展实现的 StaticResourceExtension 类。 下一个字符串 MyStyle
用作非默认 StaticResourceExtension 构造函数的输入,其中从扩展字符串获取的参数会声明所请求的 ResourceKey。 MyStyle
应为定义为资源的 Style 的 x:Key 值。 StaticResource 标记扩展用法请求在加载时通过静态资源查找逻辑使用资源提供 Style 属性值。
有关标记扩展的详细信息,请参阅标记扩展和 WPF XAML。 有关在常规 .NET XAML 实现中启用的标记扩展和其他 XAML 编程功能的参考,请参阅 XAML 命名空间 (x:) 语言功能。 有关特定于 WPF 的标记扩展,请参阅 WPF XAML 扩展。
附加属性是在 XAML 中引入的一个编程概念,通过该概念,属性可以由特定类型拥有和定义,但设置为任何元素上的特性或属性元素。 附加属性的主要用途是使标记结构中的子元素可以向父元素报告信息,而不需要跨所有元素进行广泛共享的对象模型。 反之,父元素可以使用附加属性向子元素报告信息。 有关附加属性的用途以及如何创建自己的附加属性的详细信息,请参阅附加属性概述。
附加属性使用的语法表面上类似于属性元素语法,因为你也会指定 typeName.propertyName 组合。 有两个重要的差异:
即使通过特性语法设置附加属性时,也可以使用 typeName.propertyName 组合。 附加属性是特性语法中唯一要求限定属性名称的情况。
还可以对附加属性使用属性元素语法。 但是,对于典型属性元素语法,指定的 typeName 是包含属性元素的对象元素。 如果引用附加属性,则 typeName 是定义附加属性的类,而不是包含对象元素。
附加事件是 XAML 中引入的另一个编程概念,其中事件可由特定类型定义,但处理程序可以附加到任何对象元素上。 在 WOF 实现中,定义附加事件的类型通常是定义服务的静态类型,有时这些附加事件在公开服务的类型中通过路由事件别名公开。 附加事件的处理程序通过特性语法进行指定。 与附加事件一样,特性语法针对附加事件进行了扩展以允许使用 typeName.eventName,其中 typeName 是为附加事件基础结构提供 Add
和 Remove
事件处理程序访问器的类,而 eventName 是事件名称。
XAML 根元素剖析
下表显示一个经过细分的典型 XAML 根元素,其中显示了根元素的特定特性:
Attribute
可选的非推荐 XAML 用法
以下各节介绍 XAML 处理器在技术上支持的 XAML 用法,但这些用法会产生冗长或其他美学问题,在开发包含 XAML 源的应用程序时会影响 XAML 文件保持可读性。
可选属性元素用法
可选属性元素用法包括显式写出 XAML 处理器视为隐式的元素内容属性。 例如,当你声明 Menu 的内容时,可以选择将 Menu 的 Items 集合显式声明为 <Menu.Items>
属性元素标记,并将每个 MenuItem 放置在 <Menu.Items>
中,而不是使用隐式 XAML 处理器行为(Menu 中的所有子元素都必须是 MenuItem 并且放置在 Items 集合中)。 有时,可选用法可帮助直观地阐明标记中表示的对象结构。 或者,有时显式属性元素用法可以避免在技术上可正常运行但视觉上令人困惑的标记,例如特性值中的嵌套标记扩展。
完整 typeName.memberName 限定属性
特性的 typeName.memberName 形式实际上比路由事件情况更普遍。 但在其他情况下,该形式是多余的,你应避免使用它(如果只是出于标记样式和可读性的原因)。 在以下示例中,对 Background 特性的所有三个引用都完全等效:
<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 基类。
但是,以下 typeName.memberName 形式示例不起作用,因而显示为已注释掉:
<!--<Button Label.Background="Blue">Does not work</Button> -->
Label 是 Control 的另一个派生类,如果在 Label 对象元素中指定了 Label.Background
,则此用法将正常工作。 但是,由于 Label 不是 Button 的类或基类,因此指定 XAML 处理器行为是随后将 Label.Background
作为附加属性进行处理。 Label.Background
不是可用的附加属性,此用法会失败。
baseTypeName.memberName 属性元素
与 typeName.memberName 形式适用于特性语法的方式类似,baseTypeName.memberName 语法适用于属性元素语法。 例如,以下语法可正常工作:
<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>
此处,属性元素作为 Control.Background
提供,即使属性元素包含在 Button
中。
但是正如 typeName.memberName 形式对于特性一样,baseTypeName.memberName 是标记中的糟糕样式,应避免使用。
WPF 中的 XAML
XAML 命名空间 (x:)语言功能
WPF XAML 扩展
依赖项属性概述
TypeConverters 和 XAML
XAML 及 WPF 的自定义类