《深入浅出WPF》学习总结之XAML标签语言二

x名称空间

“x名称空间”的这个X是映射XML名称空间时给他取的名字(如果用的是字母y,那他就叫“y名称空间了”),x名称空间里的成员(x.name、x.class)是专门给XAML编译器看的、用来引导XAML编译器把XAML代码编译成CLR代码的。大凡包含XAML代码的WPF程序都需要引用xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"命名空间

x命名空间里都有什么

x名称空间映射的是xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml",他所包含的类均与解析XAML语言相关,所以也可以称它"XAML"名称空间,与C#语言一样,XAML也有自己的编译器。XAML语言会被解析并编译,最终形成微软中间语言存储在程序集中。在解析和编译XAML语言过程中,我们经常需要告诉编译器一些重要的信息,比如XAML代码的编译结果应该与哪个C#代码的编译结果合并、使用XAML声明的元素是public还是privated访问级别等等。这些让程序员能够与XAML编译器沟通的工具就存放在x名称中间中。

x名称空间中包含的工具

名称 种类(在XAML中出现的形式)
x:Array 标记扩展
x:Class Attribute
x:ClassModifier Attribute
x:Code XAML指令元素
x:FieldModifier Attribute
x:Key Attribute
x:Name Attribute
x:Null 标记扩展
x:Shared Attribute
x:Static 标记扩展
x:Subclass Attribute
x:Type  标记扩展
 x:TypeArguments  Attribute
x:Uid Attribute
x:XData XAML指令元素

 

它们可以被分为Attribute、标记扩展和XAML指令元素三类

x名称空间中的Attribute

前面我们已经知道,Attribute与Property是两个层面的东西。Attribute是语言层面的东西,是给编译器看的,Property是面向对象层面的东西,是给编程逻辑用的,而且一个XAML标签的Attribute里大部分对应着对象的Property。在使用XAML编程的时候,如果你想给它加上一个特殊的标记从而影响XAML编译器对他的解析,这时候就需要额外为他添加一些Attribute了,比如,你想告诉XAML编译器将编译结果与哪个C#编译的类合并,这时候就必须为这个标签添加x:Class="目标类名"这样一个Attribute已告知XAML编译器。x:Class这个Attribute并不是对象的成员,而是我们把它从x名称空间里拿出来硬贴上去的x:Class
这个Attribute的作用是告诉XAML编译器将XAML标签的编译结果与后台代码中指定的类合并。

  在使用x:Class时必须遵循以下要求:

    1、这个Attribute只能用于根节点

    2、使用x:Class的根节点的类型要与x:Class的值所指示的类型保持一致

    3、x:Class的值所指示的类型在声明时必须使用partial(部分类)关键字。

  x:ClassModifier

    这个Attribute的作用在告诉XAML编译由标签编译生成的类具有怎用的访问级别,在使用这个标签的时候需要注意:
    1、便签必须具有x:Class Attribute。
    2、x:ClassModifier的值必须与x:Class所指示类的访问控制级别一致。   

  x:Name

  x:Name的作用有两个
    1、告诉XAML编译器,当一个标签带有x:Name时除了为这个标签生成对应实例外还要为这个实例声明一个引用变量,变量名就是x:Name的值
    2、将XAML标签所对应对象的Name属性(如果有)也设为x:Name的值,并把这个值注册到UI树上,方便查找使用

  那么在XAML代码中应该使用Name还是x:Name呢?Name属性定义在FrameworkElement类中,这个类是WPF控件的基类,所以所有的WPF控件都具有Name这个属性。当一个元素具有Name属性时,你使用Name或x:Name效果是一样的,比如<Button x:Name="btn"/>和<Button Name="btn"/>,XAML编译器的动作都是声明为btn的Button类型变量并引用一个Button类型实例,而且此实例的Name属性也为btn。此时,Name和x:Name是可以互换的,只是不能同时出现在一个元素中。对于那些没有Name属性的元素,为了在XAML声明时也创建引用变量在C#代码中访问,我们就只能使用x:Name。因为x:Name的功能涵盖了Name属性的功能,所以全部使用x:Name以增强代码的统一性和可读性

  x:FieldModifier

  在使用x:Name后,XAML标签对应的实例就具有了自己的引用变量,而且这些引用变量都是类的字段。既然是类的字段就免不了要关注一下它们的访问级别。默认情况下,这些引用变量都是类的字段。既然是类的字段就免不了需要访问级别。默认情况下,这些字段的访问级别都是按照面向对象的封装原则被设置成了internal。在编程的时候,有时候我们需要从一个程序集访问另一个程序集中窗体的元素,这时候就需要把访问控件的引用变量改为public级别,x:FieldModifier就是用来在XAML里改变引用变量访问级别的。值得注意的是,因为x:FieldModifier是用来改变引用变量访问级别的,所以使用x:FieldModifier的前提是这个标签同时也使用x:Name,不然何来的引用变量呢。

  x:Key

  最自然的方式莫过于使用“key-value”对的形式了。在XAML文件中,我们可以把很多需要多次使用的内容提取出来放在资源字典(Resource Dictionary)里,需要使用这个资源的时候就用它的Key把它检索出来。x:Key的作用就是为资源贴上用于检索的索引,在WPF中,几乎每个元素都有自己的Resources属性,这个属性是个“Key-Value”式的集合,只要把元素放在这个集合,这个元素就成为资源字典中的一个条目,当然,为了能够检索到这个条件,就必须为它添加x:Key。资源(Resources)在WPF只非常重要,需要重复使用的XAML内容,如Style、各种Template和动画等都需要放在资源里。 

 1 <Window x:Class="WpfApp2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 3 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 5 xmlns:local="clr-namespace:WpfApp2"
 6 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 7 mc:Ignorable="d"
 8 Title="Demo" Height="200" Width="300">
 9 <Window.Resources>
10 <sys:String x:Key="myString">Hello World</sys:String>
11 </Window.Resources>
12 <StackPanel Orientation="Vertical">
13 <TextBox Margin="10" Text="{StaticResource ResourceKey=myString}" x:Name="txtSrc"></TextBox>
14 <TextBox Margin="10" x:Name="txtDes"></TextBox>
15 <Button Margin="10" Click="Button_Click">Show</Button>
16 </StackPanel>
17 </Window>

 

在XAML中使用String类,我们用xmlns:sys="clr-namespace:System;assembly=mscorlib"引用了mscorlib.dll,并把其中的System命名空间映射为XAML中的sys命名空间。然后,我们使用属性标签语法向Window.Resources里添加了一个字符串,并把它的x:Key设置为myString。窗体的StackPanel里包含了两个TextBox和一个Button,在为第一个TextBox设置Text属性时,我们用到了myString这个资源,因此,程序一运行我们就可以看到第一个TextBox显示了资源字符串的值

资源不但可以在XAML中访问,在C#中也可以访问下面是Button.Click事件处理器:

private void Button_Click(object sender, RoutedEventArgs e)
{
var myString = this.FindResource("myString") as string;
txtDes.Text = myString;
}

调用一个拥有Resources属性的对象的FindResource方法就可以在他的资源字典里检索资源,检索到资源之后再把它恢复成正确的数据类型就可以使用了。单击按钮之后,就可以看见TxtDes文本框显示出同样的字符串。

   x:Shared

   在使用x:Key时候我们知道,一旦我们把某些对象当作资源放进字典后就可以把它们检索出来重复使用,那么,每当我们检索到一个对象的时候,我们得到的究竟是同一个对象呢,还是这个对象的多个副本呢?那就的看我们给x:Shared赋什么值了,x:Shared一定要与x:Key配合使用,如果x:Shared的值为True,那么每次检索到这个对象的时,我们得到的都是同一个对象,否则如果x:Shared的值为False,每次我们检索到这个对象时,我们得到的都是这个对象的一个新副本。XAML编译器会为资源隐藏添加x:Shared="True",也就是说,默认情况下我们得到的都是同一个资源

x命名空间中的扩展标记

  标记扩展(Markup Extension)实际上就是一些MarkupExtension类的直接或者间接派生类。x名称空间中就包含有一些这样的类,所以常称它们为x名称空间内的标记扩展。

  x:Type

  x:Type的值是一个数据类型的名称,一般情况下,我们在编程中操作的时数据类型的实例的引用,但有时候我们也会用到数据类型本身。当我们在XAML中想表达某个数据类型时就需要使用x:Type标记扩展,比如某个类的一个属性,它的值要求时一种数据类型,当我们在XAML为这个属性赋值就要使用x:Type。
首先我们创建一个Button派生类

 1 class MyButton:Button
 2 {
 3 public Type UserWindowType { get; set; }
 4 
 5 protected override void OnClick()
 6 {
 7 base.OnClick();
 8 
 9 var winType = Activator.CreateInstance(UserWindowType) as Window;
10 if (winType == null)
11 return;
12 winType.Show();
13 }
14 }

这个类具有一个Type类型的属性,即UserWindowType ,我们需要把一种数据类型赋值给他,同时这个类还重写了基类的OnClick事件,除了可以像基类那样触发Click事件外,还会使用UserWindowType 所存储的类型创建一个实例,如果这个类型实例时Window类(或其派生类)的实例,那么就把这个窗体显示出来
然后我们在项目中添加一个名为MyWindow的Window派生类。

<Window x:Class="WpfApp2.MyWindow"
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="MyWindow" Height="200" Width="300">
<StackPanel Orientation="Vertical" x:Name="mainPanel" >
<TextBox Margin="10"></TextBox>
<TextBox Margin="10"></TextBox>
<Button Margin="10">ClickMe</Button>
</StackPanel>
</Window>

最后,把自定义按钮添加到主窗口的界面上,并把MyWindow作为一种数据类型赋值给MyButton.UserWindowType 属性

<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"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Demo" Height="200" Width="300">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<local:MyButton Width="200" UserWindowType="{x:Type TypeName=local:MyWindow}">Show</local:MyButton>
</Grid>
</Window>

注意:因为MyButton和MyWindow这两个自定义类都包含在当前项目的名称空间中,所以把当前项目的名称空间引用进来并用local前缀映射,所以在使用MyButton和MyWindow时也要为它们加上local前缀
编译并运行程序,单击主窗体上的按钮,自定义窗体就会显示出来。我们还可以多写几个自定义窗体类来扩展这个程序,到时候只需要在XAML里面更换UserWindowType 的值就可以了。

 

  x:Null

   有时候我们需要对一个属性赋一个空值,在C#语言里,使用null关键字来表示空值,在XAML里用来表示空值的是x:Null。大多数时候我们不用显式的为一个属性赋null值,但如果一个属性具有默认值而我们又不需要这个默认值时就需要显式的设置null值了。

 1 <Window x:Class="WpfApp2.MainWindow"
 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6 xmlns:local="clr-namespace:WpfApp2"
 7 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 8 mc:Ignorable="d"
 9 Title="Demo" Height="200" Width="300">
10 <Window.Resources>
11 <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
12 <Setter Property="Width" Value="60"></Setter>
13 <Setter Property="Height" Value="30"></Setter>
14 <Setter Property="Margin" Value="10"></Setter>
15 </Style>
16 </Window.Resources>
17 <StackPanel Orientation="Vertical">
18 <Button>Hello</Button>
19 <Button>Hello</Button>
20 <Button Style="{x:Null}">Hello</Button>
21 </StackPanel>
22 </Window>

上面的例子把一个Style放在了Window的资源里并把它的x:Key 和 TargetType都设置成了Button类型,这样UI上的所有Button控件都默认的被套用了这个Style,除了出后一个Button因为它显式的把Style设置为了x:Null

 

  x:Array

  x:Array的作用就是通过他的Items属性向使用者暴露了一个类型已知的ArrayList实例,ArrayList内成员的类型由x:Array的Type指明,下面这个例子把一个x:Array当作数据源向一个ListBox提供数据

 1 <Window x:Class="WpfApp2.MainWindow"
 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6 xmlns:local="clr-namespace:WpfApp2"
 7 xmlns:sys="clr-namespace:System;assembly=mscorlib"
 8 mc:Ignorable="d"
 9 Title="Demo" Height="200" Width="300">
10 <Grid>
11 <ListBox>
12 <ListBox.ItemsSource>
13 <x:Array Type="sys:String">
14 <sys:String>Hello World</sys:String>
15 <sys:String>Hello Simple</sys:String>
16 <sys:String>Hello C#</sys:String>
17 </x:Array>
18 </ListBox.ItemsSource>
19 </ListBox>
20 </Grid>
21 </Window>

这样,在解析<x:Array>标签的时候编译器会生成调用AddChild方法的代码把<x:Array>标签的子元素逐个添加到x:Array实例的Item里

   x:Static

  x:Static是一个很常用的标记扩展,它的功能是在XAML文档中使用数据类型的static成员。因为XAML不能编写逻辑代码,所以使用x:Static访问的static成员一定是数据类型的属性或字段。

 1 <Window x:Class="WpfApp2.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:WpfApp2"
 7         xmlns:sys="clr-namespace:System;assembly=mscorlib"
 8         mc:Ignorable="d"
 9         Title="{x:Static local:MainWindow.WindowTitle}" Height="200" Width="300">
10     <StackPanel>
11         <TextBlock FontSize="30" Text="{x:Static local:MainWindow.ControlContent}"></TextBlock>
12     </StackPanel>
13 </Window>

  XAML指令元素

  XAML指令元素有两个

  x:Code

    他的作用就是可以包含一些本应放置在后置代码中的C#代码,这样做的好处是不用把XAML代码和C#代码分置在两个文件中,但若不是遇到某些极端环境请前往别这么写,这样做最大的问题就是代码不好维护,不易调试

  x:XData

    x:XData是一个专用标签,WPF中把包含数据的对象称为数据源,用于把数据源中的数据提供给数据使用者的对象被称为数据提供者(Data Provider)。WPF类库中包含多种数据提供者,其中有一个类叫XmlDataProvider,专门用于提供XML化的数据,如果想在XAML总声明一个带有数据的XmlDataProvider实例,那么XmlDataProvider实例的数据就要放在x:XData标签的内容里

posted @ 2019-11-24 20:46  Simple_Han  阅读(733)  评论(0编辑  收藏  举报