《深入浅出WPF》——XAML语法及基础知识
创始人
2024-06-02 11:39:18
0

文章目录

  • 一、 剖析一段简单的XAML代码
  • 二、 XAML中为对象属性赋值的语法
    • 2.1. 使用标签的Attribute为对象进行赋值
    • 🔺2.2. 使用TypeConverter类将XAML标签的Attribute与对象的Property进行映射
    • 2.3. 属性元素
    • ⭐2.4. 标记扩展
  • 三、 事件处理器与代码后置
  • ⭐四、导入程序集和引用其中的名称空间
  • 五、XAML的注释
  • 六、小结


一、 剖析一段简单的XAML代码

下面是一段简单的xaml代码:



这一大段代码,其中还有两个看着像主页地址的东西……它们都是些什么呢?让我们来一个一个地分析。

XAML是一种由XML派生而来的语言,所以很多XML中的概念在XAML是通用的。比如,使用标签声明一个元素(每个元素对应内存中的一个对象)时,需要使用起始标签和终止标签,夹在起始标签和终止标签中的XAML代码表示隶属于这个标签的内容。如果没有什么内容,则这个标签为空标签,可以写为

为了表示同类标签中的某个标签与众不同,可以给它的特征/特性(Attribute)赋值。为特征赋值的语法如下:

  • 非空标签 Content
  • 空标签:

在这里,有必要把Attribute和Property这两个词仔细地辨别一下。

这两个词的混淆由来已久。混淆的主要原因就是大多数中文译本里既把Attribute译为“属性”,也把Property译为“属性”。其实这两个词表达的不是一个层面上的东西。
在这里插入图片描述
在这里插入图片描述

不过这种专业词汇看翻译软件的翻译意思往往意义不大,应该不太会有人看着英文专业文档,然后把百度翻译来的意思直接代入吧,虽然在大部分专业无关词汇的情况下,往往没问题。但一旦涉及某些小众的含义,就会出现混淆、甚至难以理解的现象。这个时候,不将其翻译往往会好一些,翻译成中文本来就会发生语义的转变。这时的Attribute就是Attribute啊,哪是什么属性。

言归正传,Property属于面向对象理论范畴。在使用面向对象思想编程时,常常需要对客观事物进行抽象,再把抽象出来的结果封装成类,类中用来表示事物状态的成员就是Property。比如要写一个模拟赛车的游戏,那么必不可少的就是对现实汽车的抽象。现实中的汽车身上会带有很多数据,但在游戏中可能只关心它的长度、宽度、高度、重量、速度等有限的几个数据,同时,还会把汽车“加速”、“减速”等一些行为也提取出来并用算法模拟,这个过程就是抽象(结果就是Car这个类)。显然,Car.Length、Car.Height、Car.Speed等表达的是汽车当前处在一个什么状态,而Car.Accelerate()、Car.Break()表达的是汽车能做什么。因此,Car.Length、Car.Height、Car.Speed就是Property的典型代表,将Property译为“属性”也很贴切。
在这里插入图片描述
总结一句话就是:Property(属性)是针对对象而言的,是对象的外在表现。

Attribute则是编程语言文法层面的东西。比如有两个同类的语法元素A和B,为了表示A与B不完全相同或者A与B在用法上有些区别,这时就要针对A和B加一些Attribute。也就是说,Attribute只与语言层面上的东西相关,与抽象出来的对象没什么关系。因为Attribute是为了表示“区分”的,所以这里把它翻译成“特征”。C#中的Attribute就是这种应用的典型例子,我们可以为一个类添加Attribute,这个类的类成员中有许多Property。显然Attribute只是用来影响类在程序中的用法,而Property则对应着抽象对象身上的性状,它们根本不是一个层面的东西。

实话讲,Attribute还是很难理解。
或许是因为我编译原理没有学好,一开始就被文法、语法给弄晕了。
不过从应用上区分它们并不难。
目前只知道,Property是针对面向对象而言的,是对象的外在体现,翻译成属性更佳。
Attribute是语言层面的,翻译成特征更佳。

习惯上,英文中把标签式语言中表示一个标签特征的“名称-值”对称作Attribute。如果恰好又是用一种标签语言在进行面向对象编程,这时两个概念就有可能混淆在一起了。实际上,使用能够进行面向对象编程的标签式语言只是把标签与对象做了一个映射,同时把标签的Attribute与对象的Property也做了一个映射——针对标签还是叫Attribute,针对对象还是叫Property,仍然不是一个层面上的东西。而且,标签的Attribute与对象的Property也不是完全映射的,往往是一个标签所具有的Attribute多于它所代表的对象的Property。

因为XAML是用来在UI上绘制控件的,而控件本身就是面向对象抽象的产物 (这句话挺有意思的),所以XAML标签的Attribute里就有一大部分是与控件对象的Property互相对应的。当然,这还意味着XAML标签还有一些Attribute并不对应控件对象的Property。

XAML是一种“声明”式语言,当你见到一个标签,就意味着声明了一个对象,对象之间的层级关系要么是并列、要么是包含,全都体现在标签的关系上。

下面这些代码都是标签的Attribute。

x:Class="DManager.View.LedgerView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Height="700"Title="Window1"

其中,Height、Title一看就知道是与Window对象的Property相对应的。中间两行(即两个xmlns)是在声明名称空间。最上面一行是在使用名为Class的Attribute,这个Attribute来自x:前缀对应的名称空间。下面仔细解释。

前面说过,XAML语言是从XML语言派生出来的。XML语言有一个功能就是可以在XML文档的标签上使用xmlns特征来定义名称空间/命名空间(Namespace),xmlns也就是XML-Namespace的缩写了。定义命名空间的好处就是,当来源不同的类重名时,可也使用名称空间加以区分。xmlns特征的语法格式如下:

xmlns[:可选的映射前缀]="名称空间"

xmlns后可以跟一个可选的映射前缀,之间用冒号分隔。如果没有写可选的映射前缀,那就意味着所有来自于这个名称空间的标签前都不用加前缀,这个没有映射前缀的名称空间称为“默认名称空间”——默认名称空间只能有一个,而且应该选择其中元素被最频繁使用的名称空间来充当默认名称空间。文章开头的代码中,都来自由第二行声明的默认名称空间。而第一行中的Class特征则来自于第三行中x:前缀对应的名称空间。这里可以做一个小实验:如果给第二行声明的名称空间加上一个前缀,比如n,那么代码就必须改成这样才能编译通过:



XAML中引用外来程序集和其中.NET名称空间的语法与C#是不一样的。在C#中,如果想使用System.Windows.Controls名称空间里的Button类,需要先把包含System.Windows.Controls名称空间的程序集PresentationFramework.dll通过添加引用的方式引用到项目中,然后再在C#代码的顶部写上一句using System.Windows.Controls;。在XAML中做同样的事情也需要先添加对程序集的引用,然后再在根元素的起始标签中写上一句:xmlns:c="clr-namespace:System.Windows.Controls;assembly=PresentationFramework"。c是映射前缀,换成其他的字符串(如control)也可以。因为Button来自前缀c对应的名称空间,所以在使用Button的时候就要写成...

xmlns:c="clr-namespace:System.Windows.Controls;assembly=PresentationFramework",这么长的一串字符看上去的确有点恐怖,但不用担心,VS是有自动提示功能的。

在VS自动提示的顶部,你会看到几个看上去像网页地址的名称空间,其中就包含例子代码中的那两行。为什么名称空间看上去像是一个主页地址呢?其实就算把它拷贝到浏览器的地址栏里尝试跳转页不会打开网页。这里只是XAML解析器的一个硬性编码(hard-coding),只要见到这些固定的字符串,就会把一系列必要的程序集(Assembly)和程序集中包含的.NET名称空间引用进来(即没有为什么,我就是预先定义了这串地址的含义,看到它就把它解析为对应的含义)。

默认引用进来的两个名称空间格外重要,它们对应的程序集和.NET名称空间如下:
http://schemas.microsoft.com/winfx/2006/xaml/presentation

  • System.Windows
  • System.Windows.Automation
  • System.Windows.Controls
  • System.Windows.Controls.Primitives
  • System.Windows.Data
  • System.Windows.Documents
  • System.Windows.Forms.Integration
  • System.Windows.Ink
  • System.Windows.Input
  • System.Windows.Media
  • System.Windows.Media.Animation
  • System.Windows.Media.Effects
  • System.Windows.Media.Imaging
  • System.Windows.Media.Media3D
  • System.Windows.Media.TextFormatting
  • System.Windows.Navigation
  • System.Windows.Shapes

也就是说,你在XAML代码中可以直接使用这些CLR名称空间中的类型(因为默认XML名称空间没有前缀)。从这些名称空间的名字也可以看出,大多是显示相关的,与硬编码字串中的presentation相符。

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"则对应一些与XAML语法和编译相关的CLR名称空间。使用这些名称空间中的类型时需要加 x 前缀,因为它们被映射到了名 x 的XML名称空间中。

从这两个名称空间的名字和它们所对应的.NET程序集上,我们不难看出,第一个名称空间对应的是与绘制UI相关的程序集,是表示(Presentation)层面上的东西;第二个名称空间则对应XAML语言解析处理相关的程序集,是语言层面上的东西。

还剩下 x:Class="DManager.View.LedgerView"这个Attribute。x:前缀说明这个Attribute来自于x映射的名称空间——前面刚分析过,这个名称空间是对应XAML解析功能的。x:Class,顾名思义它与类有些关系,是何种关系呢?我们来做个实验:

首先,把x:Class="DManager.View.LedgerView"这个Attribue删掉,再到 LedgerView.xaml.cs 文件里把构造器中对 InitializeComponent 方法的调用也删掉。编译程序,你会发现程序仍然可以运行。为什么呢?打开App.xaml这个文件,你能发现这样一个Attribute——StartupUri=“LedgerView.xaml”,它是在告诉编译器把由LedgerView.xaml解析后生成的窗体作为程序启动时的主窗体。也就是说,只要LedgerView.xaml文件能够被正确解析成一个窗体,程序就可以正常运行。

然后只恢复x:Class这个Attribute(不恢复对InitializeComponent方法的调用),并更改它的值为为 x:Class="DManager.WindowABC"。编译之,仍然可以正确运行。这时,使用IL Disassembler(中间语言反编译器)打开项目的编译结果,你会发现在由项目编译生成的程序集里包含一个名为WindowABC的类。

这说明,x:Class 这个Attribute的作用是当XAML解析器将包含它的标签解析成C#类后,决定该类的类名是什么。这里,已经触及到XAML的本质。前面已经看到,示例代码的结构就是使用XAML语言直观地告诉我们,当前被设计的窗体是在一个里嵌套一个。如果是使用C#来完成同样的设计呢?显然,我们不可能去更改Window这个类,我们能做的是从Window类派生一个类(比如叫WindowABC),再为这个类添加一个Grid类型的字段,然后把这个字段在初始化时赋值给派生类的内容属性(Content)。代码看起来大概是这样:

using System.Windows;
using System.Windows.Controls;class WindowABC : Window
{private Grid grid;public WindowABC(){grid = new Grid();this.Content = grid;}
}

最后,回到最初的代码。你可能会问:在XAML里有x:Class="DManager.View.LedgerView",在LedgerView.xaml.cs里也声明了LedgerView这个类,难道它们不会冲突吗?仔细看看LedgerView.xaml.cs中LedgerView类的声明就知道了——在声明时使用了partial这个关键字。使用partial关键字,可以把一个类分拆在多处定义,只要各部分代码不冲突即可。显然,由XAML解析器生成的LedgerView类在声明时也使用了partial关键字,这样由XAML解析成的类和C#文件里定义的部分就合二为一了。正是由于这种partial机制,我们可以把类的逻辑代码留在.cs文件里,用C#语言来实现,而把那些与声明及布局UI元素相关的代码分离出去,实现UI与逻辑分离。并且,用于绘制UI的代码(如声明控件类型的字段、设置它们的外观和布局等)也不必再使用C#语言,使用XAML和XAML编辑工具就能轻松搞定。

至此,对文章开头处的简单XAML程序应该了然于胸了。

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

XAML是一种声明性语言,XAML编译器会为每个标签创建一个与之对应的对象

声明性语言
声明式编程是一种编程范式,即构建计算机程序的结构和元素的一种风格,它表达了计算的逻辑而没有描述其控制流程。

对象创建出来后要对它的属性进行必要的初始化才有使用意义。因为XAML语言不能编写程序的运行逻辑,所以一份XAML文档中除了使用标签声明对象就是初始化对象的属性了。

XAML中为对象赋值共有两种方法:

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

我们以一个标签的Fill为例来介绍这两种方法。

2.1. 使用标签的Attribute为对象进行赋值

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

  • SolidColorBrush:单色画刷
  • LinearGradientBrush:线性渐变画刷
  • RadialGradientBrush:径向渐变画刷
  • ImageBrush:位图画刷
  • DrawingBrush:矢量图画刷
  • VisualBrush:可视画刷

下面例子中使用单色画刷和线性渐变画刷两种。

我们先学习使用字符串对Attribute进行简单赋值。假设我们的Rectangle只需要填充成单一的蓝色,那么只需要简单地写成:



运行效果如下,
在这里插入图片描述
可以看到,Blue这个字符串最终被翻译成了一个SolidColorBrush对象并赋值给了rectangle对象。换成C#代码是这样的:

// ...
SolidColorBrush sBrush = new SolidColorBrush();
sBrush.Color = Colors.Blue;
this.rectangle.Fill = sBrush;
// ...

需要注意的是,通过这种Attribute=Value语法赋值时,由于XAML的语法限制,Value只可能是一个字符串值。这就引出了两个问题:

  • 如果一个类能使用XAML语言进行声明,并允许它的Property与XAML标签的Attribute互相映射,那就需要为这些Property准备适当的转换机制。
  • 由于Value是个字符串,所以其格式复杂程度有限,尽管可以在转换机制里包含一定的按格式解析字符串的功能以便转换成较复杂的目标对象,但这会让最终的XAML使用者头疼不已。因为他们不得不在没有编码辅助的情况下写一个格式复杂的字符串以满足赋值要求。

第一个问题的解决方案是使用TypeConverter类的派生类,在派生类里重写TypeConverter的一些方法,第二个问题的解决方案是使用属性元素(Property Element)。

题外话


使用WPF时,我有时会想在Attribute中能写下简单的计算逻辑,比如:Height=“100+1”、IsEnable="!true"等
但是通过上面内容可知,由于它是字符串赋值,特征内的值就是一串字符串,所以要达到目标效果显然得解析字符串,那么各种计算逻辑也是先通过字符串解析成表达式再运算的,而XAML自带的解析器并没有提供这样直接解析的功能(虽然可以通过converter的方式)。因此有时想实现一些简单计算,还是挺麻烦的,这是XAML的一个我认为不太方便的点。

🔺2.2. 使用TypeConverter类将XAML标签的Attribute与对象的Property进行映射

首先,我们准备一个类:

public class Human
{public string Name {get; set;}public Human Child {get; set;}
}

这个类有两个属性:

  • string类型的Name。
  • Human类型的Child。

现在我的期望是,如果在XAML里这样写:



则能够为Human实例的Child属性赋一个Human类型的值,并且Child.Name就是这个字符串的值。

我们先看看直接写行不行。在UI上添加一个按钮button1,并且在它的Click事件处理器里写上:

private void button1_Click(object sender, RoutedEventArgs e)
{Human h = (Human)this.FindResource("human");MessageBox.Show(h.Child.Name);
}

编译没有问题,但在单击按钮之后程序抛出异常:
在这里插入图片描述
为什么会出现上述异常呢?

原因很简单,Human的Child属性是Human类型,而XAML代码中的ABC是个字符串,编译器不知道如何把一个字符串实例转换成一个Human实例。
在这里插入图片描述
那我们应该怎么做呢?
办法是使用TypeConverter和TypeConverterAttribute这两个类。

首先,我们要从TypeConverter类派生出自己的类,并重写它的一个ConvertFrom方法。这个方法有一个参数名为value,这个值就是在XAML文档里为它设置的值,我们要做的就是把这个值“翻译”成合适类型的值赋给对象的属性:

public class StringToHumanTypeConverter : TypeConverter
{public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value){if (value is string){Human h = new Human();h.Name = value as string;return h;}return base.ConvertFrom(context, culture, value);}
}

有了这个类还不够,还要使用TypeConverterAttribute这个特征类把StringToHumanTypeConverter这个类“粘贴”到作为目标的Human类上。

[TypeConverterAttribute(typeof(StringToHumanTypeConverter))]
public class Human{public string Name { get; set; }public Human Child { get; set; }}

因为特征类在使用的时候可以省略Attribute这个词,所以也可以写成:

[TypeConverter(typeof(StringToHumanTypeConverter))]

但这样写,需要注意写在方括号里的是TypeConverterAttribute而不是TypeConverter。

完成之后,再次单击按钮,我们想要的结果就出来了。
在这里插入图片描述

注意
TypeConverter类的使用远远不是只重载一个ConverterFrom方法那么简单。为了配合这个方法的运行,还需要重载其他几个方法。实际开发中,经常会用到一些封装过的Converter类,定义后在XAML的Resources中实例化,在绑定时使用。

2.3. 属性元素

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



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



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


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

上面代码为了突出属性元素语法,我将所有属性都展开成属性元素,结果是代码的可读性一落千丈。经过优化,代码变成这样:



注意


其实上面所谓优化了的代码,也就是去掉StartPoint、EndPoint的元素(因为它们的默认值分别是(0,0)、(1,1))。
一般来讲,在保证了基本可读性的情况下,代码还是精简一点比较好。二话不说,上来一大段代码,谁看了都犯迷糊。
这边有几个简化XAML的技巧:

  • 能使用Attribute=Value形式赋值的就不使用属性元素。
  • 充分使用默认值,取出冗余:StartPoint=“0, 0” EndPoint="1,1"是默认值,可以省略。
  • 充分利用XAML的简写方式:XAML的简写方式有很多,需要在实际应用中慢慢积累(其实这是熟练度问题了)。如本例,LinearGradientBrush.GradientStops的数据类型是GradientStopCollection,如果严格按照语法来写,这个属性元素的内容应该是一个标签,实际上,XAML允许你省略这个标签而把集合元素直接写在属性元素的内容里,控件的“内容属性”也有类似简写。

最后是一个小例子来结束这一小节:



这是一个径向渐变画刷的例子,效果如下:
在这里插入图片描述
千万不要以为这是在VS里一行一行敲出来的XAML——这段代码的大部分内容是用Blend通过绘图的形式自动生成的。由Blend生成的代码里会包含一些冗余的细节。常见的细节包括:

  • 值过于精确:比如0.79103108323这样的值,一般可以简化为0.8以提高可读性。
  • 默认值被显式地写出:比如为StackPanel显式地写出Orientation=“Vertical”,一般删掉即可。
  • 专门用于Blend绘图的标记:比如用于锁定图形的标记。根据实际需要决定保留还是删除。

一般情况下,对于复杂的绘图和动画创作,应该现在Blend里进行操作,然后回到VS里进行微调,在保证不影响效果的情况下尽可能地提高代码的可读性和可维护性。

Blend for Visual Studio工具,操作界面如下,和VS差不多,但是在编辑界面上可以图形化操作,拖拽一些图形,下方的代码框中会生成相应的代码,也算是一个生产力工具吧。
在这里插入图片描述

⭐2.4. 标记扩展

仔细观察XAML中为对象属性赋值的语法,你会发现大多数赋值都是为属性生成一个新对象。但有时候需要把同一个对象赋值给两个对象的属性,还有时需要给对象的属性赋一个null值,WPF甚至允许将一个对象的属性值依赖在其他对象的某个属性上。当需要为对象的属性进行这些特殊类型的赋值时就需要使用标记扩展了。

注意


所谓标记扩展,实际上是一种特殊的Attribute=Value语法,其特殊的地方在于Value字符串是由一对花括号及其括起来的内容组成,XAML编译器会对这样的内容做出解析,生成相应的对象。

因为本节内容重在讲述XAML的语法,所以不必过分追究下面代码的编程细节,只需要关注标记扩展的语法即可。在下面代码中,将使用Binding类的实例将TextBox的Text属性依赖在Slider的Value上,这样,当Slider滑块滑动时TextBox就会显示Slider当前的值。



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

  • 当编译器看到这句代码时就会把花括号里的内容解析成相应的对象。
  • 对象的数据类型名是紧邻左花括号的字符串。
  • 对象的属性由一串以逗号连接的子字符串负责初始化(注意,属性值不再加引号)。

初学者常常会认为这个语法比较难记,其实这个语法与C# 3.0中的对象初始化语法非常接近。如果使用C# 3.0的语法来创建一个Binding类的实例,最佳的语法应该是:

Binding binding = new Binding() { Source = slider1, Mode = BindingMode.OneWay };

C# 3.0中对象初始化器也是这样,使用一对花括号包围一组由逗号分隔的子字符串,这些子字符串用来初始化对象的属性。只是XAML的标签扩展把对象的数据类型也搬到括号里面来了。

标记扩展也会是对属性的赋值,所以完全可以使用属性标签的形式来替换标记扩展,只是简洁性使然没人这么做罢了。下面是使用属性标签替换标记扩展后的代码:



这样写的弊端是使代码量增加、阅读不便,但也有一个好处:老版本的VS(2008)没有对标记扩展提供智能语法提示,而使用属性标签是支持智能提示的。

尽管标记扩展的语法简洁方便,但并不是所有对象都能用标记扩展的语法来写,只有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 myDataSource}, 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}"是等价的。

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

前面说过,当一个XAML标签对应着一个对象时,这个标签的一部分Attribute会对应这个对象的Property。除了这部分对应着对象Property的Attribute外,还有一部分Attribute对应着对象的事件(Event)。

相关内容

热门资讯

清朝有很多有名的历史人物,能给... 清朝有很多有名的历史人物,能给大家说几个清朝的历史人物吗?林则徐,张之洞,乾隆皇帝,曾国藩,朱耷,这...
曹丕的“太子四友”指的是谁 曹丕的“太子四友”指的是谁首先说,曹丕这四个,陈群司马懿是顶级的谋士和政治家,吴质有些小聪明,朱铄不...
在这次遇难者中存在了几名幸存者... 在这次遇难者中存在了几名幸存者。这句话是不是逻辑错误这句话的逻辑没有问题,有问题的是用词不当。遇难者...
在中国古代,有许多充满哲学智慧... 在中国古代,有许多充满哲学智慧的成语典故、寓言故事,如...在中国古代,有许多充满哲学智慧的成语典故...
清澈的意思是什么,… 清澈的意思是什么,…清净而明澈清而透明【造句】看着他清澈而又天真的眼眸,我的心久久不能平静……
蚂蚁森林合种爱情树一方退出怎么... 蚂蚁森林合种爱情树一方退出怎么找回来两个人合种的爱情树,我退出来,我怎么再次加入进去继续合作那个树?...
有好看的古代修炼小说推荐吗? 有好看的古代修炼小说推荐吗?古代重生穿越修炼......让我帮你找一下这些古代修炼的小说,找到这些类...
女主重生爱上前世辜负的人 女主重生爱上前世辜负的人重生我是你正妻渣女重生之竹马重生之弃渣重生之夫君可欺重生之换我疼你重生妇归来...
华胥引有广播剧吗 华胥引有广播剧吗现有的华胥引的广播剧是忆语广播剧社出品的,只有十三月和杯(这个是错字,请无视,居然输...
选文韩麦尔先生在说了,我的朋友... 选文韩麦尔先生在说了,我的朋友们我就要离开你们呢了,再见了银头鲑鱼tj75rt6yturdrruv ...
中通快递从北京保定市到广东揭阳... 中通快递从北京保定市到广东揭阳普宁要多久?中通快递从北京保定市到广东揭阳普宁要多久?从北京到广东需要...
关于离婚悲伤的歌曲 关于离婚悲伤的歌曲关于离婚悲伤的歌曲林俊杰《可惜没如果》 张靓颖《我走以后》 金志文《流着泪说分手》...
形容文笔差怎么说啊? 形容文笔差怎么说啊?哥哥姐姐,麻烦问下,我是做文员的,形容文案方面的工作很差应该怎么说啊?粗鄙怎么样...
我想做未婚妈妈,可行吗? 我想做未婚妈妈,可行吗?没关系吧?我同学好多他们妈妈都是30岁以后省得他们,都没事啊,但是如果你自己...
如何在两个excel表格里筛选... 如何在两个excel表格里筛选出重复的名字如何在两个excel表格里筛选出重复的名字1、电脑打开EX...
且试天下 哪些小说是用白绫做武... 且试天下 哪些小说是用白绫做武器的?神雕侠侣吖- -..小龙女一开始就是用白绫的聊斋 辛十四娘嘿嘿~...
自带高冷体质,笑起来温暖又治愈... 自带高冷体质,笑起来温暖又治愈的星座,你了解吗?虽然天生高冷体质,但是笑起来特别的温暖治愈的新作用天...
火星未解之谜 火星未解之谜多列举一些,每个事例最好长一点,谢啦~“火星人脸”, “地表被水冲击河道”,“原始大气和...
我是1991年10月4号生的,... 我是1991年10月4号生的,是什么星座啊有的说是处女座有的说是天平座,糊涂了,到底是什么啊很负责任...
梦见白狐狸,然后当时我骑着自行... 梦见白狐狸,然后当时我骑着自行车,我想躲开它,他很凶的的追赶我,最后它向我扑了过来,然后我就醒乐.你...