10. 数据提供程序
(1) XmlDataProvider
XmlDataProvider 允许我们直接将 XML 数据作为数据源,我们将前面章节的例子改成 XML 数据岛试试,注意此时我们已经不需要在代码中定义 Personal、PersonalList 类型。
<Window x:Class="Learn.WPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" XPath="Personals">
<x:XData>
<Personals xmlns="">
<Personal Name="Tom" Age="15" Sex="Male" />
<Personal Name="Mary" Age="11" Sex="Female" />
<Personal Name="Jack" Age="12" Sex="Male" />
</Personals>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" XPath="Personals">
<x:XData>
<Personals xmlns="">
<Personal Name="Tom" Age="15" Sex="Male" />
<Personal Name="Mary" Age="11" Sex="Female" />
<Personal Name="Jack" Age="12" Sex="Male" />
</Personals>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
在资源中定义 XML 数据岛,注意 "Personals xmlns" 不能省略,另外采用 XPath 进行了绑定操作 (XPath 的语法可参考 MSDN 文档)。除了使用数据岛,我们还以使用 XML 数据文件。
Window1.xaml
<Window x:Class="Learn.WPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" Source="pack://siteOfOrigin:,,,/Personals.xml"
XPath="Personals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" Source="pack://siteOfOrigin:,,,/Personals.xml"
XPath="Personals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
Personals.xml
<?xml version="1.0" encoding="utf-8" ?>
<Personals xmlns="">
<Personal Name="Tom" Age="15" Sex="Male" />
<Personal Name="Mary" Age="11" Sex="Female" />
<Personal Name="Jack" Age="12" Sex="Male" />
</Personals>
<Personals xmlns="">
<Personal Name="Tom" Age="15" Sex="Male" />
<Personal Name="Mary" Age="11" Sex="Female" />
<Personal Name="Jack" Age="12" Sex="Male" />
</Personals>
在 Source 属性中指定 XML Uri。
当然,我们也可以在程序代码中通过 XmlDocument 来控制 XML 数据源。
Window1.xaml
<Window x:Class="Learn.WPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1">
<Window.Resources>
<XmlDataProvider x:Key="personals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding XPath=*}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding XPath=@Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding XPath=@Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var xml = new XmlDocument();
xml.Load("Personals.xml");
var provider = this.FindResource("personals") as XmlDataProvider;
provider.Document = xml;
provider.XPath = "Personals";
}
}
{
public Window1()
{
InitializeComponent();
var xml = new XmlDocument();
xml.Load("Personals.xml");
var provider = this.FindResource("personals") as XmlDataProvider;
provider.Document = xml;
provider.XPath = "Personals";
}
}
逻辑代码只需修改 XmlDocument 即可自动同步显示到界面上。
protected void ButtonClick(object sender, RoutedEventArgs e)
{
var provider = this.FindResource("personals") as XmlDataProvider;
var xml = provider.Document;
var mary = xml.SelectSingleNode("Personals/Personal[@Name=/"Mary/"]") as XmlElement;
var age = Convert.ToInt32(mary.Attributes["Age"].Value);
mary.Attributes["Age"].Value = (++age).ToString();
}
{
var provider = this.FindResource("personals") as XmlDataProvider;
var xml = provider.Document;
var mary = xml.SelectSingleNode("Personals/Personal[@Name=/"Mary/"]") as XmlElement;
var age = Convert.ToInt32(mary.Attributes["Age"].Value);
mary.Attributes["Age"].Value = (++age).ToString();
}
- 如果设置了 Source 属性,则放弃所有内联 XML 数据;如果设置了 Document 属性,则清除 Source 属性并放弃所有内联 XML 数据。
- 设置以下属性将隐式导致此 XmlDataProvider 对象刷新:Source、Document、XmlNamespaceManager 和 XPath。
- 在更改多个导致刷新的属性时,建议使用 DeferRefresh。
(2) ObjectDataProvider
ObjectDataProvider 比我们直接绑定对象有如下三个好处:
- 可以在 XAML 申明中使用构造参数。
- 绑定到源对象的方法上。
- 支持异步数据绑定。
我们先看看构造参数的使用。
Window1.xaml.cs
enum Sex
{
Male,
Female
}
class Personal
{
public string Name { get; private set; }
public int Age { get; private set; }
public Sex Sex { get; private set; }
public Personal(string name, int age, Sex sex)
{
this.Name = name;
this.Age = age;
this.Sex = sex;
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
{
Male,
Female
}
class Personal
{
public string Name { get; private set; }
public int Age { get; private set; }
public Sex Sex { get; private set; }
public Personal(string name, int age, Sex sex)
{
this.Name = name;
this.Age = age;
this.Sex = sex;
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
Window1.xaml
<Window x:Class="Learn.WPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1">
<Window.Resources>
<ObjectDataProvider x:Key="personal" ObjectType="{x:Type my:Personal}">
<ObjectDataProvider.ConstructorParameters>
<sys:String>Tom</sys:String>
<sys:Int32>15</sys:Int32>
<my:Sex>Male</my:Sex>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personal}">
<Label Content="{Binding Name}" />
<Label Content="{Binding Age}" />
<Label Content="{Binding Sex}" />
</StackPanel>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1">
<Window.Resources>
<ObjectDataProvider x:Key="personal" ObjectType="{x:Type my:Personal}">
<ObjectDataProvider.ConstructorParameters>
<sys:String>Tom</sys:String>
<sys:Int32>15</sys:Int32>
<my:Sex>Male</my:Sex>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personal}">
<Label Content="{Binding Name}" />
<Label Content="{Binding Age}" />
<Label Content="{Binding Sex}" />
</StackPanel>
</Grid>
</Window>
接下来,我们尝试绑定到一个方法上。
Window1.xaml.cs
class PersonalList : ObservableCollection<Personal>
{
public PersonalList GetPersonals()
{
this.Add(new Personal("Tom", 15, Sex.Male));
this.Add(new Personal("Mary", 11, Sex.Female));
this.Add(new Personal("Jack", 13, Sex.Male));
return this;
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
{
public PersonalList GetPersonals()
{
this.Add(new Personal("Tom", 15, Sex.Male));
this.Add(new Personal("Mary", 11, Sex.Female));
this.Add(new Personal("Jack", 13, Sex.Male));
return this;
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
Window1.xaml
<Window x:Class="Learn.WPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1">
<Window.Resources>
<ObjectDataProvider x:Key="personals" ObjectType="{x:Type my:PersonalList}"
MethodName="GetPersonals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:Learn.WPF"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="Window1">
<Window.Resources>
<ObjectDataProvider x:Key="personals" ObjectType="{x:Type my:PersonalList}"
MethodName="GetPersonals" />
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Sex}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
和构造方法参数一样,我们也可以向方法提供参数。
Window1.xaml.cs
class PersonalList : ObservableCollection<Personal>
{
public IEnumerable<Personal> GetPersonals(int top)
{
this.Add(new Personal("Tom", 15, Sex.Male));
this.Add(new Personal("Mary", 11, Sex.Female));
this.Add(new Personal("Jack", 13, Sex.Male));
return this.Take(top);
}
}
{
public IEnumerable<Personal> GetPersonals(int top)
{
this.Add(new Personal("Tom", 15, Sex.Male));
this.Add(new Personal("Mary", 11, Sex.Female));
this.Add(new Personal("Jack", 13, Sex.Male));
return this.Take(top);
}
}
Window1.xaml
<ObjectDataProvider x:Key="personals" ObjectType="{x:Type my:PersonalList}" MethodName="GetPersonals">
<ObjectDataProvider.MethodParameters>
<sys:Int32>2</sys:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider.MethodParameters>
<sys:Int32>2</sys:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>