首页 > 代码库 > WPF之旅(二)- XAML
WPF之旅(二)- XAML
什么是XAML
XAML(Extensible Application Markup Lanaguage的简写,发音“zammel”)是用于实例化.NET对象的标记语言。尽管XAML是一种可以用于诸多不同问题领域的技术,但其主要作用是构造WPF用户界面。换言之,XAML文档定义了在WPF应用程序中组成窗口的面板、按钮以及各种控件的布局。
WPF之前的图形用户设计界面存在的问题
1. 每个图形元素(背景和按钮等)需要导出为单独的位图。这限制了组合位图的能力和使用动态效果的能力,如反锯齿、透明和阴影效果。
2. 相当多的用户界面逻辑都需要开发人员嵌入到代码中,图形设计人员无法控制其中的任何细节。
3. 在不同的图形元素之间没有固有的连接,经常会使用不匹配的图像集合。
4. 在调整图形大小时必然会损失质量。基于位图的界面依赖于分辨率,这意味着它不能适应大显示器以及高分辨率显示设置。
XAML变体
我们使用XAML表示整个XAML语言,它是一种基于通用XML语法,专门用于表示一颗.NET对象树的语言,它包括如下几个集合:
1. WPF XAML包含描述WPF的内容元素。
2. XPS XAML是WPF XAML的一部分,它为格式化的电子文档定义了一种XML表示方式。
3. Silverlight XML是一个用于Microsoft Silverlight应用程序的WPF XAML子集。
4. WF XAML包含描述WF(Work FLOW,工作流)内容的元素。
XAML编译
WPF使用BAML(Binary Application Markup Lanaguage,二进制应用程序标记语言)来克服这个缺点。BAML并非新事物,它实际上就是XAML的二进制表示,所有XAML都被转换成BAML,然后这些BAML作为资源被嵌入到最终的DLL或EXE程序集中。大多数程序员不用考虑XAML向BAML的转换,因为编译器会在后台执行这项工作。但也可以使用未经编译的XAML,这对于需要即时提供一些用户界面的情况可能有意义的(例如从某个数据库中提取内容作为一块XAML标签)。
XAML标准
1. XAML文档中的每个元素都映射为.NET类的一个实例,元素的名称也完全对应于类名。
2. 与所有XML文档一样,可在一个元素嵌套另一个元素。
3. 可通过特性(Attribute)设置每个类的属性(Property),在某种情况下,特性不足以完成既需要通过特殊的语法使用嵌套的标签(tag)。
XAML的命名空间
xmlns特性是XML中的一个特殊特性,它专门用来声明名称空间,在创建的所有WPF XAML中都会出现这两个命名空间。
1. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"是WPF核心命名空间。它包含了所有WPF类,包括用来构建用户界面的控件。
2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"是XAML命名空间。它包括了各种XAML实用特性,这些特性可影响文档的解释方式。该命名空间被映射为前缀x。这意味着可通过在元素名称之前放置命名空间前缀x来使用该命名空间(例如:<x:ElementName>),
“x”前缀/XAML语言XAML命名空间常见的结构:
x:key:为XAML ResourceDictionary中的每一个资源设置唯一的用户定义密匙。
x:class:为XAML页面提供代码隐藏类指定代码命名空间和代码名称。例如<window x:Class="WindowApplication1.Window1">。
x:Name:在处理XAML中定义的对象元素后,为运行时代码中存在的对象指定一个运行时的对象名。你可将在XAML中设置x:Name看作是代码中声明命名变量。例如:<Grid x:Name="grid1">
x:Uid:标识某些元素,应将本地化后的资源用与该元素的一些属性值。如下图资源文件:
将控件关联到资源文件:<TextBlock x:Uid="Greeting" Text="" />
XAML中的属性和事件
1. 类型转换器:为了关联字符串值和非字符串属性,XAML解析器需要执行转换。由类型转换器执行转换,其提供了使用的方法,可将特定的.NET数据类型转换为任何其他.NET类型或将任何其他.NET类型转换为指定的数据类型。XAML通过两个步骤来查找类型转换器:
a. 检查属性声明,查找TypeConverter特性。
b. 如果属性声明中没有TypeConverter,XAML解析器将检查对应数据类型的类声明。
注意:与所有基于XML的语言一样,XAML也区分大小写,这意味着不能用<button>替代<Button>, 然而,类型转换器通常不区分大小写,这意味着ForeGround="White"和ForeGround="white"具有相同的效果。
2. 复杂属性
虽然类型转换器便于使用,但他们不能解决所有的实际问题,幸运的是XAML提供另一种选择:属性元素语法(property-element-syntax)。使用属性元素语法,可添加名称形式为Parent.PropertyName的子元素。例如:
<Grid>
<Grid.Background>
Blue
</Grid.Background>
</Grid>
真正起作用的是元素名中的句点(.)。这个句点把该属性和其他类型的嵌套内容区分开来。这个句点把该属性和其他类型的嵌套内容区分开来。下面我们来看下嵌套属性:
<Grid>
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.00" Color="Red"></GradientStop>
<GradientStop Offset="0.50" Color="Indigo"></GradientStop>
<GradientStop Offset="1.00" Color="Violet"></GradientStop>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
</Grid>
任何XAML标签集合都可以用一系列执行相同任务的代码语句替换,上面显示的标签与以下代码等价:
var brush = new LinearGradientBrush();
var gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Red;
brush.GradientStops.Add(gradientStop1);
var gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
var gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Violet;
brush.GradientStops.Add(gradientStop3);
grid1.Background = brush;
3. 标记扩展
标记扩展允许XAML文件访问只根据支持类型声明元素的值或行为,对大多数属性而言,XAML属性语法可以工作得非常好。但有些情况,不能硬编码属性值,例如,可能希望将属性值设置为一个已经存在的对象,或者可能希望通过一个属性绑定到另一个控件来动态得设置属性值,这些情况下都需要使用标记扩展-一种以非常规的方式设置属性的专门语法。
标记扩展可用于嵌套标签或XML特性中,当用于特性中时,他们总是被花括号{}包围起来。例如:
<Button Foreground="{x:Static SystemColors.ActiveCaptionBrush}"></Button>
标记扩展使用{标记扩展类 参数}语法。在上面示例中,标记扩展是StaticExtension类(根据约定,在引用扩展类时可以省略最后一个单词Extension)。x前缀指示在XAML名称空间中查找StaticExtension类。上面的XAML的最终结果和下面代码相同:
cmdBtn.Foreground = SystemColors.ActiveCaptionBrush;
因为标记扩展映射为类,所以他们也可以用作嵌套属性,如下:
<Button>
<Button.Foreground>
<x:Static Member="SystemColors.ActiveCaptionBrush"></x:Static>
</Button.Foreground>
</Button>
4. 附加属性
附加属性(attached property)是XAML中引入的编程概念,借此,特殊类型可以拥有和定义属性,但在任何元素上都将属性设置为特性或者属性元素。它的工作原理是每个控件有个子固有的属性(例如,文本框有其特定的字体,文本颜色吗,这些通过FontFamily, ForeGround属性指定的)。当在容器放置控件时,根据容器的类型控件会获得额外特征。
附加属性使用使用包含两个部分的命名形式:定义类型.属性名。例如,通过附加属性在网格的每一行放置各个控件:
<TextBox Grid.Row="0">Some words</TextBox>
<TextBox Grid.Row="1">Some words</TextBox>
附加属性根本不是真正的属性,他们实际上被转换为方法调用。XAML解析器采用以下形式的调用静态方法:DefiningType.SetPropertyName()。例如,在上面的XAML代码中,定义类型为Grid类,并且属性是Row,所以解析器调用Grid.SetRow()方法,具体为: Grid.SetRow(txtAns,0)。
5. 嵌套元素
XAML文档被排列成一棵巨大的嵌套的元素树,XAML让每个元素决定如何处理嵌套的元素,这种交互使用下面三种机制中的一种,而且求值的顺序也是下面列出的这三种机制的顺序:
a. 如果父元素实现了IList接口,解析器将调用IList.Add()方法,并且该方法传入子元素作为参数。
b. 如果父元素实现了IDictionary接口,解析器将调用IDictionary.Add()方法,并且为该方法传递子元素作为参数。当时用字典集合时,还必须设置x:Key特性以便为每个条目指定键名。
c. 如果父元素使用ContentProperty特性进行修饰,解析器将使用子元素设置对应的属性。
6. 特殊字符与空白
XAML受到XML规则的限制,如果我们试图使用一些特殊字符,例如,&,<和>,将会遇到麻烦,因为XAML解析器认为你正在处理其他事情。有时候这并不是期望的结果,这些情况下,需要为元素使用特定的转换,例如<l替代<, >替代>,当希望文本包含一系列空格,需要为元素使用xml:space="preserve"特性。
XAML的事件
特性也可以用于关联事件处理程序,语法为:事件名="事件处理程序方法名",例如:
<Button ... Click="cmdBtn_Click"></Button>
下面是一个和要求的方法:
private void cmdBtn_Click(object sender, RoutedEventArgs e)
{
//do something
}
当添加事件处理程序特性时,VS的只能感应可提供极大的帮助,VS会显示一个包含代码隐藏类中的所有合适的事件处理程序的下拉列表,用户可以选择并创建这个事件处理程序。
本人最近开始学习WPF以及DevExpress,藉此提升自己C/S架构编程的能力。在学习过程会有一些心得体会,于是便会写一些博客来记录这些想法,有兴趣的朋友可以和我一起交流学习。那么就让我们从这里开始WPF与DevExpress的旅程吧!
QQ群: 32745894,欢迎大家加入讨论!
WPF之旅(二)- XAML