XAML 概述二

通过上一节我们已经对XAML有了一定的了解,这一节我们来系统的学习一下XAML。

 

一. 简单属性与类型转换器,属性元素:

我们已经知道 XAML是一种声明性的语言,并且XAML解析器会为每个标签创建一个与之对应的对象。光有对象是没有任何意义的,需要对属性之类的赋值才有使用意义。所以就要求XAML为对象属性赋值。XAML中为对象属性赋值共有2种语法:

 · 使用字符串进行简单赋值

 · 使用属性元素进行复杂赋值

<TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" FontFamily="Verdana" FontSize="14" Foreground="Gray">XAML</TextBox>

为了使上面的设置起作用,System.Windows.Controls.TextBox类必须提供上面面这些属性。

上面的设置中,有2个属性为枚举类型(VerticalAlignment,HorizontalAlignment),一个FontFamily类型,一个double类型(FontSize)和一个SolidColorBrush类型(Foreground)。在XAML中,Attribute的值总是一个字符串的值。为了使之能正常工作,XAML解析器悄悄的执行了类型转换:

1.首先检查属性声明,查找该属性是否使用了TypeConverter特性;如果使用了就能进行类型转换。

2.如果属性声明中没有使用TypeConverter特性,就查找数据类型的类的声明;如果使用了就能进行类型转换。

如果属性和类中都没有声明,XAML解析器就会生成一个错误。

     [Category("Appearance"), Localizability(LocalizationCategory.None), TypeConverter(typeof(FontSizeConverter)), Bindable(true)]
        public double FontSize
        {
            get
            {
                return (double)base.GetValue(FontSizeProperty);
            }
            set
            {
                base.SetValue(FontSizeProperty, value);
            }
        }

        [Category("Appearance"), Bindable(true)]
        public Brush Foreground
        {
            get
            {
                return (Brush)base.GetValue(ForegroundProperty);
            }
            set
            {
                base.SetValue(ForegroundProperty, value);
            }
        }

我们查看源代码就可以看到,FontSize属性声明了TypeConverter特性,但是Brush没有。我们再查看Brush类声明的源代码,就会发现有一个TypeConverter(typeof(BrushConverter))特性。其中BrushConverter,FontSizeConverter都是实现了TypeConverter的类型转换类。

 

虽然类型转换器很方便,但是不能解决所有的问题。有些属性是完备的对象,这些对象有着自己的一系列属性。如果继续使用类型转换器的话,语法会很复杂,也有可能出错。幸好XAML提供了一种更好的选择:属性元素。其语法为:

<ClassName>
     <ClassName.AttributeName></ClassName.AttributeName>
</ClassName>

重要的就是中间的那个点(.) 

 

二.标记扩展(Markup Extensions)

仔细观察XAML中为对象属性赋值的语法,大多数都是为属性生成一个新对象。但有的时候需要把同一个对象赋值给两个对象的属性,还有时候需要给对象的属性赋null值,或者赋值为一个静态对象等。这个时候就需要标记扩展了。

有2种方式可以设置标记扩展:

 · 当用于提供特性值时,语法就是左右大括号({ 和 })。 然后,由紧跟在左大括号后面的字符串标记来标识标记扩展的类型。

 · 当用在属性元素语法中时,标记扩展在外观上与其他任何用于提供属性元素值的元素相同,即:一个将标记扩展类作为一个元素引用并以尖括号 (<>) 括起的 XAML 元素声明。

 

尽管标记扩展的语法简单方便,但是不是所有的对象都能使用标记扩展的语法来书写。只有直接或间接实现了System.Windows.Markup.MarkupExtension 的类才能使用标记扩展。

标记扩展并不多,它们是:

 · XAML定义的标记扩展:x:Type 、x:Static、x:Null、x:Array (这几个后面介绍x命名空间的时候会介绍到它们的用法,它们并非是 XAML 的 WPF 实现所特有的,而是语言形式的 XAML 的内部函数或功能实现。)

 

 · 特定于WPF的标记扩展:StaticResource、DynamicResource、Binding、RelativeSource、TemplateBinding、ColorConvertedBitmap、ComponentResourceKey、ThemeDictionary (后面的章节中会介绍它们中部分的用法)

 

最后要注意一下几点:

· 标记扩展是可以嵌套的,逗号表示各个标记的分隔符,例如:

<TextBox Text="{Binding Source={StaticResource Person},Path = Name}"></TextBox>

· 标记扩展有一些简写语法,如{Binding Name,...}与{Binding Path = Name,...}是等价的。两种写法中,前者称为固定位置参数(Positional Parameter),后者称为具名参数(Named Parameters)。固定位置参数实际就是构造函数的参数,其位置由构造函数参数列表决定。如果是具名参数,则会首先为标记扩展调用默认构造函数;之后,每个“名称=值”对都会被解释为标记扩展上存在的属性名称以及赋给该属性的值。

· 标记扩展类几乎以单词Extension为后缀(Binding除外),在XAML中使用的时候可以全写StaticResourceExtension,也可以简写StaticResource。其实这点只用知道就行,我们在用Visaul Studio的时候,智能感知就已经帮我们用简写的方式了。

   

三.附加属性

附加属性是由 XAML 定义的概念。附加属性是可以用于多个控件但在一个类中定义的属性。通常的语法为:AttachedPropertyProvider.PropertyName。

<Grid>
     <TextBox Grid.Row="0">XAML</TextBox>
</Grid>

后面有章节会专门讲到附加属性,到时候再专门讲它。

 

四.嵌套元素

正如我们看到,XAML文档被排列成一个巨大的嵌套元素的树。XAML中有3种机制可以决定如何处理嵌套元素:

· 如果父元素实现了IList接口,解析器就会调用IList.Add()方法,并且为该方法传入子元素作为参数。

· 如果父元素实现了IDictionary接口,解析器就会调用IDictionary.Add()方法,并为该方法传入子元素作为参数。当使用字典集合的时候,必须使用x:Key特性以便为每个条目指定键名。

<TextBox>
     <TextBox.Background>
          <LinearGradientBrush>
                <GradientStopCollection>
                     <GradientStop Offset="0" Color="Red"></GradientStop>
                     <GradientStop Offset="0.5" Color="LavenderBlush"></GradientStop>
                     <GradientStop Offset="1" Color="WhiteSmoke"></GradientStop>
                </GradientStopCollection>
          </LinearGradientBrush>
     </TextBox.Background>
</TextBox>

我们查看GradientStopCollection类的源码,就可以发现它实现了IList接口,所以解析器把每个GradientStop以IList.Add()的方式添加到集合中。

· 如果父元素使用了ContentProperty特性,解析器就会使用子元素设置其对应的属性。

列如我们的查看Grid的源代码,虽然该类没有使用ContentProperty特性,但是Grid的父类Panel类使用了该特性ContentProperty("Children")。这表明应该使用任何嵌套元素来设置Children属性。XAML解析器会根据是否是一个集合属性(集合属性是否实现了IList接口和IDictionary接口),以不同的方式来处理内容属性。因为Panel.Children返回的是一个UIElementCollection对象,并且该对象实现了IList接口,所以解析器使用IList.Add()方法把嵌套的元素添加到集合中。

var grid = new Grid();
var textBox = new TextBox();
grid.Children.Add(textBox);

嵌套的内容不完全就是一个集合,我们查看TextBox,发现它ContentProperty("Text")指向的是一个Text属性,并且该属性返回的是一个string类型。所以TextBox的嵌套元素只能为一个字符串。

还一个Button类,我们知道它的直接基类ButtonBase,并没有发现ContentProperty特性;然后再往上找ContentControl这个基类,就发现了ContentProperty("Content")指向的是Content属性;查找该属性,就会发现它返回的是一个object类型。所有可以在Button中嵌套任何元素。

 

五.事件

到目前为止,介绍的所有Attribute都被映射为Property。然后,Attribute也可以关联事件处理程序。如果使用Visual Studio的话,就可以自动的帮我们完成事件和后台代码的创建与关联。

也可以使用Code元素,直接在XAML文档中嵌入代码(如事件处理程序)。

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

不过最好不要使用这一做法,WPF中并不需要使用这一技术,不值得推荐。

 

六.导入程序集和引用其中的命名空间

这个使用很简单,只需要把需要引用的dll添加到程序引用。然后再使用xmlns:Prefix="clr-namespace:NameSpace;assembly=AssemblyName"语法就行。

Prefix:命名空间前缀。

NameSpace:完全限定的.NET命名空间名称

AssemblyName:声明类型的程序集,没有.dll扩展名

 

七.注释与特殊符号

XAML的注释语法继承自XML。语法为:<!--需要注释的内容-->

不过有几点需要注意:

· 注释只能出现在标签的内容区域,也就是说只能出现在开始和结束标签之间

· 注释不能用于标签的Attribute

· 注释不能嵌套

 

特殊的符号:

特殊字符 字符实体
< &lt;
> &gt;
& &amp;
" &quot;

 

 

 

 

 

 

接下来的一节,我们会了认识一下x命名空间下的具体内容。

posted @ 2013-12-13 17:23  Mind-Hacker  阅读(1234)  评论(0编辑  收藏  举报