第一章:XAML概览
1.1、XAML是什么
软件开发中最常见的分工合作就是设计师和程序员之间的协作。
XAML在桌面开发及富媒体网络程序的开发中扮演了HTML+CSS+JavaScript的角色,成为设计师与程序员沟通的枢纽。
绚丽的UI和动画会以XAML的形式直接保存进项目,无需转化就可以直接编译,节省了大量的时间和成本。
简而言之:XAML是WPF技术中专门用于设计UI的语言。
1.2、XAML的优点
真正实现了UI与逻辑的剥离。
XAML是单纯的声明型语言,它只能用来声明一些UI元素、绘制UI和动画(在XAML中实现动画是不需要编程的),根本无法加入程序逻辑。
与UI相关的元素全部集中在UI层、与逻辑相关的都在程序逻辑层,形成了一种”高内聚—低耦合”的结构。这种结构无论是对UI或者底层代码有重大修改都不会花费太多力气。
第二章:从零起步认识XAML
2.1、新建WPF项目
我使用的是Visual Studio2019,系统是Window10,这里简单演示一下新建WPF项目:
上面这个图与创建过程无关,只是我自己记录一下一些命令操作。
打开新建的项目之后,可以在解决方案资源管理器中发现一堆文件:
- 依赖项/引用:标记了当前这个项目需要引用哪些其他的项目。目前里面列出来的条目都是.NET Core框架的类库,有时候还要添加其他的类库或者别的程序员编写的项目以及类库等。
- App.xaml:程序的主体。大家都知道,在Windows系统里,一个程序就是一个进程(Process)。Windows还规定,一个GUI进程需要有一个窗体(Window)作为"主窗体"。App.xaml文件的作用就是声明了程序的进程会是谁,同时指定了程序的主窗体是谁。在这个分支里还有一个文件——App.xaml.cs,它是App.xaml的后台代码。
- MainWindow.xaml:程序的主窗体。我们第一眼看到的那个窗口(下图)就是由它声明的。它也具有自己的后台代码MainWindow.xaml.cs。
- Proprities(一般是没有的,用到了才有):里面的主要内容是程序要用到的一些资源(如:图标、图片、静态的字符串)和配置信息。
2.2、剖析最简单的XAML代码
分析的重点是MainWindow.xaml和它的后台代码。在MainWindow.xaml文件里能看到以下代码:
XAML使用标签声明一个元素(每个元素对应内存中的一个对象)时,需要使用起始标签
为了表示同类标签中的某个标签与众不同,可以给它的特征(Attribute)赋值。为特征赋值的语法如下:
- 非空标签:<Tag Attribute1=Value1 Attribute2=Value2>Content</Tag>
- 空标签:<Tag Attribute1=Value1 Attribute2=Value2/>
没什么事请不要手贱加多余的空格,例如在等号左右两边加空格。
在这里有必要把Attribute和Property这两个词仔细地辨别一下:
- Property:属于面向对象理论范畴。在使用面向对象思想编程的时候,常常需要对客观事物进行抽象,再把抽象出来的结果封装成类,类中用来表示事物状态的成员就是Property。比如要写一个模拟赛车的游戏,那么必不可少的就是对现实汽车的抽象。现实中的汽车身上会带有很多的数据,但在游戏中只关心它的长度、宽度、高度、重量、速度等有限的几个数据。同时,还会把汽车"加速"、"减速"等一些行为也提取出来并用算法进行模拟,这个过程就是抽象(结果就是Car这个类)。显然,Car.Length、Car.Height、Car.Speed等表达的是汽车当前处于一个什么状态,而Car.Accelerate()、Car.Break()表达的是汽车能做什么。因此,Car.Length、Car.Height、Car.Speed就是Property的典型代表,将Property翻译为"属性"也很贴切。**<font style="color:#E8323C;">总结一句话而言,Property就是针对对象而言的</font>**。
- Attribute:是编程语言文法层面的东西。比如有两个同类的语法元素A和B,为了表示A与B不完全相同或者A与B在用法上有些区别,这时候就要针对A和B加一些Attribute。也就是说,Attribute只与语言层面上的东西相关,与抽象出来的对象没什么关系。因为**<font style="color:#E8323C;">Attribute是为了表示"区分"的,所以把它译为"特征"</font>**。C#中的Attribute就是这种应用的典型例子,我们可以为一个类添加Attribute,这个类的类成员中有很多Property。显然Attribute只是用来影响类在程序中的用法,而Property则对应着抽象对象身上的性状,他们根本不是一个层面上的东西。
使用能够进行面向对象编程的标签式语言知识把标签与对象做了一个映射,同时把标签的Attribute与对象的Property也做了一个映射——针对标签还是叫Attribute,针对对象还是叫Property仍然不是一个层面上的东西。而且,标签的Attribute与对象的Property也不是完全映射的,往往是一个标签所具有的Attribute对于他所代表的对象的Property。
因为XAML是用来在UI上绘制控件的,而控件本身就是面向对象抽象的产物,所以XAML标签的Attribute里就有一大部分是与控件对象的Property互相对应的。当然,这还意味着XAML标签还有一些Attribute并不对应控件对象的Property。
将上图中的代码进行简化:
可以理解为是一个
XAML是一种”声明”式语言,当你见到一个标签,就意味着一个标签,就意味着声明了一个对象,对象之间的层级关系要么是并列、要么是包含,全都体现在标签的关系上。
、
以上这些代码就是
其中,Title、Height和Width一看就知道是与Window对象的Property相对应的。中间两行(即两个xmls)是在声明名称空间。最上面一行是在使用名为Class的Attribute,这个Attribute来自于x:前缀所对应的名称空间。下面仔细解释:
前面已经说过,XAML语言是从XML语言派生出来的。XML语言有一个功能就是可以在XML文档的标签上使用xmlns特征来定义名称空间(Namespace),xmlns也就是XML-Namespace的缩写了。定义名称空间的好处就是,当来源不同的类重名时,可以使用名称空间加以区分。xmlns特征的语法格式如下:
xmlns[:可选的映射前缀 ]=”名称空间 “
xmlns后可以跟一个可选的映射前缀,之间用冒号分隔。如果没有写可选映射前缀,,那就意味着所有来自于这个名称空间的标签前都不用加前缀,这个没有映射前缀的名称空间称为”默认名称空间”——默认名称空间只能有一个,而且,应该选择其中元素被最频繁使用的名称空间来充当默认名称空间。上面的例子中,
这里做一个有趣的小实验:
如果给第二行声明的名称空间加上一个前缀,比如n,那么代码必须改成这样才能编译通过:
引用,然后再在根元素的起始标签中写上一句:xmlns:c=”clr-namespace:System.Windows.Controls;assembly=PresentationFramework”。c是映射前缀,换成其他的字符串(如:control)也可以。因为Button来自前缀c对应的名称空间,所以在使用Button的时候就要写成
xmlns:c=”clr-namespace:System.Windows.Controls;assembly=PresentationFramework”这一长串看上去恐怖,但是你输入的时候软件会给提示。
P.S.这里是用的书上的例子,可能作者用的是Framework框架,所以末尾那里是Framework。
默认引用进来的两个名称空间格外重要,它们所对应的程序集和.NET名称空间如下:
也就是说,你在XAML代码中可以直接使用这些CLR名称空间中的类型(因为默认XML名称空间没有前缀)。
http://schemas.microsoft.com/winfx/2006/xaml 这个名称空间则对应一些与XAML语法和编译相关的CLR名称空间。使用这些名称空间中的类型时需要加x前缀,因为它们被映射到了名为x的XML名称空间中。(名为x的XML名称空间将在后面介绍)
从这两个名称空间的名字和它们所对应的.NET程序集上,我们不难看出,第一个名称空间对应的是与绘制UI相关的程序集,是表示(Presentation)层面上的东西;第二个名称空间则对应XAML语言解析处理相关的程序集,是语言层面上的东西。
还剩下 x:Class=”First.MainWindow”这个Attribute。x:前缀说明这个Attribute来自于x映射的名称空间——前面刚刚分析过,这个名称空间是对应XAML解析功能的。x:Class,顾名思义它与类有些关系,是何种关系呢?让我们做个有趣的实验:
首先,把 x:Calss=”MyFirstWpfApplication.Window1”这个Attribute删掉,再到Window1.xaml.cs文件里,把构造器中对InitializeComponent方法的调用也删掉。编译程序,你会发现程序仍然可以运行。为什么呢?打开App.xaml文件,你能发现一个Attribute——StartupUri=”Window1.xaml”,它是在告诉编译器把由Window1.xaml解析后生成的窗体作为程序启动时的主窗体。也就是说,只要Window1.xaml文件能够被正确解析成一个窗体,程序就可以正常运行。
然后,只恢复x:Class=”MyFirstWpfApplication.Window1”这个Attribute(不恢复对InitializeComponent方法的调用),并更改它的值为x:Class=”MyFirstWpfApplication.WindowABC”.编译之后,仍然可以正确运行。这时,使用IL Dissassembler(中间语言反编译器,如下图)打开项目的编译结果,你会发现在由项目编译生成的程序集里包含一个名为WindowABC的类,如下图。
这说明,x:Class这个Attribute的作用是当XAML解析器将包含它的标签解析成C#类后,这个类的类名是什么。这里,已经触及到XAML的本质。p27