本文是作者学习WPF从入门到放弃过程中的一些总结,主要内容都是对学习过程中拜读的文章的整理归纳。
参考资料
- XAML 概述 (WPF):https://msdn.microsoft.com/zh-cn/library/ms752059.aspx
- XAML 语法详述:https://msdn.microsoft.com/zh-cn/library/ms788723.aspx
- 闲话WPF之二(XAML概述):http://www.cnblogs.com/YilingLai/archive/2006/12/14/591944.html
- [MS-XAML]XAML语言规范(哪位大神翻译一下啊)::http://go.microsoft.com/fwlink/?LinkId=114525
- WPF XAML 的 XAML 命名空间和命名空间映射:https://msdn.microsoft.com/zh-cn/library/ms747086.aspx
- XAML 中的空白处理:https://msdn.microsoft.com/zh-cn/library/ms788746.aspx
- 标记扩展和 WPF XAML:https://msdn.microsoft.com/zh-cn/library/ms747254.aspx
- TypeConverters 和 XAML https://msdn.microsoft.com/zh-cn/library/aa970913.aspx
- xamalot http://www.xamalot.com/
什么是XAML
XAML 是一种声明性标记语言。 XAML 简化了为 .NET Framework 应用程序创建 UI 的过程。 可以在声明性 XAML 标记中创建可见的 UI 元素,然后使用代码文件(通过分部类定义与标记相连接)将 UI 定义与运行时逻辑相分离。 XAML 直接以程序集中定义的一组特定后备类型表示对象的实例化。这与大多数其他标记语言不同,后者通常是与后备类型系统没有此类直接关系的解释语言。 XAML 实现了一个工作流,通过此工作流,各方可以采用不同的工具来处理应用程序的 UI 和逻辑。
以文本表示时,XAML 文件是通常具有 .xaml 扩展名的 XML 文件。 可通过任何 XML 编码对文件进行编码,但通常编码为 UTF-8。
下面的内容表示一个带有一个按钮的WINDOW窗体的XAML描述
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ExampleNamespace.ExamplePage">
<Button Click="Button_Click" >Click Me!</Button>
</Window>
XAML语法介绍
XAML 中的大小写和空白
XAML 区分大小写。 按名称与程序集中的基础类型进行比较或者与类型的成员进行比较时,对象元素、属性元素和特性名称均必须使用区分大小写的形式指定。 XAML 语言关键字和基元也区分大小写。 值并不总是区分大小写。 值是否区分大小写将取决于与采用该值的属性关联的类型转换器行为,或取决于属性值类型。 例如,采用 Boolean 类型的属性可以采用 true 或 True 作为等效值。
WPF XAML 处理器和序列化程序将忽略或删除所有无意义的空白,并规范化任何有意义的空白。 这与 XAML 规范的默认空白行为建议一致。 通常,只有当您在 XAML 内容属性中指定字符串时,此行为的重要性才会体现出来。 简言之,XAML 将空格、换行符和制表符转化为空格,如果它们出现在一个连续字符串的任一端,则保留一个空格。
对象元素
对象元素将XAML中的元素与.NET中的类映射,对象元素以左尖括号(<)始,右面紧跟着类名。对象元素可以包含零个或多个特性,使用空格分隔每一个"特性="值""对。元素定义必须满足下面两条
元素和标记需要已
/>
结尾-
开始标记必须以
>
结尾。其他对象元素、属性元素或者内部文本可以跟在开始标记后面。可以包含的内容会受到到模型约束。<StackPanel>
<Button Content="Click Me"/>
</StackPanel>
上面的代码声明了两个对象元素StackPanel
和Button
,运行时两个对象元素会映射成System.Windows.Controls.StackPanel
和System.Windows.Controls.Button
类的对象,映射出的Button对象的Content属性会被设置为"Click Me",创建对象时会调用默认的构造函数(无参构造函数)。
特性元素
特性是用来描述对象元素的属性,特性名必须是对象元素相关类的属性或者事件,并且该属性或事件的访问级别应该是public
的。
特性值的处理
目前没有掌握,只了解了有三种特性的特性值是被特殊处理的。
- 带有{}大括号的特性值
- 如果属性是用特性化 TypeConverter 声明的(目前还不明白)
- 如果没有 TypeConverter(目前还不明白)
枚举特性值
设置枚举特性值时,不能使用枚举的具体值,而要通过指定枚举的某个命名常量的字符串名称。
设置枚举特性值时,不需要使用枚举类型.枚举值
的方式,可以直接使用枚举值的字符串。
如果枚举是按位枚举,可以使用,
分隔多个枚举值。
属性元素
在XML中特性的值一般是字符串,但是字符串并不能很好的描述对象之间的关联。在XAML中可以指定对象元素为 属性的值,但是并不将属性指定为元素标记内的特性,而是使用元素的开始标记指定“元素类型名称.属性名称”形式的属性,在其中指定属性的值,然后结束属性元素。
例如,下面的属性元素语法针对的是Button
的ContextMenu
属性
<Button>
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="1">First item</MenuItem>
<MenuItem Header="2">Second item</MenuItem>
</ContextMenu>
</Button.ContextMenu>
Right-click me!</Button>
集合语法
在XAML中可以添加值为集合类型的属性元素。但属性元素必须继承自
- IList
- IDictionary
- Array
集合检测功能不支持泛型列表和字典接口(IList 和 IDictionary<TKey, TValue>)。 但是,可以将 List 类用作基类(因为它直接实现 IList),或者将 Dictionary<TKey, TValue> 用作基类(因为它直接实现 IDictionary)。
如果属性的值是集合类型的不需要以对象元素的形式指定集合的类型,集合中的项标记为属性元素的一个或者多个子元素。我们将这种集合称为隐式集合
下面的例子是显示集合的对象元素的语法,并不是所有的集合都可以显示声明,显示声明要求集合必须支持默认构造函数。
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0.0" Color="Red" />
<GradientStop Offset="1.0" Color="Blue" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
除了根元素,XAML文件中另一个元素的子元素形式嵌套的每个对象元素实际上属于下面两种情况之一:父元素的隐式集合属性成员,父元素指定XAML内容属性的元素。。换言之,一个标记页上的父元素与子元素之间的关系实际上就是一个根对象,而根对象下面的每个对象元素要么是为父元素提供属性值的一个实例,要么是同样作为父元素的集合类型属性值的集合中的一项。
内容属性
一个类可以指定它的一个且仅一个属性为 XAML 内容属性,指定的方法是在类声明时加ContentPropertyAttribute
特性,并设置内容属性的NAME
。可以在 XAML 标记中设置该属性时省略属性元素。
例如:Border
指定内容属性Child
。系统处理下面两个Border元素的方式相同。 第一个元素利用了内容属性语法而省略了Border.Child
属性元素。 第二个元素显式标明Border.Child
。
<Border>
<TextBox Width="300"/>
</Border>
<!--显示指定-->
<Border>
<Border.Child>
<TextBox Width="300"/>
</Border.Child>
</Border>
XAML 内容属性的值必须完全在该对象元素的其他任何属性元素之前或之后指定。 无论将 XAML 内容属性的值指定为字符串还是指定为一个或多个对象都是如此。
文本内容
有少量XAML元素可以直接将文本作为期内容。但必须满足以下条件之一:
- 类必须声明一个内容属性,并且该内容属性必须是可符值给字符串的类型(可以是
Object
)例如,任何ContentControl
都将Content
用作其内容属性,并且其类型为Object
,这样就支持实际的ContentControl
,Button
上的如下用法:<Button>Hello</Button>
。 - 类型必须声明一个类型转换器,该类型转换器将将文本内容作为初始化文本。例如,
<Brush>Blue</Brush>
。 - 类型是已知的 XAML 语言基元类型
内容属性与特殊元素不能混合使用下面的代码会报错
<Button Content="This example">This example</Button>
内容属性和集合语法组合
当内容属性的类型为集合时,则不需要标记中指定隐含的集合类型为对象元素,也不需要指定该XAML内容属性作为属性元素。
所有 Panel 派生类都证实 XAML 内容属性为 Children(它要求值为 UIElementCollection 类型)
<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遇到这样的符号以后,就不在把属性值作为字符串处理了。 左大括号后面的第一个字符串必须引用用来提供特定扩展行为的类,如果子字符串“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 属性值。
类型转换器
特性元素值必须设定为字符串,但是对字符串如何转换为其他对象类型或基元值的基本类型处理取决于 String
类型本身,以及对某些类型(如 DateTime 或 Uri)的本机处理。 但是很多 WPF 类型或这些类型的成员扩展了基本字符串特性处理行为,因此可以指定更复杂的对象类型的实例作为字符串和特性。
下面的示例使用类型转换和特性语法来为 Margin 提供值,Margin的类型为Thickness
,该类型可以实现类型转换。
<Button Margin="10,20,10,30" Content="Click me"/>
附加属性和附加事件
在特性中使用附加属性的语法为所有者类型.属性名
的形式
所有者类型”始终是一种与从中要设置附加属性的对象元素不同的类型。 “所有者类型”这种类型提供 XAML 处理器为获取或设置附加属性值所需要的访问器方法。
使用附加属性的最常见方案是使子元素能够向其父元素报告属性值。
下面的示例演示了 DockPanel.Dock 附加属性。 DockPanel 类为 DockPanel.Dock 定义访问器,因此拥有附加属性。 DockPanel 类还包括一个逻辑,该逻辑迭代其子元素并具体检查每个元素是否具有 DockPanel.Dock 设置值。 如果找到一个值,将在布局过程中使用该值定位子元素。
<DockPanel>
<Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
<Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>
看了一些文章还不能很好的理解。
根元素和命名空间
下表显示了一个经过分解的典型 XAML 根元素,同时显示了根元素的具体特性:
<Page | 根元素的开始对象元素 |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | 默认 (WPF) XAML 命名空间 |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | XAML 语言 XAML 命名空间 |
x:Class="ExampleNamespace.ExampleCode" | 类声明,它将标记连接到为类定义 |
> | 根的对象元素的末尾。 由于该元素包含子元素,因此对象未结束 |
请注意,只有在每个 XAML 文件的根元素上,xmlns 特性才是绝对必需的。 xmlns 定义将适用于根元素的所有子代元素。同时允许根以下的其他元素上具有 xmlns 特性,这些特性将适用于定义元素的任何子代元素。 但是,频繁定义或重新定义 XAML 命名空间可能会导致 XAML 标记样式难以阅读。