文章目录
x名称空间
<Window x:Class="WpfApp1.Window1"
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:WpfApp1"
mc:Ignorable="d"
Title="Window" Height="188" Width="300">
每个XAML程序的开始处都有 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
,它表示将后面字符串中的命名空间映射到了 x 。我们通常叫它x名称空间。那么x名称空间是怎么来的、又是做什么用的呢?
简单来讲,“x名称空间” 的这个x是映射XML名称空间时给它取的名字(如果用的字母是y,那它就应该叫“y名称空间了”);x名称空间里的成员(如x:Class、x:Name)是专门写给XAML编译器看、用来引导XAML编译器把XAML代码编译成CLR代码的(所以这个x只是XAML的首字母)。
1. x名称空间里都有什么
x名称空间映射的是 http://schemas.microsoft.com/winfx/2006/xaml ,显然,它包含的类均与解析XAML语言有关,所以也可称其为“XAML名称空间”。
与C#语言一样,XAML也有自己的编译器。XAML语言会被解析并编译,最终形成微软中间语言存储在程序集中。在解析和编译XAML语言的过程中,我们经常需要告诉编译器一些重要的信息,比如XAML代码的编译结果应该与哪个C#代码的编译结果合并、使用XAML声明的元素是public还是private访问级别等等。这些让程序员能够与XAML编译器沟通的工具就存放在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指令元素三类。
2. x名称空间中的Attribute
前面已经知道,Attribute和Property是两个层面的东西。Attribute是语言层面的东西、是给编译器看的,Property是面向对象层面的东西、是给编程逻辑用的,而且一个XAML标签的Attribute里大部分都对应着对象的Property。在使用XAML编程的时候,如果你想给它加上一些特殊的标记从而影响XAML编译器对它的解析,这时候就需要额外为它添加一些Attribute了。比如,你想告诉XAML编译器将编译结果与哪个C#编译的类合并,这时就必须为这个标签添加 x:Class="目标类名"
这样一个Attribute以告知XAML编辑器。x:Class这个Attribute并不是对象的成员,而是我们把它从x名称空间里拿出来硬贴上去的。(这句话有助于理解 “Attribute是语言层面的,是给编译器看的;而Property是对象层面的” )
接下来,让我们浏览一下常用的Attribute。
⭐2.1. x:Class
这个Attribute的作用是告诉XAML编译器将XAML标签的编译结果与后台代码中指定的类合并。在使用x:Class时必须遵循以下要求:
- 这个Attribute只能用于根节点。
- 使用x:Class的根节点的类型要与x:Class的值所指示的类型保持一致。
- x:Class的值所指示的类型在声明时必须使用partial关键字。
🔺2.2. x:ClassModifier
这个Attribute的作用是告诉XAML编译标签生成的类具有怎样的访问控制级别。
注意
使用这个Attribute时需要注意:
- 标签必须具有x:Class Attribute
- x:ClassModifier的值必须与x:Class所指示类的访问控制级别一致
- x:ClassModifier的值随后台代码的编译语言不同而有所不同,参见TypeAttributes枚举类型。
这里举个例子,先创建一个WPF项目并编译它,把编译结果用IL反编译器打开,你会看到Window1这个类的访问级别为public。
然后,为XAML文档中的<Window>标签加上x:ClassModifier="internal"
。此时进行编译则会收到一个编译错误,告诉你类的定义有冲突。我们去C#文件里找到Window1类的声明,把声明中的public也改成internal,然后再编译。把编译的结果用IL反编译器打开,你会看到类的访问级别变成了private(之所以是private而不是internal是因为程序集级别的internal与private等价)。
⭐2.3. x:Name
我们已经多次提到XAML是一种声明式语言,但你是否想过XAML标签声明的是什么呢?其实,XAML标签声明的是对象,一个XAML标签会对应一个对象,这个对象一般是一个控件类的实例。在.NET平台上,类是引用类型。引用类型的实例在使用时一般是以“引用者→实例”的形式成对出现的,而且我们只能通过引用者来访问实例。当一个实例不再被任何引用者所引用时,它就会被当作内存垃圾而被销毁。
常见的引用者是引用变量,但这并不是唯一的。比如下面这段XAML代码:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Window1" Height="188" Width="300">
<StackPanel>
<TextBox Margin="5"/>
<Button Content="OK" Margin="5" Click="Button_Click"/>
</StackPanel>
</Window>
这段代码中通篇没有出现一个名字(x:Name),但我们却可以通过引用者的层级关系找到最终想要的控件。下面是Button的Click事件处理器:
private void Button_Click(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = this.Content as StackPanel;
TextBox.textBox = stackPanel.Children[0] as TextBox;
if (string.IsNullOrEmpty(textBox.Name))
{
textBox.Text = "No name!";
}
else
{
textBox.Text = textBox.Name;
}
}
Window1.Content属性引用着StackPanel的实例,而StackPanel实例的Children[0]又引用着TextBox的实例(在C#中,集合索引器又称为“带参数的属性”)。知道了这个关系,就可以一路顺着查找下来并进行类型转换。最后,文本框里显示“No name!”。
虽然理论上我们可以使用这种方法访问到UI上的所有元素,但这毕竟太繁琐了。换句话说:XAML这种对象声明语言只负责声明对象而不负责为这些对象声明引用变量。如果我们需要为对象准备一个引用变量以便在C#代码中直接访问就必须显式地告诉XAML编译器——为这个对象声明引用变量,这时x:Name就派上用场了。
⭐注意
x:Name的作用有两个:
(1). 告诉XAML编译器,当一个标签带有x:Name时除了为这个标签生成对应的实例外还要为这个实例声明一个引用变量,变量名就是x:Name的值。
(2). 将XAML标签所对应对象的Name属性(如果有)也设为x:Name的值,并把这个值注册到UI树上,以方便查找。
让我们做一个小实验——把上面的代码编译结果用IL反编译器打开,你会发现Window1类里不包含任何字段。然后为<TextBox>标签添加x:Name,代码变成这样:
<StackPanel>
<TextBox x:Name="textBox" Margin="5"/>
<Button Content="OK" Margin="5" Click="Button_Click"/>
</StackPanel>
因为TextBox的实例由名为textBox的引用变量所引用,所以我们可以通过引用变量直接访问实例,Button.Click事件处理器中的间接查找代码也就可以省略掉:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(textBox.Name))
{
textBox.Text = "No name!";
}
else
{
textBox.Text = textBox.Name;
}
}
单击按钮后,TextBox中出现“textBox”,说明x:Name不但让编译器声明了引用变量,同时还为实例的Name属性赋了值。使用IL反编译器查看编译结果,你能在Windows1类中找到textBox这个字段。注意,如果一个标签对应的实例没有Name这个属性,那么x:Name作用就只剩下为这个实例创建引用变量了。
那在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以增强代码的统一性和可读性。
🔺2.4. x:FieldModifier
使用x:Name后,XAML标签对应的实例就有了自己的引用变量,而且这些引用变量都是类的字段。既然是类的字段就免不了要关注一下它们的访问级别。默认情况下,这些字段的访问级别按照面向对象的封装原则被设置成了internal。在编程的时候,有时我们需要从一个程序集访问另一个程序集中窗体的元素,这时候就需要把访问控件的引用变量改为public级别,x:FieldModifier就是用来在XAML里改变引用变量访问级别的。
如下所示,声明一个窗体中的控件:
<StackPanel>
<TextBox x:Name="textBox1" x:FieldModifier="public" Margin="5"/>
<TextBox x:Name="textBox2" x:FieldModifier="public" Margin="5"/>
<TextBox x:Name="textBox3"/>
</StackPanel>
使用IL反编译器打开编译结果,可
textBox1和textBox2的访问级别被设置为public,而textBox3的访问级别仍为默认的internal(即程序集级别)。
注意
因为x:FieldModifier是用来改变引用变量访问级别的,所以使用x:FieldModifier的前提是这个标签同时也使用x:Name。
⭐2.5. x:Key
最自然的检索方式莫过于使用“Key-Value”对的形式了。在XAML文件中,我们可以把很多需要多次使用的内容提取出来放在资源字典(Resources Dictionary)里,需要使用这个资源的时候就用它的Key把它检索出来。
x:Key的作用就是为资源贴上用于检索的索引。在WPF中,几乎每个元素都有自己的Resources属性,这个属性是个“Key-Value”式的集合,只要把元素放进这个集合,这个元素就称为资源字典中的一项(item),当然,为了能够检索到该项,就必须为它添加x:Key。资源(Resources)在WPF中非常重要,需要重复使用的XAML内容,如Style、各种Template和动画等都需要放在资源里。
在这里,我们用一个简单例子说明x:Key的用法。先在Window1的资源字典里添加一项,该项是一个字符串,我们将在XAML和C#中多次使用这个字符串。
下面是示例XAML代码:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window" Height="188" Width="300">
<Window.Resources>
<sys:String x:Key="myString">Hello WPF Resource!</sys:String>
</Window.Resources>
<StackPanel Background="Gray">
<TextBox Text="{StaticResource ResourceKey=myString}" Margin="5"/>
<TextBox x:Name="textBox1" Margin="5"/>
<Button Content="Show" Click="Button_Click" Margin="5"/>
</StackPanel>
</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)
{
string str = this.FindResource("myString") as string;
this.textBox1.Text = str;
}
调用一个拥有Resources属性的对象的FindResource方法就可以在它的资源字典里检索资源,检索到资源之后再把它恢复成正确的数据类型就可以使用了。单击按钮之后,可以看到第二个文本框显示出同样的字符串。
2.6. x:Shared
在学习x:Key时我们已经知道,一旦我们把某些对象当作资源放进资源字典里后就可以把它们检索出来重复使用。那么,检索到一个对象时,我们得到的究竟是同一个对象呢,还是这个对象的多个副本呢?这就要看我们给x:Shared赋什么值了。x:Shared一定要和x:Key配合使用,如果x:Shared的值为true,那么每次检索到这个对象时(共享对象),我们得到的都是同一个对象,否则,得到的是对象的一个新副本。XAML编译器会为资源隐藏地添加x:Shared=“true”,也就是说,默认情况下我们得到的都是同一个对象。
3. x名称空间中的标记扩展
标记扩展(Markup Extension)实际上就是一些MarkupExtension类的直接或间接派生类。x名称空间中就包含有一些这样的类,所以常称它们为x名称空间内的标记扩展。
⭐3.1. x:Type
顾名思义,x:Type的值应该是一个数据类型的名称。一般情况下,我们在编程中操作的是数据类型的实例或实例的引用,但有时我们也会用到数据类型本身。
初学者往往搞不清数据类型与类、结构、枚举这些名词之间的关系。我们拿类(Class)来做分析。就“类”这个名词而言,它是具有双重身份的:在逻辑层面上,类是现实世界对象经过抽象和封装后的结果;在编程层面上,我们会使用这个类去创建对象和引用。当我们使用一个类去创建对象的时候,编译器会以这个类为蓝本、按照类的成员的多少在内存中开辟出相应大小的一块内存,并用程序员指定的构造器刷新(即初始化)这块内存。此时,类充当的角色就是对象的“模具”,使用它创建出来的对象在型号(即内存大小)和内部布局上都完全一样。在这个层面上,我们把类称为数据类型(Type),其实,Type这个词本身就具有型号的意思。同理,结构、枚举等也都是数据类型。同时,为了能让程序员在编程层面上自由地操作这些数据类型,比如在不知道具体数据类型的情况下创建这个类型的实例并尝试调用它的方法,.NET Framework中还包含了名为Type的类作为所有数据类型在编程层面上的抽象。
当我们在XAML中想表达某个数据类型时就需要使用x:Type标记扩展。比如某个类的一个属性,它的值要求是一种数据类型,当我们在XAML为这个属性赋值时就要使用x:Type。看下面例子:
创建一个Button的派生类,
class MyButton : Button
{
public Type UserWindowType {get; set; }
protected override void OnClick()
{
base.OnClick();
Window win = Activator.CreateInstance(this.UserWindowType) as Window;
if (win != null)
{
win.ShowDialog();
}
}
}
这个类具有一个Type类型的属性,即UserWindowType,你需要把一种数据类型当作值赋给它。同时,这个类还重写了基类的OnClick方法——除了可以像基类那样触发Click事件外,还会使用UserWindowType所存储的类型创建一个实例,如果这个实例是Window类(或其派生类)的实例,那么就把这个窗体显示出来。
然后,在项目里添加一个名为MyWindow的Window派生类。它的UI包含三个TextBox和一个Button,背景为浅蓝色:
<Window x:Class="WpfApp1.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" Height="188" Width="300">
<StackPanel Background="LightBlue">
<TextBox Margin="5"/>
<TextBox Margin="5"/>
<TextBox Margin="5"/>
<Button Content="OK" Margin="5"/>
</StackPanel>
</Window>
最后,把自定义按钮添加到主窗口的界面上,并把MyWindow作为一种数据类型赋值给MyButton.UserWindowType属性:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
Title="Window1" Height="188" Width="300">
<StackPanel>
<local:MyButton Content="Show" UserWindowType="{x:Type TypeName=local:MyWindow}" Margin="5"/>
</StackPanel>
</Window>
注意
因为MyButton和MyWindow这两个自定义类都包含在当前项目的名称空间里,所以把当前项目的名称空间引用进来并用local前缀映射:
xmlns:local="clr-namespace:WpfApp1"
。在使用MyButton和MyWindow时也要为它们加上local前缀。
回顾一下标记扩展的语法,因为TypeExtension类的构造器可以接受数据类型名作为参数,所以我们完全可以这样写(固定位置参数写法):
UserWindowType="{x:Type local:MyWindow}"
编译并运行程序,单击主窗体上的按钮,自定义窗体就会显示出来。我们还可以多写几个自定义窗体类来扩展这个程序,到时候只需要在XAML里更换UserWindowType的值就可以了。
3.2. x:Null
有时我们需要显式对一个属性赋一个空值。在C#语言里,使用null关键字来表示空值,在XAML里用来表示空值的是x:Null。
下面看一个例子。
大多数时候我们不用显式地为一个属性赋null值,但如果一个属性具有默认值而我们又不需要这个默认值时就需要显式地设置null值了。在WPF中,Style的作用是按照一定的审美规格设置控件的各个属性,程序员可以通过为控件更换Style来产生各种风格迥异的效果。程序员可以逐个为控件设置Style,也可以为一个Style指定目标控件类型,一旦指定了目标类型那么这类控件的实例将都使用这个Style——除非你显式地将某个实例的Style属性设置为x:Null。
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="x:Null Sample" Height="188" Width="300">
<Window.Resources>
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Width" Value="60"/>
</Style>
</Window.Resources>
<StackPanel>
<Button Content="OK"/>
<Button Content="OK"/>
<Button Content="OK" Style="{x:Null}"/>
</StackPanel>
</Window>
该例把一个Style放在了Window的资源里并把它的x:Key和TargetType都设置成了Button类型,这样,UI上的所有Button控件都会默认地被套用这个Style——除了最后一个Button——因为它显式地把Style设置为了x:Null。
🔺3.3. 标记扩展实例的两种声明方法
前面已经认识了x:Type和x:Null两个标记扩展,并使用了它们的转义字符串式声明(即用花括号括起来的字符串作为值赋给标签Attribute的形式)。因为标记扩展也是标准的.NET类,所以,我们也可以使用XAML标签来声明标记扩展的实例。拿上面x:Null的例子来说,最后一个Button的代码完全可以写成这样:
<Button Content="OK">
<Button.Style>
<x:Null/>
</Button.Style>
</Button>
这样做的缺点显而易见,就是代码太啰嗦。所以,为了保持代码的整洁,我们很少使用这种语法。但是有一个例外,那就是x:Array标记扩展——如果想在XAML文档里声明一个包含数据的x:Array实例,必须使用标签式声明才能做到。
⭐3.4. x:Array
x:Array的作用就是通过它的Items属性向使用者暴露一个类型已知的ArrayList实例,ArrayList内成员的类型由x:Array的Type指明。下面例子把一个x:Array当作数据源向一个ListBox提供数据。
在WPF中把包含数据的对象称为数据源(Data Source)。如果想把一个x:Array的实例作为数据源提供给一个ListBox的话,代码是这样:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1" Height="188" Width="300">
<Grid Background="LightBlue">
<ListBox Margin="5" ItemSource="{x:Array Type=sys:String}"/>
</Grid>
</Window>
此时,作为数据源的x:Array实例是没有数据可提供的,所以需要我们为x:Array实例添加一些数据。这时问题就出现了:向ArrayExtension中添加数据需要调用它的AddChild方法,而在XAML中我们无法编写逻辑代码。同时,ArrayExtension的Items属性是只读的,所以,我们也不可能使用ItemSource="{x:Array Type=sys:String Items=XXXXXX}"
的形式为其赋值。我们只能改用标签声明语法:
<ListBox Margin="5">
<ListBox.ItemSource>
<x:Array Type="sys:String">
<sys:String>Tim</sys:String>
<sys:String>Tom</sys:String>
<sys:String>Victor</sys:String>
</x:Array>
</ListBox.ItemSource>
</ListBox>
这样,在解析<x:Array>标签的时候编译器会生成调用AddChild方法的代码把<x:Array>标签子元素逐个添加到x:Array实例的Items里。
最后,看一下ArrayExtension的源代码片段:
[ContentProperty("Items")]
[MarkupExtensionReturnType(typeof(Array))]
public class ArrayExtension : MarkupExtension
{
private ArrayList _arrayList = new ArrayList();
private Type _arrayType;
// 默认构造器
public ArrayExtension() { };
// 带Array参数的构造器
public ArrayExtension(Array elements)
{
// ...
_arrayList.AddRange(elements);
_arrayType = elements.GetType().GetElementType();
}
// 带Type参数的构造器
public ArrayExtension(Type arrayType)
{
// ...
_arrayType = arrayType;
}
//
// 摘要:
// Gets the contents of the array. Settable in XAML through XAML collection syntax.
//
// 返回结果:
// The array contents.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public IList Items
{
get { return _arrayList; }
}
// Type属性,并指明是XAML中的固定位置参数
[ConstructorArgument("type")]
public Type Type
{
get { return _arrayType; }
set { _arrayType = value; }
}
// 向内部ArrayList实例添加元素
public void AddChild(object value)
{
_arrayList.Add(value);
}
public void AddText(string text)
{
AddChild(text);
}
// 向使用者提供数据
// 注意:它是将内部ArrayList根据Type属性转换成Array后以对象的形式提供的
public override object ProvideValue(IServiceProvider serviceProvider)
{
// ...
object retArray = null;
// ...
retArray = _arrayList.ToArray(_arrayType);
// ...
return retArray;
}
}
⭐3.5. x:Static
x:Static是一个常用的标记扩展,它的功能是在XAML文档中使用数据类型的static成员。因为XAML不能编写逻辑代码,所以使用x:Static访问的static成员一定是数据类型的属性或字段。
看一个例子,
首先,为Window1添加两个static成员,一个是static字段,一个是static属性。
public partial class Window1 : Window
{
public static string WindowTitle = "山高月小";
public static string ShowText { get { return "水落石出"; } }
public Window1()
{
InitializeComponent();
}
}
然后,在XAML中使用x:Static来访问这两个成员:
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
Title="{x:Static local:Window1.WindowTitle}" Height="188" Width="300">
<StackPanel>
<TextBlock FontSize="32" Text="{x:Static local:Window1.ShowText}"/>
</StackPanel>
</Window>
运行结果如下图,
注意
如果一个程序需要国际化支持,一般会把需要显示的字符串保存在一个资源类的static属性中,所以支持国际化的程序UI中对x:Static的使用非常频繁。
🔺4. XAML指令元素
XAML指令元素只有两个
- x:Code
- x:XData
<x:Code>标签,作用就是可以把本应该放在后置代码中的C#代码放在XAML文档中。这样做的好处是不用把XAML代码和C#代码分置在两个文件中,但若不是遇到某些极端环境我想应该没人这么干,这样做最大的问题就是代码不好维护、不易调试。
x:XData标签是一个专用标签。WPF中把包含数据的对象称为数据源,用于把数据源中的数据提供给数据使用者的对象称为数据提供者(Data Provider)。WPF类库中包含多种数据提供者,其中有一个类叫XmlDataProvider,专门用于提供XML化的数据。如果想在XAML里声明一个带有数据的XmlDataProvider实例,那么XmlDataProvider实例的数据就要放在x:XData标签的内容里。
<Window.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Supermarket xmlns="">
<Fruits>
<Fruit Name="Peach"/>
<Fruit Name="Banana"/>
</Fruits>
</Supermarket>
</x:XData>
</XmlDataProvider>
</Window.Resources>
5. 小结
至此,我们可以说已经比较完整地掌握XAML的语法和常用元素了。