目录
7、使用 XMLDataProvider 和 XPath 查询绑定到 XML 数据
若要支持?OneWay?或?TwoWay?绑定,从而使绑定目标属性能够自动反映绑定源的动态更改。
若要实现?INotifyPropertyChanged,需要声明?PropertyChanged?事件并创建?OnPropertyChanged
?方法。 然后,对于每个需要更改通知的属性,只要进行了更新,就可以调用?OnPropertyChanged
。
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SDKSample
{
// This class implements INotifyPropertyChanged
// to support one-way and two-way bindings
// (such that the UI element updates when the source
// has been changed dynamically)
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
public Person(string value)
{
this.name = value;
}
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
本示例介绍了如何使用?UpdateSourceTrigger?属性控制绑定源更新的执行时间。 本主题使用?TextBox?控件作为示例。
TextBox.Text?属性的?UpdateSourceTrigger?默认值为?LostFocus。 这意味着如果应用程序的?TextBox?包含数据绑定?TextBox.Text?属性,则直到?TextBox?失去焦点(例如,将鼠标移到?TextBox?外单击时),键入到?TextBox?中的文本才会更新源。
如果希望在键入过程中更新源,请将该绑定的?PropertyChanged?设置为?UpdateSourceTrigger。 在下面的示例中,突出显示的代码行显示?TextBox?和?TextBlock?的?Text
?属性都绑定到相同的源属性。?TextBox?绑定的?UpdateSourceTrigger?属性设置为?PropertyChanged。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SDKSample"
xmlns:system="clr-namespace:System;assembly=mscorlib"
SizeToContent="WidthAndHeight"
Title="Simple Data Binding Sample">
<Window.Resources>
<ObjectDataProvider x:Key="myDataSource" ObjectType="{x:Type src:Person}">
<ObjectDataProvider.ConstructorParameters>
<system:String>Joe</system:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
<Style TargetType="{x:Type Label}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="25"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<Border Margin="25" BorderBrush="Aqua" BorderThickness="3" Padding="8">
<DockPanel Width="200" Height="100">
<Label>Enter a Name:</Label>
<TextBox>
<TextBox.Text>
<Binding Source="{StaticResource myDataSource}" Path="Name"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Label>The name you entered:</Label>
<TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=Name}"/>
</DockPanel>
</Border>
</Window>
因此,TextBlock?所显示的文本将与用户输入到?TextBox?中的文本相同(因为源发生更改),如该示例的以下屏幕快照所示:
如果拥有一个对话框或用户可编辑的窗体,并且希望将源更新延迟到用户完成字段编辑并单击“确定”之后,可以将绑定的?UpdateSourceTrigger?值设置为?Explicit,如下面的示例所示:
<TextBox Name="itemNameTextBox"
Text="{Binding Path=ItemName, UpdateSourceTrigger=Explicit}" />
如果将?UpdateSourceTrigger?值设置为?Explicit,则仅当应用程序调用?UpdateSource?方法时,该源值才会发生更改。 下面的示例演示如何为?itemNameTextBox
?调用?UpdateSource:
// itemNameTextBox is an instance of a TextBox
BindingExpression be = itemNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
?备注
此方法也可用于其他控件的属性,但请记住,其他大多数属性的默认?UpdateSourceTrigger?值为?PropertyChanged。
?备注
UpdateSourceTrigger?属性用于处理源更新,因此仅适用于?TwoWay?或?OneWayToSource?绑定。 若要使?TwoWay?和?OneWayToSource?绑定生效,源对象需要提供属性更改通知。?
此示例演示了如何实现大纲-详细信息方案。
在此示例中,LeagueList
?是?Leagues
?的集合。 每个?League
?都有一个?Name
?和一个?Divisions
?集合,每个?Division
?都有一个名称和一个?Teams
?集合。 每个?Team
?都有一个团队名称。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SDKSample"
Width="400" Height="180"
Title="Master-Detail Binding"
Background="Silver">
<Window.Resources>
<src:LeagueList x:Key="MyList"/>
<DockPanel DataContext="{Binding Source={StaticResource MyList}}">
<StackPanel>
<Label>My Soccer Leagues</Label>
<ListBox ItemsSource="{Binding}" DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding Path=Name}"/>
<ListBox ItemsSource="{Binding Path=Divisions}" DisplayMemberPath="Name"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding Path=Divisions/Name}"/>
<ListBox DisplayMemberPath="Name" ItemsSource="{Binding Path=Divisions/Teams}"/>
</StackPanel>
</DockPanel>
</Window>
下面是该示例的一个屏幕快照。?Divisions
ListBox?自动跟踪?Leagues
ListBox?中的所选项并显示相应的数据。?Teams
ListBox?跟踪其他两个?ListBox?控件中的所选项。
此示例中有两点需要注意:
必须在要跟踪其所选项的?ListBox?控件上将?IsSynchronizedWithCurrentItem?属性设置为?true
。 设置此属性可确保所选项始终设置为?CurrentItem。 或者,如果?ListBox?从?CollectionViewSource?获取数据,它会自动同步所选项和货币。
此示例演示了如何使用 XML 数据实现大纲-详细信息方案。
在此示例中,数据来自文件?League.xml
。 请注意,第三个?ListBox?控件如何通过绑定到其?SelectedValue?属性跟踪第二个?ListBox?控件中的选择更改。
<Window x:Class="SDKSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Multiple ListBox Binding Sample"
Width="400" Height="200"
Background="Cornsilk">
<Window.Resources>
<XmlDataProvider x:Key="MyList" Source="Data\Leagues.xml"
XPath="Leagues/League"/>
<DataTemplate x:Key="dataTemplate">
<TextBlock Text="{Binding XPath=@name}" />
</DataTemplate>
</Window.Resources>
<DockPanel DataContext="{Binding Source={StaticResource MyList}}">
<StackPanel>
<Label>My Soccer Leagues</Label>
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource dataTemplate}"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding XPath=@name}"/>
<ListBox Name="divisionsListBox"
ItemsSource="{Binding XPath=Division}"
ItemTemplate="{StaticResource dataTemplate}"
IsSynchronizedWithCurrentItem="true"/>
</StackPanel>
<StackPanel>
<Label Content="{Binding XPath=@name}"/>
<ListBox DataContext="{Binding ElementName=divisionsListBox,
Path=SelectedItem}"
ItemsSource="{Binding XPath=Team}"
ItemTemplate="{StaticResource dataTemplate}"/>
</StackPanel>
</DockPanel>
</Window>
此示例演示如何使用?ElementName?属性将一个已实例化控件的属性绑定到另一个控件的属性。
下面的示例演示如何将?Canvas?的?Background?属性绑定到?ComboBox?的?SelectedItem.Content?属性:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="460" Height="200"
Title="Binding the Properties of Two Controls">
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<Style TargetType="Canvas">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Margin" Value="8"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
<Style TargetType="ComboBox">
<Setter Property="Width" Value="150"/>
<Setter Property="Margin" Value="8"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<Border Margin="10" BorderBrush="Silver" BorderThickness="3" Padding="8">
<DockPanel>
<TextBlock>Choose a Color:</TextBlock>
<ComboBox Name="myComboBox" SelectedIndex="0">
<ComboBoxItem>Green</ComboBoxItem>
<ComboBoxItem>Blue</ComboBoxItem>
<ComboBoxItem>Red</ComboBoxItem>
</ComboBox>
<Canvas>
<Canvas.Background>
<Binding ElementName="myComboBox" Path="SelectedItem.Content"/>
</Canvas.Background>
</Canvas>
</DockPanel>
</Border>
</Window>
当此示例呈现时,应如下所示:
?备注
绑定目标属性(在本例中为?Background?属性)必须是一个依赖属性。?
本示例演示如何创建和绑定到派生自?ObservableCollection<T>?类的集合,该类是一个在添加或移除项时提供通知的集合类。
下面的示例演示?NameList
?集合的实现:
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
this.firstName = first;
this.lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
可以根据使数据可用于 XAML 中的绑定
中的说明,按照与其他公共语言运行时 (CLR) 对象相同的方式使集合可用于绑定。 例如,可以在 XAML 中实例化该集合,并将该集合指定为一个资源,如下所示:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:SDKSample"
x:Class="SDKSample.Window1"
Width="400"
Height="280"
Title="MultiBinding Sample">
<Window.Resources>
<c:NameList x:Key="NameListData"/>
...
</Window.Resources>
然后可以绑定到该集合:
<ListBox Width="200"
ItemsSource="{Binding Source={StaticResource NameListData}}"
ItemTemplate="{StaticResource NameItemTemplate}"
IsSynchronizedWithCurrentItem="True"/>
此处没有显示?NameItemTemplate
?的定义。
?备注
集合中的对象必须满足
绑定源概述中所述的要求。 特别是,如果使用?OneWay?或?TwoWay(例如,希望 UI 在源属性发生显著变化时进行更新),则必须实现一个适当的“属性已更改”通知机制,如?INotifyPropertyChanged?接口。
此示例介绍如何使用?XmlDataProvider?绑定到 XML 数据。
使用?XmlDataProvider,在应用程序中可通过数据绑定访问的基础数据可以是 XML 节点的任意树。 也就是说,XmlDataProvider?提供一种将 XML 节点的任意树用作绑定源的简便方式。
在以下示例中,数据作为 XML 数据岛直接嵌入?Resources?部分。 XML 数据岛必须包装在?<x:XData>
?标记中,并始终具有一个单一根节点,在本示例中根节点为 Inventory。
?备注
XML 数据的根节点具有一个将 XML 命名空间设置为空字符串的 xmlns 属性。 将 XPath 查询应用到 XAML 页中内联的数据岛时,需要此属性。 在此内联情况下,XAML 以及数据岛会继承?System.Windows?命名空间。 因此,需要将命名空间设置为空白,以防止 XPath 查询被?System.Windows?命名空间限定而误导查询。
<StackPanel
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Cornsilk">
<StackPanel.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Inventory xmlns="">
<Books>
<Book ISBN="0-7356-0562-9" Stock="in" Number="9">
<Title>XML in Action</Title>
<Summary>XML Web Technology</Summary>
</Book>
<Book ISBN="0-7356-1370-2" Stock="in" Number="8">
<Title>Programming Microsoft Windows With C#</Title>
<Summary>C# Programming using the .NET Framework</Summary>
</Book>
<Book ISBN="0-7356-1288-9" Stock="out" Number="7">
<Title>Inside C#</Title>
<Summary>C# Language Programming</Summary>
</Book>
<Book ISBN="0-7356-1377-X" Stock="in" Number="5">
<Title>Introducing Microsoft .NET</Title>
<Summary>Overview of .NET Technology</Summary>
</Book>
<Book ISBN="0-7356-1448-2" Stock="out" Number="4">
<Title>Microsoft C# Language Specifications</Title>
<Summary>The C# language definition</Summary>
</Book>
</Books>
<CDs>
<CD Stock="in" Number="3">
<Title>Classical Collection</Title>
<Summary>Classical Music</Summary>
</CD>
<CD Stock="out" Number="9">
<Title>Jazz Collection</Title>
<Summary>Jazz Music</Summary>
</CD>
</CDs>
</Inventory>
</x:XData>
</XmlDataProvider>
</StackPanel.Resources>
<TextBlock FontSize="18" FontWeight="Bold" Margin="10"
HorizontalAlignment="Center">XML Data Source Sample</TextBlock>
<ListBox
Width="400" Height="300" Background="Honeydew">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}"
XPath="*[@Stock='out'] | *[@Number>=8 or @Number=3]"/>
</ListBox.ItemsSource>
<!--Alternatively, you can do the following. -->
<!--<ListBox Width="400" Height="300" Background="Honeydew"
ItemsSource="{Binding Source={StaticResource InventoryData},
XPath=*[@Stock\=\'out\'] | *[@Number>\=8 or @Number\=3]}">-->
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="12" Foreground="Red">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
如此示例中所示,若要使用属性语法创建相同的绑定声明,必须对特殊字符进行正确转义。?
运行此示例时,ListBox?将显示以下项。 这些项为?Books?下所有元素的?Title,其中?Stock?值为“out”,Number?值为 3 或者大于或等于 8。 请注意,没有返回任何 CD 项,因为?XmlDataProvider?上设置的?XPath?值表示只应公开 Books 元素(本质上是设置筛选器)。
此示例显示书名,因为?DataTemplate?中的?TextBlock?绑定的?XPath?设为“Title”。 如果希望显示属性值,如 ISBN,则应将?XPath?值设置为“@ISBN
”。
WPF 中的 XPath 属性使用 XmlNode.SelectNodes 方法处理。 可以修改?XPath?查询以获取不同的结果。 以下是上一示例中对绑定?ListBox?执行?XPath?查询的部分示例:
XPath="Book[1]"
?将返回第一个 Book 元素(“XML in Action”)。 请注意,XPath?索引从 1 而不是从 0 开始。
XPath="Book[@*]"
?将返回带有任意属性的所有 Book 元素。
XPath="Book[last()-1]"
?将返回第二个至最后一个 Book 元素(“Introducing Microsoft .NET”)。
XPath="*[position()>3]"
?将返回除前 3 个元素之外的所有 Book 元素。
当运行 XPath 查询时,它将返回?XmlNode?或 XmlNode 列表。?XmlNode?是公共语言运行时 (CLR) 对象,这意味着可以使用?Path?属性绑定到公共语言运行时 (CLR) 属性。 再以上述示例为例。 如果该示例的其余部分保持不变,将?TextBlock?绑定更改为下面的值,则将在?ListBox?中看到返回的 XmlNode 的名称。 在此情况下,所有返回节点的名称为“Book”。
<TextBlock FontSize="12" Foreground="Red">
<TextBlock.Text>
<Binding Path="Name"/>
</TextBlock.Text>
</TextBlock>
在某些应用程序中,将 XML 作为 XAML 页的源内的数据岛嵌入可能很不方便,因为在编译时必须知道该数据的确切内容。 因此,还支持从外部 XML 文件获取该数据,如下面的示例所示:
<XmlDataProvider x:Key="BookData" Source="data\bookdata.xml" XPath="Books"/>
如果 XML 数据驻留在远程 XML 文件中,可以通过将适当的 URL 分配给?Source?属性来定义对该数据的访问,如下所示:
<XmlDataProvider x:Key="BookData" Source="http://MyUrl" XPath="Books"/>