举例讲解WPF数据绑定
本来想通过WPF白皮书来讲解,看完过后发现过于详细,很多东西不是那么容易让人理解,便在网上收集了部分东西进行整理。也包括自己总结的部分。
要使用 WPF 数据绑定功能,您必须始终要有目标和源。绑定的目标可以是从 DependencyProperty 派生而来的任何可访问属性或元素,例如 TextBox 控件的 Text 属性。绑定的源可以是任何公共属性,包括其他控件、公共语言运行库 (CLR) 对象、XAML 元素、ADO.NET Dataset、XML 片段等的属性。为了帮助您正确实现绑定,WPF 包含了两个特殊的提供程序:XmlDataProvider 和 ObjectDataProvider。
Source.:提供数据的对象
RelativeSource.:相对源对象
DataContext.:数据文本对象
一:简单的绑定
(1)
<TextBlock.Text>
<Binding ElementName="lbColor" Path="SelectedItem.Content"/>
</TextBlock.Text>
或:
<TextBlock Text="{Binding ElementName=lbColor,
Path=SelectedItem.Content}" />
Binding 标记的 ElementName 属性指示 TextBlock 的 Text 属性要与其绑定的控件的名称。Path 属性指示我们将绑定到的元素(在本例中是 ListBox)的属性。
(2)
以下依次为:NEW个绑定,设置源,设置路径,设置数据流如模式,绑定
Binding binding = new Binding();
binding.Source = sliderFontSize;
binding.Path = new PropertyPath("Value");
binding.Mode = BindingMode.TwoWay;
lblSampleText.SetBinding(TextBlock.TextProperty, binding);
二:绑定模式
<TextBlock
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"/>
请注意前一个示例中将 Mode 属性设为 OneWay 的语句。Mode 属性用于定义绑定模式,它将决定数据如何在源和目标之间流动。除 OneWay 之外,还有另外三种绑定模式:OneTime、OneWayToSource 和 TwoWay。
当我想向用户显示只读数据时,我通常会采用 OneWay 模式。
当我希望用户可以更改控件中的数据,并且让该变化能在数据源(DataSet、对象、XML 或其他绑定控件)中体现出来时,我会使用 TwoWay 绑定。
如果想让用户在数据源不将其数据绑定到目标的情况下更改数据源,我发现 OneWayToSource 是个不错的选择。
接到一个任务,要求在只读控件中显示与加载屏幕时一样的数据状态时,可以使用 OneTime 绑定。
Name | Description |
OneWay | 每当源发生变化,数据就会从源流向目标。 |
TwoWay | 会将源数据发送到目标,但如果目标属性的值发生变化,则会将它们发回给源。 |
OneTime | 仅当应用程序启动时或 DataContext 进行更改时会将数据从源发送到目标 |
OneWayToSource | 将数据从目标发送到源 |
Default | 双向绑定。 |
三:绑定时间
为 UpdateSourceTrigger 指定值,它是用于定义何时更新源的绑定属性。可以为 UpdateSourceTrigger 设置三个值:Explicit、LostFocus 和 PropertyChanged。
将 UpdateSourceTrigger 设置为 Explicit,则不会更新源,除非从代码调用 BindingExpression.UpdateSource 方法。LostFocus 设置(TextBox 控件的默认值)指示源在目标控件失去焦点时才会更新。PropertyChanged 值指示目标会在目标控件的绑定属性每次发生更改时更新源。如果您想指示绑定的时间,该设置非常有用。
四:绑定到 XML
XmlDataProvider 可用来绑定到 XML 文档或片断,该文档或片段既可以嵌入在 XmlDataProvider 标记中,也可以位于外部位置引用的文件中。
嵌入式 XML 内容必须置于 XmlDataProvider 内部的 <x:XData> 标记中,必须为 XmlDataProvider 提供 x:Key 值,以便数据绑定目标可对其进行引用。如:
<StackPanel>
<StackPanel.Resources>
<XmlDataProvider x:Key="MoreColors" XPath="/colors">
<x:XData>
<colors >
<color name="pink"/>
<color name="white"/>
</colors>
</x:XData>
</XmlDataProvider>
XmlDataProvider 定义为 StackPanel 上下文中的资源。这意味着 XmlDataProvider 将可用于该 StackPanel 内部的所有内容。
绑定到控件时,可以设置绑定的 ElementName 和 Path 属性。但是绑定到资源时,需要设置 Source 属性,由于我们是绑定到 XmlDataProvider,所以还要设置绑定的 XPath 属性。如:
<ListBox x:Name="lbColor"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource MoreColors},
XPath=color/@name}">
</ListBox>
XmlDataProvider 也可以指向 XML 内容的外部源,XmlDataProvider 资源添加到 StackPanel,并将其引向 XML 文件即可。请注意,将 Source 属性设置为了 XML 文件的名称,并将 x:Key 设置为 Colors:如:
<XmlDataProvider x:Key="Colors" Source="Colors.xml" XPath="/colors"/>
五:对象绑定和 DataTemplates
想绑定到对象或对象列表时,可以创建 ObjectDataProvider 作为资源。ObjectDataProvider 的 ObjectType 指定将提供数据绑定源的对象,而 MethodName 则指示为获得数据而需调用的方法。
例如,有一个名为 PersonService 的类,该类使用一种名为 GetPersonList 的方法来返回列表 <Person>,那么 ObjectDataProvider 可能会如下所示:
<StackPanel.Resources>
<ObjectDataProvider x:Key="persons"
ObjectType="{x:Type svc:PersonService}"
MethodName="GetPersonList"></ObjectDataProvider>
</StackPanel.Resources>
ObjectDataProvider 还可以使用许多其他属性。ConstructionParameters 属性允许您将参数传递给要调用的类的构造函数。此外,可以使用 MethodParameters 属性来指定参数,同时还可以使用 ObjectInstance 属性来指定现有的对象实例作为源。
在添加 ObjectDataProvider 时,必须限定数据源类的命名空间。如:
xmlns:svc="clr-namespace:DataBindingWPF"
既然数据源已通过 ObjectDataProvider 定义,接着我想将 ListBox 控件中的项绑定到此数据。我想在每个 ListBoxItem 中显示两行文本。第一行将以粗体显示 Person 实例的 FullName 属性,第二行将显示该实例的 Title 和 City。在 XAML 中,通过使用 DataTemplate,这是很容易实现的,DataTemplate 允许您定义可重用的数据可视化策略。其中将 DataTemplate 定义为在我指定的布局中显示 Person 信息。我设置了 DataTemplate 的 DataType 属性,以指示 DataTemplate 将会引用 Person 类类型。我没有在 DataTemplate 中指定真正的绑定,因为我会在 ListBox 控件中指定。通过省略绑定源,可对作用域内的当前 DataContext 执行绑定。我将 ListBox 的 ItemsSource 属性设置为绑定到人员资源,以便我可以将数据绑定到 ListBox,而不用对它进行格式化。通过将 ItemTemplate 属性设置为 PersonLayout 资源(即 DataTemplate 的键名),可以正确显示数据。如下:
<Window x:Class="DataBindingWPF.ObjectBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:svc="clr-namespace:DataBindingWPF"
Title="DataBindingWPF" Height="300" Width="300">
<StackPanel>
<StackPanel.Resources>
<ObjectDataProvider x:Key="persons"
ObjectType="{x:Type svc:PersonService}"
MethodName="GetPersonList" ></ObjectDataProvider>
<DataTemplate x:Key="personLayout" DataType="Person">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=FullName}"
FontWeight="Bold" Foreground="Blue">
</TextBlock>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Title}"></TextBlock>
<TextBlock Text=", "></TextBlock>
<TextBlock Text="{Binding Path=City}"></TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<TextBlock></TextBlock>
<ListBox x:Name="lbPersons"
ItemsSource="{Binding Source={StaticResource persons}}"
ItemTemplate="{DynamicResource personLayout}"
IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</Window>
六:
对数据进行排序
如果想以特定的方式对数据进行排序,可以绑定到 CollectionViewSource,而不是直接绑定到 ObjectDataProvider。CollectionViewSource 则会成为数据源,并充当截取 ObjectDataProvider 中的数据的媒介,并提供排序、分组和筛选功能,然后将它传送到目标。
接着显示的 CollectionViewSource 将其 Source 属性设置为 ObjectDataProvider(人员)的资源名称。然后我通过指示排序依据的属性及其方向定义了数据的排序顺序:
<CollectionViewSource x:Key="personView"
Source="{Binding Source={StaticResource persons}}">
<CollectionViewSource.SortDescriptions>
<ComponentModel:SortDescription
PropertyName="City"
Direction="Ascending" />
<ComponentModel:SortDescription
PropertyName="FullName"
Direction="Descending" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
DataContext 可用来将容器控件内部的所有控件都绑定到数据源中。如:
<StackPanel>
<TextBlock Text="{Binding Source={StaticResource personView},
Path=FullName}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource personView},
Path=Title}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource personView},
Path=City}"></TextBlock>
</StackPanel>
以下是绑定到 DataContext 的三个相同的 TextBox,在此处,DataContext 反过来引用了该控件的 StackPanel 容器:
<StackPanel DataContext="{Binding Source={StaticResource personView}}" >
<TextBlock Text="{Binding Path=FullName}"></TextBlock>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
<TextBlock Text="{Binding Path=City}"></TextBlock>
</StackPanel