XAML 简述
XAML(eXtensible Application Markup Language,可扩展应用程序标记语言)是微软公司创建的一种新的描述性语言,用于搭建应用程序用户界面。XAML实现了用户界面与业务逻辑完全分离。XAML是一种解析性的语言,尽管它也可以被编译。它的优点是简化编程式上的用户创建过程,应用时要添加代码和配置等。
在第1章中,对照了HTML与XAML两者的异同点。发现尽管XAML在元素的声明、程序样式的设置等语法结构上和HTML上有相似之处,但XAML有别于HTML最显著的特征是xmlns指令和标记扩展。其中,xmlns指令需要在标记中关联名称空间。
XAML并不是HTML,而是基于XML的,是WPF的外在表现形式。而HTML只是一种标记语言,仅仅是用来为浏览器呈现页面内容。XAML可以用来呈现信息和请求用户输人等基本的功能,它还支持动画和3D等高级特性。
XAML是可扩展的,开发人员可以创建自定义的控件、元素和函数来扩展XAML。由于XAML各元素在本质上就是WPF类的映射,因此开发人员可以很轻松地使用面向对象的技术对XAML元素进行扩展。也就是说,可以开发一些自定义控件和组合元素,用户界面设计人员和开发人员直接使用即可,软件真正做到了可复用性。
XAML的发音为"Zamel"。虽然XAML包含了许多新规则、元素和语法,但我们并不认为学习它是一个麻烦的过程。只要读者稍微具备一些HTML基础知识,就可以快速地掌握XAML中的大部分内容。
可扩展应用程序标记语言——XAML
XAML 文档框架
创建一个新的WPF项目 WpfApp2
如何创建项目 参见之前的文章:
WPF编程基础———第一章 引言 创建一个WPF项目.
所生成的基本项目结构
我们目前分析的XAML为MainWindow.xaml,后台的cs代码等等之后都会渐渐提到
系统自动生成的代码如下:
VS2019版本自动生成的代码之中有更多默认xmlns指令
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
在此首先了解XAML文件基本框架,这个文档包含一个顶级元素Window和一个Grid元素。Window代表整个窗口,Grid布局上可以放置所需控件。一个XAML文件只能有一个顶级元素。
WPF中可以当顶级元素的还有Page元素和Application元素。
Page用于可导航的应用程序; Application用于定义应用程序的资源和启动设置。
分析Window开始标签,依照自上而下的顺序,它包含一个类名、五个xmlns指令关联的名称空间,三个属性(Title、Height、Width)以及一个有关于兼容性mc名称空间的指令。
XAML 文档结构
在XAML文件基本框架上,为此程序添加设计要求:在Button上放置一个Image和一个TextBox。在<Grid>
与</Grid>
之间,添加XAML代码来实现设计要求。
XAML注释方式 :<!-- -->
<!-- window 代码部分省略 -->
<Grid>
<Button Width="600" Height="400">
<Button.Content>
<!-- 设置为垂直方向 -->
<StackPanel Orientation="Vertical">
<Image Source="battle.jpg" Width="600" Height="375"></Image>
<TextBox Text="Hathaways Flash" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
</StackPanel>
</Button.Content>
</Button>
</Grid>
</Window>
效果图
根据运行之后的效果图和分析代码的逻辑结构我们可以知道,Window为顶级元素,在Grid布局下有一个Button,在Button下使用StackPanel布局后加入一个Image和TextBox。
此XAML文档树形结构
应用程序的UI在用户看来是平面结构的,与传统设计结构不同的是,在后台开发者面对的XAML是树形逻辑结构的,不过Button上放置其他控件的方式有很多,但文档框架始终都是树形结构。
XAML 基础语法
XAML中最基本的语法元素与XML类似,下面回顾一下XML中的标签、属性和内容的语法。
标签
标签通常是以<>开始,以</>结束的,一一个标签的声明通常表示一个对象。 如<Window></Window>
、<Grid></Grid>
分别定义了一个窗体对象及一个Grid对象,标签定义有以下两种常用写法。
(1)非自闭合标签:如<Window></Window>
、<Grid></Grid>
,标签要成对出现。
(2)自闭合标签:如<Window />
、<Grid/>
、<Button/>
。
这种自闭合标签用于无内容情况下,可以让代码看上去更简洁,当然,正常情况下Window及Grid都是有内容的。
属性
属性通常以键值对形式出现,形式如下。
Attribute = Value
例如,<Window></Window>
标签中的"Title=“MainWindow” Height=“450” Width=“800” ",等号左边表示Window标签的属性,等号右边表示该属性的值。
内容
一组标签对之间夹杂的文本或其他标签都称为这个标签之间的内容。此处Window标签的内容就是一对<Grid></Grid>
标签。
XAML 中的属性
继续使用WpfApp2这个解决方案,根据需要还会不断丰富其功能,以便配合学习讲解XAML
XAML 简单属性
XAML是声明性语言,XAML编译器给每个标签创建一个与标签对应的对象,再对其属性初始化。所以,每个标签是要先声明对象,再为对象赋初值。赋值方法有两种:一种是XAML中的字符串简单赋值,另外一种是在后台CS代码中,使用属性元素进行复杂赋值。
1.字符串简单赋值
XAML使用标签定义UI元素,每一个标签对应。NET Framework类库的一个 控件类。通过设置标签的Attribute(属性),实现标签对应的控件对象Property(属性)赋值。
首先在WpfApp2新建一个新的WPF项目:
其中Grid标签之间的代码
<Grid>
<!-- 设置名称属性为rectangle 颜色为Blue 边界为左50,上75,右125,下175 -->
<Rectangle x:Name="rectangle" Fill="Blue" Margin="50,75,125,175"></Rectangle>
</Grid>
效果图
其中在VS2019切换启动项目在菜单栏下方
2.属性元素复杂赋值
在CS代码中,创建对象,并设置他们的属性,代码如下:
/*using System... 省略*/
namespace Rectangle
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
SolidColorBrush scb = new SolidColorBrush();
scb.Color = Colors.Yellow;
this.rectangle.Fill = scb;
}
}
}
效果图
两种赋值方法相对照,这里需解释
Fill
属性的用法。因为Fill
类型是Brush
,但是Brush
一个抽象类,由于抽象类不能实例化.所以只能用抽象类的子类实例赋值,Brush
的派生类简要说明如下。
- SolidColorBrush:使用纯Color绘制区域;
- LinearGradientBrush:使用线性渐变绘制区城;
- RadialGradientBrush:使用径向渐变绘制区域;
- ImageBrush:使用图像(由ImageSource对象表示)绘制区域:
- DrawingBrush:使用Drawing绘制区域。绘图可能包含向量和位图对象:
- VisualBrush:使用Visual对象绘制区域,使用VisualBrush可以将内容从应用程序的一个部分复制到另一个区域,这在创建反射效果和放大局部屏幕时会非常有用。
XAML 复杂属性
在WpfApp2中加入新的设计需求: Grid的背景使用渐变色,渐变方案是:由红色到黄色,再由黄色到绿色。要实现该功能就需要对Grid.Background
中的LinearGradientBrush
进行设置,代码如下。
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Yellow" Offset="0.6"/>
<GradientStop Color="Green" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<!-- 保留原Button后续代码 -->
效果图
根据如上所示的效果,其中代码中的
Grid.Background
便是复杂属性。下面介绍程序中用到的属性。
现在我们知道LinearfGradientBrush
的功能是线性渐变笔刷,填充线性渐变颜色到当前区域,故可搭配两种或两种以上的颜色。
它的的重要属性有GradientStop(倾斜点)、Color(渐变颜色)、Offset(偏移量)、StartPoint(起点坐标)、EndPoint(终点坐标)。
LinearGndientBrush的渐变色是由多个GradientStop组成的,其中的Color和Offset分别表示渐变色值和颜色起始位置,Offset宽度是整个绘制区域,取值范围为0~1.0。
XAML 附加属性
继续在WpfApp2中加入新的设计需求,在Grid布局中加入一个蓝色矩形与之前的button并列
效果图如下
要实现如上的效果,首先我们需要对Grid布局中新增加一列,对<Grid.ColumnDefinitions>
属性设置代码如下:
<!-- 保留之前的代码 -->
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
要将<Rectangle>
置于<Grid>
的第1列,类似程序语言中的数组,Grid布局的起始行与列都是从0开始,
<!-- 保留之前的代码 -->
</Button>
<Rectangle x:Name="Rec" Grid.Column="1" Fill="Blue" Margin="25"></Rectangle>
</Grid>
</Window>
此例之中用的属性,<Grid.ColumnDefinitions>
是复杂属性,Grid.Column="1"
位于<Rectangle>
内部,这就是附加属性。
这时候出现了一个疑问,以上两个属性均为Grid.
的形式,但却是不同的属性,因为Grid.ColumnDefinitions
这一属性隶属于Grid
,他完全可以视为Grid
的子标签,而Grid.Column="1"
是在<Rectangle>
内部,用来设置Rectangle
标签的。他并非真正的属性,被转为调用Get.SetColumn() 方法来实现,之后会发现附件属性全部在控件布局之中。
XAML 处理特殊字符和空白
在XAML中,可以让一些特殊字符作为元素的内容出现,例如,之前Button中的Hathaways Flash要变成<Hathaways Flash>
显示,需要修改TextBox标签中的Text="<Hathaways Flash>"
XAML中特殊字符处理方式
特殊字符 | 处理方式 | 特殊字符 | 处理方式 |
---|---|---|---|
< | <; | & | &; |
> | >; | " | &auot; |
在XAML中处理特殊字符并不难,但还存在一个关键问题——空白的处理。
在默认情况下,XAML忽略所有的空白,这就意味着空格、Tab键、硬回车、带有多个空格的长字符串被转换为单个空格。有时放在文本中的空格,希望输出,例如,文本框中输出带尖括号及空格的字符,如< Hathaways Flash >,则需要修改代码如下,
<TextBox Text="< Hathaways Flash >" xml:space="preserve" VerticalAlignment="Center" HorizontalAlignment="Center"></TextBox>
在保留空格的元素(TextBox)中使用" xml:spac= “preserve””, Text属性中的空格被保留输出。事实上" xml:spac= “preserve”"特性是XML标准的一部分。
XAML 名称空间
我们来继续学习并介绍XAML文件根标记中的两个默认XAML名称空间映射及其用意,同时还将说明如何生成类似的映射,便于用在代码中或单独的程序集中定义的元素。
XAML 名称空间的作用
开发语言会将常用功能以类的形式封装,开发人员根据业务需求,也会封装符合自身业务需求的类,有序组织这些类。这样,一方面,便于开发人员准确调用;另一方面,编译器可以有效地识别具有相同命名的类,就引入了名称空间。
名称空间是通过类似树形结构来组织各种类,是一种较为有效的类名排列方式。
XAML和.NET其他语言一样,也是通过名称空间有效地组织XAML内部的相关元素类,但是XAML的名称空间与. NET中的名称空间不是一对一的映射关系 ,而是一对多映射。
XAML 默认名称空间
在之前我们建立的WPF项目的XAML文档结构之中,了解到系统默认的名称空间的代码如下,
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="1300">
XAML名称空间是XML名称空间概念的扩展。xmlns是XML-Namespace的缩写,看起来类似“网址”,但在浏览器中无法打开,所以它并不是网址。它是遵循XAML解析器标准的命名规则,是声明程序集和.NET名称空间的引用。
- 其中,“xmlns=“http://schemas.microsoft.com/winfx/2006/xaml/presentation”"与.NET的名称空间中的System.Windows下的类相对应,包含XAML基本的布局和控件。
- 带x的“xmlns:x=“http://schemas.microsoft.com/winfx/2006/xaml”"对应一些XAML语法和编译相关的CLR名称空间。
- 带d的“xmlns:d="http://schemas.microsoft.com/expression/blend/2008""是让我们在写xaml文件时看到控件的大小。
- 带mc的“xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006""关于程序兼容性的控制
- 带loacl的“xmlns:local=“clr-namespace:WpfApp2””对应本地项目的名称空间
- mc:Ignorable="d"的意思就是告诉编辑器(VS2019)在项目运行时忽略命名空间d设置的大小
为了避免概念上的混淆,在此,对C#、CLR与.NET简要说明一下。在人们做开发时,常会使用C#语言,它是. NET的核心开发语言; . NET(.NET Framework)是一个运行时平台;CLR(Common Language Runtime)是公共语言运行库,与Java虚拟机(JVM)一样是一个运行时环境,它负责资源管理(内存分配和垃圾收集等),确保应用和底层操作系统之间必要的分离。正是因为有了CLR,所以.NET成为一个跨语言的集成开发平台。
XAML 名称空间的标记扩展
标记扩展是XAML的重要特性,它放在一对花括号{}中,使用类似对象标签的方式来精确解析。
现在增加新的设计需求:设置矩形框的颜色为紫色。简单的方法是Fill= “Violet” ,但是我们现在用标记扩展来实现。先设置窗体资源,代码如下。
<!--保留Window代码-->
<Window.Resources>
<SolidColorBrush x:Key="bg" Color="Violet" />
</Window.Resources>
<Grid>
<!--保留后续代码-->
再修改Rectangle
的Fill,代码如下。
<Rectangle x:Name="Rec" Grid.Column="1" Fill ="{StaticResource bg}" Margin="25"></Rectangle>
运行代码后,效果图如下。Fill使用标记扩展来赋值。
XAML 内置 XAML特性名称空间指令及含义
XAML 名称空间指令 | 含 义 | 示 例 |
---|---|---|
x:Array | 创建CLR数组 | < x;ArrayType= “(x:Type Button)”> < Button/> < Button/> </x:Array > |
x:Class | 定义的类名 | < Window x:Class =”MainWindow">… </ Window > |
x:ClassModifier | 定义类型的模式Public、Internal | < Window… x:Class = " MainWindow">… x:ClassModifier = " Pubie"… </ Window > |
x:Code | XAML中内嵌代码 | < x:Code > Public void SomeMethod(){} </x:Code > |
x:Key | 包含在字典中的元素键 | < Window. Resources > < SolidColorBrush x: Key = “bg”…/> </Window. Resources > |
x:Name | 指定元素的编程名 | < Rectangle x:Name= “Rec” …/> |
x:Null | 创建一个空值 | < TextBox Text= "{x:Null} "> |
x:Static | 静态字段和属性值 | < Button background = “{Statie…)”/> |
x:Type | 提供CLR类型 | < ControlTemplate > TargetType= "{x:Type Button} " < /ControlTemplate > |
x:TypeArguments | 为实例化泛型类型指定泛型类型的参数 | < object x: Class = " namespace. classname" x:TypeArguments="{x; Type type1} [,{x: Type type2},{x:Type type3… }]">< /object> |
x:XData | XAML指令元素 | 主要用作 XmlDataProvider 的子对象 或 XmIDataProvider.XmlSerializer属性的子对象 |
XAML 类型转换器
XAML的属性元素赋值形式为。Attribute = Value此处的Value是一个String类型但在后台CS代码中,对象的属性(Property)类型不一定是String类型。为了解决XAML与CS代码中的数据类型不统一,使用TypeConverter类。这个类的功能是:将值类型转换为其他类型,如可以把字符串转换成对象。
- 现在在WpfApp2中新增需求,在CS中创建Teacher类,Teacher中有Name和Student两个属性,其中Name类型是String,Student类型是Teacher;XAML中的Button按下后弹出一个消息框,消息框中的显示内容为“I AM THE FLASH”。其中,消息框调用方式为Teacher.Student.Name。
首先是在后台CS代码中创建Teacher类,
变更XAML中的代码如下:
<!--保留Window代码-->
<Window.Resources>
<local:Teacher x:Key="teacher" Student="I AM THE FLASH"/>
<SolidColorBrush x:Key="bg" Color="Violet" />
</Window.Resources>
<!--保留Window代码-->
<Button Name="Btn" Width="600" Height="400" Click="Btn_Click">
<!--保留Window代码-->
在给Button加入Click
后在MainWindow.xaml.cs之中的Btn_Click
方法中编辑事件代码
private void Btn_Click(object sender, RoutedEventArgs e)
{
Teacher t = (Teacher)this.FindResource("teacher");
MessageBox.Show(t.Student.Name);
}
这时候运行代码点击按钮报错
错误XDG0028 “Teacher”的 TypeConverter 不支持从字符串进行转换。
而这个问题的解决方案是通过TypeConverter派生用户自定义类,重载该类的ConvertForm方法。该方法中有一个Value,他是在XAML中进行设置的,再把这个Value转成用户期望的数据类型。下面创建一个将String转换成Teacher的用户自定义类,将这个类命名为StringToTeacherTypeConverter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel; // ADD new NameSpace
namespace WpfApp2
{
class StringToTeacherTypeConverter:TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
Teacher t = new Teacher();
t.Name = value as string;
return t;
}
return base.ConvertFrom(context, culture, value);
}
}
}
之后修改Teacher.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
namespace WpfApp2
{
[TypeConverterAttribute(typeof(StringToTeacherTypeConverter))]
public class Teacher
{
public string Name{ get; set; }
public Teacher Student { get; set; }
}
}
现在运行代码,单击Button,弹出消息框,效果图如下
此处可以把TypeConverterAttribute中的Attribute省去,因为特征类在使用时可省略Attribute。
XAML 导入程序集
在项目开发时,一个项目由多个模块构成,模块之间可以相互调用。在用VisualStudio2019创建的解决方案下,每个项目都可以独立编译,独立编译的结果就得到一个程序集。常见的程序集包括exe(可执行文件)和dII(Dynamic Link Library动态链接库)。这里说的“导入程序集”是指dll类型。程序集要放在合适的名称空间下,名称空间解决不同模块类名相同问题。
在XAML中导人程序集。设用户自定义的程序集名为UserLibrary.dlI,它有Comm
和Cont
两个名称空间,在XAML中引用这两个名称空间的语法格式如下。
xmlns:映射名= "clr - namespace:名称空间;assembly=程序集名"
接下来根据语法格式,在程序集UserLibrary.dll中的两个名称空间对应的XAML引用为:
xmlns:cont= "clr - namespace :Comm; assembly = UserLibrary"
xmlns:cont= "clr - namespace :Cont; assembly = UserLibrary"
在XAML文档添加引用后,就可以使用名称空间中的类,语法格式如下。
<映射名:类名>...</映射名:类名>
<comm:类名></comm:类名>
<cont:类名></cont:类名>
这个例子只是简单引入,后面会详细说明。
本章小结
本章节从XAML文档框架开始,让我们认识到UI的平面结构对应着XAML文档的树形结构。XAML是可扩展的应用程序声明式语言,它是基于XML的,所以XAML中的标签、属性、内容语法结构与XML有相似之处。我们所写的案例从Button上放置Image、TextBox的例子开始,通过分层叠加式案例,逐步地讲解XAML的复杂属性、附加属性、xmIns指令和名称空间中的标记扩展,并使用TypeConverter类把字符串转换成对象。还讲解了项目开发时,在XAML导入程序集的语法。倘若在学习中,有些内容不懂,可以跳过,当遇到具体应用后,回过头来阅读,会恍然大悟。对XAML语法知识有所了解以后,便可以用它来创建用户界面了。
相关阅读
上一篇:WPF编程基础入门 ——— 第一章 引言.
下一篇:WPF编程基础入门 ——— 第三章 布局(一)布局原则.
WPF编程基础入门 ——— 目录导航.