XAML,全称Extesible Application Markup Language,是一种与HTML和XML很相像的语言(或者可以说是HTML和XML的结合体),通过XAML可以对界面中控件及其各种属性进行定义。虽然一般在创建WPF程序后会自成XAML文件及其对应的cs代码文件,但是XAML与WPF并不是依附的关系,也就是说XAML文件可以单独使用(在一定条件下,比如没有Click这样的事件,很像.html文件),WPF程序中也不并要求一定有XAML文件。
XAML和HTML比较
由于XAML跟HTML很像,因此下面对两个代码做一个比较。
HTML源代码


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> </head> <body> <button>123</button> </body> </html>
XAML代码(窗体)


<Window x:Class="BasicControls.BrushExample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="BrushExample" Height="300" Width="300"> <Grid> </Grid> </Window>
看上面XAML代码可以发现其与HTML语言的第一个不同:在XAML中只能有一个根节点,这与XML一样,在WPF中,只能使用以下几个元素作为顶级元素:
1.Window元素:WPF窗体程序
2.Page元素:WPF网页程序
3.Application:用于对应用程序进行配置
4.ResourceDictionary:只能在资源字典文件中使用
其实可以把XAML的根元素看成是HTML代码中<!DOCTYPE …>标签、<head>标签与<body>标签的结合体,在XAML的根元素中,不仅使用xmlns定义了命名空间,也表示内容的开始,同时还包含了窗体(或页面)的标题(根元素为<Application>除外)。因此理解XAML语言很容易,有些HTML语言基础的人均可以写出XAML的代码。
在根元素中还有一些要事情要注意。首先在上面的XAML代码中出现的x:Class=”BasicControls.BurshExample”,这是在告诉XAML解析器,需要在BasicControls命名空间下生成一个BushExample的类,该类继承Windows基类(窗体程序如此;如果根元素为Page则继承自Page类;如果根元素为Application则继承自Application类),同时将XAML代码与这个类联系起来。
其次,上面XAML代码中xmlns:x=”…”,这可以算是一种特殊的表达方式。引号内的内容为名称空间,为了后面代码中表达的方便,使用x这个字母来代替引号中的内容。这个有点类似于写C#代码时,如果我们在定义一个变量时写”System.Collections.Generic.List<int> list;“时比较麻烦,因此使用在最前面写上”System.Collections.Generic;”,在代码中只写”List<int> list;”这样更方便也不易出错的表达方式。通过将名称空间命名为x,在后面需要用名称空间时就可以用x来代替,如之前提到x:Class。如果想在文件中声明System这个名称空间,并且在代码中使用sys代替System这个名称,可以进行如下定义:
xmlns: sys=”clr-namespace: Sysetm; assembly=mscorlib”
其中assembly是名称空间所在程序集的名称,如果是当前项目中的名称空间,assembly可以省略。声明完成后就可以在直接使用了,如<sys: XXX>…</sys: XXX>
由于XAML的这种特性在给元素命名的时候有了一种新的方式。在HTML中是给元素的Name属性赋值,如<button name=”btnOK”/>,在XAML中,不仅仅支持这种方式,还可以使用XAML中的Name特性进行命名,如<Button x:Name=”btnOK”>。这两种方式是等价的,但实现的机理不一样。在后一种表述方式中,XAML会添加如下字段,为相应的类生成下面的一部分:
private System.Window.Controls.Button btnOK;
而对于前一种方式,使用RuntimeNameProperty特性修饰过后才能正常工作。因为两种是等价的,因此使用哪种方式进行命名均可以。
与其他语言一样,在XAML中有特殊的字符:<、>、&、””,这些字符无法在界面上正常显示,如果要显示它们需要使用特定字符编码对他们进行表达,这个与HTML语言中一样。
XAML与HTML在语法上最后一块差异比较大的是控件属性的表达上,这一部分放到后面介绍WPF属性时统一介绍。
XAML与WPF
之前也提到XAML与WPF是相互补充又相互独立的技术。XAML在一定条件下可以独自使用,而WPF也可以在不使用XAML的前提下完成程序加载和编译。
1.XAML的独立性
单独使用XAML也可以运行,使用浏览器就可以打开这个文件,但是需要满足一定的条件,以下面的代码为例:


<Window x:Class="BasicControls.BrushExample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="BrushExample" Height="300" Width="300"> <Grid> <Button Content=”Click Me” Click=”button1_Click”/> </Grid> </Window>
如果要让上面的代码在浏览器中正常运行,需要做以下三步:
1.在根元素上删除类特性,即将x:Class=”…”这部分删除。
2.删除所有关联事件处理程序的特性,即将Click=”button1_Click”删除
3.将Window标签改为Page。因为浏览器只能显示页面,不能显示单独的窗口
修改后的代码如下:


<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="BrushExample" Height="300" Width="300"> <Grid> <Button Content=”Click Me“/> </Grid> </Page>
这样,XAML文件就可以在页面上正常的显示了。
2.WPF的独立性
在WPF中,如果没有XAML文件,程序也可以制作出一个窗口程序。
新建一个控制台项目LoadAndCompileXAML,在里面建一个类文件:CodeOnly.cs。代码如下:


using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LoadAndCompileXAML { class CodeOnly { } }
要将CodeOnly这个类做出传统窗口程序的效果,要做以下几个步骤:
1.在项目中添加如下引用:WindowBase、PresentationCore和PresentationFramework
2.在类中添加命名空间:System.Windows、System.Windows.Markup和System.Windows.Controls
3.该类需要继承自Window类
4.一般在建立窗口程序时,系统会自动在构造函数中添加InitializeComponent()方法。此方法定义了窗体上的控件和它们动作发生时对应的事件名称。因此在这个类的构造函数中添加初始化控件的方法,并在类中手动输入控件的布局、及其动作发生时对应的事件名称等。
5.添加事件内容,例如按钮单击事件Button_Click的事件内容
完成后的代码如下:


using System.Windows; using System.Windows.Markup; using System.Windows.Controls; namespace LoadAndCompileXAML { class CodeOnly : Window { private Button button1; public CodeOnly() { InitializeComponent(); } private void InitializeComponent() { // Configure the form. this.Width = this.Height = 285; this.Left = this.Top = 100; this.Title = "Code-Only Window"; // Create a container to hold a button. DockPanel panel = new DockPanel(); // Create the button. button1 = new Button(); button1.Content = "Please click me."; button1.Margin = new Thickness(30); // Attach the event handler button1.Click += button1_Click; // Place the button in the panel. IAddChild container = panel; container.AddChild(button1); // Place the panel in the form container = this; container.AddChild(panel); } private void button1_Click(object sender, RoutedEventArgs e) { button1.Content = "Thank you."; } } }
为了在运行程序时让窗口显示出来,对Program.cs文件中的代码也要做相应的修改。修改后的代码如下:


class Program : Application //添加System.Xaml的引用 { [STAThread] static void Main() { Program app = new Program(); app.MainWindow = new CodeOnly(); app.MainWindow.ShowDialog(); } }
这样就完成只使用代码创建一个窗口的工作。这种方式并不常见,而且需要程序员写的代码很多,但是对于需要随意定制应用程序,如添加或替换控件时,这种方式要比XAML有优势的多,因为如果使用XAML文档,控件不能随意的进行修改,而作为固定不变的资源加入到程序集中。
WPF中仅仅使用代码就可以完成窗体的创建,但还不仅仅局限于此,还可以使用XML文件辅助完成窗体的创建。在XML文件中记录的是控件的布局情况,例如:


<?xml version="1.0" encoding="UTF-8"?> <DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <Button Margin="30" Name="button1">Please click me.</Button> </DockPanel>
在类文件中,将控件定义的部分改成读取XML文件中的内容就可以了,类的内容如下:


using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.IO; namespace LoadAndCompileXAML { class CodeAndXml:Window { private Button button1; public CodeAndXml() { InitializeComponent(); } private void InitializeComponent() { // Configure the form. this.Width = this.Height = 285; this.Left = this.Top = 100; this.Title = "Dynamically Loaded XAML"; // Get the XML content from an external file. FileStream s = new FileStream("XMLFile1.xml", FileMode.Open); DependencyObject rootElement = (DependencyObject)XamlReader.Load(s); this.Content = rootElement; // Find the control with the appropriate name. button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1"); // Wire up the event handler button1.Click += button1_Click; } private void button1_Click(object sender, RoutedEventArgs e) { button1.Content = "Thank you."; } } }
这样同样可以完成创建窗体的任务。这是WPF中比较有趣的地方,而一般把XML文件称为未编译的XAML,通过XamlReader类对文件进行解析。但是如果页面比较复杂的话,时间消耗会比较大。