XAML语法

1、XAML文档的树形结构

  XAML使用标签来定义UI元素,每个标签对应.NET Framework类库中的一个控件类。通过设置标签的Attribute,不但可以对标签所对应控件对象的Property进行赋值,还可以做一些额外的事件(如声明名称空间、指定类名等)。
UI通过树形逻辑结构来描述UI.类似于XML。

2、XAML中为对象属性赋值的语法

  XAML中为对象属性赋值共有两种语法:

  • 使用字符串进行简单赋值
  • 使用属性元素(Property Element)进行复杂赋值

2-1 使用标签的Attribute为对象属性赋值

  前情纪要,一个标签的Attribute里有一部分与对象的Property互相对应,“<”Rectangle“>”标签的Fill这个Attribute就是这样——它与Rectangle类对象的Fill属性对应。在MSDN文档库里可以查到,Rectangle.Fill的类型是Brush。Brush是一个抽象类,凡是以Brush为基类的类都可作为Fill属性的值。Brush的派生类有很多:

  • SolidColorBrush:单色画笔
  • LinearGradientBrush:线性渐变画刷
  • RadialGradientBrush:径向渐变画刷
  • ImageBrush:位图画刷
  • DrawingBrush:矢量图画刷
  • VisualBrush:可视元素画刷
    需要注意的是,通过这种Attribute=Value语法赋值时,由于XAML的语法限制,Value只可能是一个字符串值。这就引出了两个问题:
  • 如果一个类能使用XAML语言来进行声明,并允许它的Property与XAML标签的Attribute互相映射,那就需要为这些Property准备适当的转换机制。
  • 由于Value是个字符串,所以其格式复杂程序有限,尽管可以在转换机制里包含一定的按格式解析字符串的功能以便转换成较复杂的目标对象,但这会让最终的XAML使用者头疼不已。因为他们不得不在没有编码辅助的情况下手写一个格式复杂的字符串以满足赋值要求。
    第一个问题的解决方案是使用TypeConverter类的派生类,在派生类里重写TypeConverter的一些方法;第二个问题的解决方法就是使用属性元素(Property Element)。

2-2 使用TypeConverter类将XAML标签的Attribute与对象的Property进行映射

2-3 属性元素

  在XAML中,非空标签均有自己的内容(content)。标签的内容指的是夹在起始标签和结束标签之间的一些子级标签,每个子级标签都是父级标签内容的一个元素(Element),简称为父级标签的一个元素。顾名思义,属性元素指的是某个标签的一个元素对应这个标签的一个属性,即以元素的形式来表达一个实例的属性。代码描述为:

<ClassName><ClassName.PropertyName><!--以对象形式为属性赋值--></ClassName.PropertyName>
</ClassName>

  这样,在这个标签内部就可以适用对象(而不是局限于简单的字符串)进行赋值。
如果把上面的例子用属性标签式语法改写一下,XAML代码是这样:

    <Grid VerticalAlignment="Center" HorizontalAlignment="Center"><Rectangle x:Name="rectangle" Width="200" Height="120"><Rectangle.Fill><SolidColorBrush Color="Blue"/></Rectangle.Fill></Rectangle></Grid>

  效果与先前代码一致。所以,对于简单赋值而言,属性元素语法并没有什么优势,反而让代码看起来有点冗长。但是,遇到属性是复杂啊啊对象时,这种语法的优势就体现出来了。如使用线性渐变画刷来填充这个矩形。

        Title="MainWindow" Height="450" Width="800"><Grid VerticalAlignment="Center" HorizontalAlignment="Center"><Rectangle x:Name="rectangle" Width="200" Height="120"><Rectangle.Fill><LinearGradientBrush><LinearGradientBrush.StartPoint><Point X="0" Y="0"/></LinearGradientBrush.StartPoint><LinearGradientBrush.EndPoint><Point X="1" Y="1"/></LinearGradientBrush.EndPoint><LinearGradientBrush.GradientStops><GradientStopCollection><GradientStop Offset="0.2" Color="LightBlue"/><GradientStop Offset="0.7" Color="Blue"/><GradientStop Offset="1.0" Color="DarkBlue"/></GradientStopCollection></LinearGradientBrush.GradientStops></LinearGradientBrush></Rectangle.Fill></Rectangle></Grid>

  LinearGradientBrush的GradientStops属性是一个GradientStop对象的集合(GradientStopCollection),即一系列的矢量渐变填充点。在这些填充点之间,系统会自动进行插值运算、计算出过渡色彩。填充矢量的方向是StartPoint和EndPoint两个属性(类型为Point)的连线方向,矩形的左上角为(0,0),右下角为(1,1)。这段代码中,针对这三个属性都使用了属性标签式的赋值方法。

2-4 标记扩展(Markup Extensions)

  当需要为对象的属性进行这些特殊类型赋值时就需要使用标记扩展了。
  所谓的标记扩展,实际上是一种特殊的Attribute=Value语法,其特殊的地方在于Value字符串是由一对花括号及其括起来的内容组成,XAML编译器会对这样的内容做出解析、生成相应的对象。

<StackPanel Background="LightSlateGray"><TextBox Text="{Binding ElementName=slider1,Path=Value,Mode=OneWay}" Margin="5"/><Slider x:Name="slider1" Margin="5"/>
</StackPanel>

  其中,Text="{Binding ElementName=slider1,Path=Value,Mode=OneWay}"这句就是标记扩展了。
  分析一下这句代码:

  • 当编译器看到这句代码时就会把花括号里的内容解析成相应的对象。

  • 对象的数据类型名是紧邻左花括号里的字符串

  • 对象的属性由一串以逗号连接的子字符串负责初始化(注意,属性值不再加引号)。
    尽管标记扩展的语法简洁方便,但并不是所有对象都能用标记扩展的语法来写,只有MarkupExtension类的派生类(直接或者间接均可)才能使用标记扩展语法来创建对象。MarkupExtension的直接派生类不多,它们是:

  • System.Windows.ColorConvertedBitmapExtension

  • System.Windows.Data.BindingBase

  • System.Windows.Data.RelativeSource

  • System.Windows.DynamicResourceExtension

  • System.Windows.Markup.ArrayExtension

  • System.Windows.Markup.NullExtension

  • System.Windows.Markup.StaticExtension

  • System.Windows.Markup.TypeExtension

  • System.Windows.ResourceKey

  • System.Windows.StaticResourceExtension

  • System.Windows.TemplateBindingExtension

  • System.Windows.ThemeDictionaryExtension
    最后,使用标记扩展时还需要注意以下几点:

  • 标记扩展是可以嵌套的,例如“Text={Binding Source={StaticResource myDateSource},Path=PersonName}”是正确的语法。

  • 标记扩展具有一些简写语法,例如“{Binding Value, …}”与“{Binding Path = Value, …}”是等价的,“{StaticResource myString,…}”与“{StaticResource ResourceKey = myString, …}”是等价的。两种写法中,前者称为固定位置参数(Positional Parameter),后者称为具名参数(Named Parameters)。固定位置参数实际上就是标记扩展类构造器的参数,其位置由构造器参数列表决定。

  • 标记扩展类的类名均以单词Extension为后缀,在XAML使用它们的时候Extension后缀可以省略不写,比如Text="{x:Static …}“与写Text=”{x:StaticExtension …}"是等价的。

3、事件处理器与代码后置

  在.NET事件处理机制中,可以为对象的某一个事件指定一个能与该事件匹配的成员函数,当这个事件发生时,.NET允许时会去调用这个函数,即表示对这个事件的响应和处理。因此,我们把这个函数称为“事件处理器”(Event Handler)。WPF支持在XAML里为对象的事件指定事件处理器,方法是使用事件处理器的函数名为对应对象事件的Attribute进行赋值:

<ClassName EventName="EventHandlerName"/>

  当我们为一个XAML标签的事件性Attribute进行赋值时,XAML编辑器会自动为我们生成相应的事件处理器。事件处理器是使用C#语言编写的函数。以“<”Button“>”标签为例,当为Click赋值时,VS会有提示,按下Enter键,VS会自动生成一个事件处理器,并把它的名字(函数名)赋值给Click。如下:

<Button x:Name="button1" Click="button1_Click"/>

  我们知道,C#语言编写的代码应该用于处理程序的逻辑,需要让它与表示UI的XAML代码分开。这些C#函数会放在哪里呢?由于C#支持partial类,XAML标签又可以使用x:Class特征指定将由XAML代码解析生成的类与哪个类合并,因此,我们完全可以把用于实现程序逻辑的C#代码放在一个文件里,把用于描述程序UI的XAML代码放在另一个文件里,并且让事件性Attribute充当XAML与C#之间沟通的纽带——设计师用XAML为程序创建UI并且展现给客户;程序员用C#编写逻辑、从后台支持前面的UI——这种将逻辑代码与UI代码分离、隐藏在UI代码后面的形式叫作“代码后置”(Code-Behind)。

注:
  之所以能实现代码后置功能,是因为.NET支持partial类并能解析XAML所生成的代码与x:Class所指定的类进行合并。有两点需要注意的是:

  • 不只是事件处理器,一切用于实现程序逻辑的代码都要放在后置的C#文件中。
  • 默认情况下,VS为每一个XAML文件生成的后置代码文件名为“XAML文件全名.cs”,比如XAML文件名为MyWindow.xaml,那么它的后置代码文件名为MyWindow.xaml.cs。这样做是为了方便管理文件,但并不是必须的,只要XAML解析器能找到 x:Class 所指定的类,无论你的文件叫什么名字都可以。

  介绍一个有意思的标签——x:Code,使用它可以把本来应该呆在后置代码里的C#代码搬到XAML文件里来。x:Code的内容一定要使用XML语言的<![CDATA[…]>转义标签。

   <x:Code><![CDATA[private void button1_Click(object sender,RoutedEventArgs e){MessageBox.Show("Bye! Code-Behind!");}]]></x:Code>

4、导入程序集和引用其中的名称空间

  .NET的模块称为程序集(Assembly)。一般情况下,是引用VS创建的是解决方案(Solution)。一个解决方案就是一个完整的程序。解决方案中会包含若干个项目(Project),每个项目是可以独立编译的,它的编译结果是一个程序集。常见的程序集是以.exe为扩展名的可执行程序或者是以.dll为扩展名的动态链接库,大多数情况下,我们说“引用其他程序集”的时候,说的都是动态链接库。因为.NET编程接口(API)以类和类级别的单元为主(Win32API是以函数为主),所以我们又常把引用程序集说成是引用类库。
  名称空间的作用是避免同名类的冲突。

注意:想在自己的程序里引用类库,需要分三步来做:

  1. 编写类库项目并编译得到.dll文件或者获得别人编译的.dll文件。
  2. 将类库项目或者.dll文件引用进自己的项目。
  3. 在C#和XAML中引用类库中的名称空间。

  一旦将一个类库引用进程序,就可以引用其中的名称空间。假设我的类库程序集名为MyLibrary.dll,其中包含Common和Controls两个名称空间,而且已经把这个程序集引用进WPF项目,那么在XAML中引用这两个名称空间的语法是:

xmlns:映射名="clr-namespace:类库中名称空间的名字;assembly=类库文件名"

  对于MyLibrary.dll里的两个名称空间,XAML中的引用会是:

xmlns:common="clr-namespace:Common;assembly=MyLibrary"
xmlns:controls="clr-namesapce:Controls;assembly=MyLibrary"

分析一下XAML引用名称空间的语法:

  • xmlns是用于在XAML中声明名称空间的Attribute,它从XML语言继承而来,是XML Namespace的缩写。
  • 冒号后的映射名是可选的,但由于可以不加映射名的默认名称空间已经被WPF的主要名称空间占用,所以所引用的名称空间都需要加上这个映射名。映射名可以根据喜好自由选择,但团队内部最好使用一致的命名。一个建议就是使用类库中名称空间的原名或者缩写。
  • 引号中的字符串值确定了你要引用的是哪个类库以及类库中的哪个名称空间。XAML编辑器可以帮我们自动填充字符串的写法。

  一旦我们将类库中的名称空间引用XAML文档,我们就可以使用这些名称空间里的类。语法格式是:

<映射名:类名>...</映射名:类名>

例如使用Common和Controls中的类,代码是这样的:

<common:MessagePanel x:Name="window1"/>
<controls:LedButton x:Name="button1"/>

  XAML中引用名称空间的语法与C#不太一样。最大的差别就是XAML需要为被引用的名称空间添加一个映射名,用这个映射名来代表被引用的名称空间。其实C#也可以这样引用名称空间,只是不经常用。

WPF学习日记(二)——XAML语法相关推荐

  1. vue3学习日记二 setup语法糖

    1.基本语法 <script setup> console.log('hello script setup') </script> 2.顶层的绑定会暴露给模板,即在顶层声明的变 ...

  2. 学习Kotlin(二)基本语法

    推荐阅读: 学习Kotlin(一)为什么使用Kotlin 学习Kotlin(二)基本语法 学习Kotlin(三)类和接口 学习Kotlin(四)对象与泛型 学习Kotlin(五)函数与Lambda表达 ...

  3. wpf学习笔记二 深入学习 xaml

    1.XAML 主要用于绘制UI界面,最大的优点是能使UI与运行逻辑分离开来,使得整个程序回到逻辑处理上来. 每一个标签对应.NET Framework类库的一个控件类.通过设置标签的Attribute ...

  4. wpf学习笔记---初识xaml标签语言

    最近下载了windows sdk,也来凑下热闹学学新技术.顺便也简单的记录下学习过程.此非教程.由于刚接触,一切皆以实用为主.先了解其特性为好. 一.xaml的结构为xml形式组成,与flex中的标签 ...

  5. WPF学习笔记5: Xaml之Markup Extensions

    Markup Extensions 与TypeConverter 差不多,允许扩展Xaml表达式,把Xaml中的文本转换成相应的对象/对象程序. 在上述例子中,x:Null, x:Static, Bi ...

  6. WPF学习笔记:XAML入门

    1.什么是XAML XAML是WPF技术中专门用于设计UI的语言,它在桌面开发及富媒体网络程序的开发中扮演了HTML+CSS+JAVASCRIPT的角色,成为设计师和程序员之间沟通的桥梁.它帮助开发团 ...

  7. WPF学习日记(一)——初步了解什么是WPF

    WPF简介 what is WPF   WPF( Windows Presentation Foundation)的功能是用来编写应用程序的表示层. XAML是WPF技术中专门用于设计UI的声明型语言 ...

  8. Vue2.0学习笔记二 基础语法

    1. Mustache语法 Mustache语法也叫插值表达式,Mustache语法式通过{{}}渲染到页面,并且数据是响应式的. 数据的响应式:数据的变化导致页面的内容随之变化 效果图: 2. 指令 ...

  9. Python学习日记(二十八) hashlib模块、configparse模块、logging模块

    hashlib模块 主要提供字符加密算法功能,如md5.sha1.sha224.sha512.sha384等,这里的加密算法称为摘要算法.什么是摘要算法?它又称为哈希算法.散列算法,它通过一个函数把任 ...

最新文章

  1. 自动解析复杂类的属性 实现归档或者进行序列化 反序列话的时候为每一个属性添加序列化方法的繁琐...
  2. Thinkphp 发送邮件
  3. Java PipedInputStream available()方法与示例
  4. AttributeError: module 'pymysql' has no attribute 'escape' 错误的出现以及解决
  5. Xcode字体新宠 Monoid
  6. python3内存分析_调试和分析 - tracemalloc —- 跟踪内存分配 - 《Python 3.7 标准库》 - 书栈网 · BookStack...
  7. 能打开2D、3D图文件的小工具abviewer
  8. [视频发布] 掘金 Podcast 报名中,摩拜单车、美团点评团队分享 Vue 最佳实践
  9. 【粤教版必修二《信息系统与社会》】知识总结与题目分析
  10. Postman 是一个接口测试和 http 请求的神器,非常好用。
  11. PTX JIT compilation failed相关问题
  12. 详解IP分片与TCP分段的区别
  13. 关于手机端点击HTML input输入框页面放大的问题解决放法
  14. android接入即时IM(接入亲加通信云)
  15. 日语:假定形、可能形、被动形、使役形、使役被动形
  16. 阿里数据中台七年演化史——行在口述干货
  17. 用条码打印软件批量打印图片
  18. malloc lab
  19. Java之支付宝支付(电脑网站支付)沙箱测试版
  20. mysql安装后账号密码_mysql安装好后设置账号和密码

热门文章

  1. Fluke ADPT连接器给福禄克万用表插上翅膀----电容测量
  2. 搭建达梦DSC(两节点)
  3. GPS AGPS原理
  4. CAD图纸如何提取标注的方法
  5. 【论文模型讲解】Two-Stream Convolutional Networks for Action Recognition in Videos
  6. 《基于Unity与SteamVR构建虚拟世界》(Yanlz+Unity+XR+SteamVR+LeapMotion+Neuron+Kinect+IMU+Kickstarter+立钻哥哥++ok++)
  7. RK3568-签批一体机主板方案
  8. 基础理论知识复习(下)
  9. html中文字处理美化的效果,PPT文字处理 PPT图片美化 PPT页面的布局-泡泡糖办公...
  10. Python-Django毕业设计安卓基于移动群智感知城市轨道交通激励APP(程序+LW)