WPF笔记(4.3 集合数据绑定)——Data Binding
本节介绍如何绑定一数据集合。
1.CurrentItem
将上节的Person聚集为泛型People类:
XAML文件相应为:
这时候,我们使用的是ListCollectionView,而不再是ICollectionView。ListCollectionView是WPF提供的类,已经实现了ICollectionView接口。
1.CurrentItem
将上节的Person聚集为泛型People类:
public class People : List<Person>
{
}
{
}
XAML文件相应为:
<Window x:Class="TestDataBind.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataBind"
Title="TestDataBind" Height="300" Width="300"
>
<Window.Resources>
<local:People x:Key="Family">
<local:Person Name="Melissa" Age="36" />
<local:Person Name="Tom" Age="9" />
<local:Person Name="John" Age="11" />
</local:People>
</Window.Resources>
<Grid DataContext="{StaticResource Family}" Name="grid1">
<TextBlock>Name:</TextBlock>
<TextBox Name="nameTextBox" Text="{Binding Path=Name}" ></TextBox>
<TextBlock>Age:</TextBlock>
<TextBox Name="ageTextBox" Text="{Binding Path=Age}"></TextBox>
</Grid>
</Window>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataBind"
Title="TestDataBind" Height="300" Width="300"
>
<Window.Resources>
<local:People x:Key="Family">
<local:Person Name="Melissa" Age="36" />
<local:Person Name="Tom" Age="9" />
<local:Person Name="John" Age="11" />
</local:People>
</Window.Resources>
<Grid DataContext="{StaticResource Family}" Name="grid1">
<TextBlock>Name:</TextBlock>
<TextBox Name="nameTextBox" Text="{Binding Path=Name}" ></TextBox>
<TextBlock>Age:</TextBlock>
<TextBox Name="ageTextBox" Text="{Binding Path=Age}"></TextBox>
</Grid>
</Window>
注意,这次Grid绑定的是Famliy这个集合对象,因为只有一个TextBox,所以默认显示第一项"Melissa"。
页面导航技术:
2.绑定到数据列表控件
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
没有Path属性,意为绑定到当前的所有对象。
ItemsSource属性设为"{Binding}",不用设置具体是哪一个Source,默认为找到的第一个数据源。
IsSynchronizedWithCurrentItem属性设置为True,保证了自身选项变化,其他绑定控件也相应跟着改变。
3.数据模板
如上所示,我们要显示出正确的数据列表。这个很像GridView或DataList中的数据模板。具体设置如下:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Name}" />
<TextBlock TextContent=" (age: " />
<TextBlock
TextContent="{Binding Path=Age}"
Foreground="
{Binding
Path=Age,
Converter={StaticResource AgeToForegroundConverter}}" />
<TextBlock TextContent=")" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Name}" />
<TextBlock TextContent=" (age: " />
<TextBlock
TextContent="{Binding Path=Age}"
Foreground="
{Binding
Path=Age,
Converter={StaticResource AgeToForegroundConverter}}" />
<TextBlock TextContent=")" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
这里我们使用了ListBox.ItemTemplate属性,这个属性接受一个DataTemplate对象。该对象里面的绑定方式同4.2中介绍的方法。
当然,我们可以把绑定部分放入Resource,这样,ListBox就简单了。<Window.Resources>
<local:People x:Key="Family"></local:People>
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Name}" />
<TextBlock TextContent=" (age: " />
<TextBlock TextContent="{Binding Path=Age}" />
<TextBlock TextContent=")" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<!-- no need for an ItemTemplate setting -->
<ListBox ItemsSource="{Binding}" >
<local:People x:Key="Family"></local:People>
<DataTemplate DataType="{x:Type local:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock TextContent="{Binding Path=Name}" />
<TextBlock TextContent=" (age: " />
<TextBlock TextContent="{Binding Path=Age}" />
<TextBlock TextContent=")" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<!-- no need for an ItemTemplate setting -->
<ListBox ItemsSource="{Binding}" >
4.修改数据集合
以Add为例,这么写是不对的,因为数据源根本不知道有改动:
void addButton_Click(object sender, RoutedEventArgs e) {
People people = (People)this.FindResource("Family");
people.Add(new Person("Chris", 35));
}
People people = (People)this.FindResource("Family");
people.Add(new Person("Chris", 35));
}
WPF为集合提供了一个ObservableCollection<T>类:
namespace System.Windows.Data {
public class ObservableCollection<T> :
Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged {
}
}
public class ObservableCollection<T> :
Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged {
}
}
以及使用INotifyPropertyChanged接口:
namespace System.Collections.Specialized {
public interface INotifyCollectionChanged {
event NotifyCollectionChangedEventHandler CollectionChanged;
}
}
public interface INotifyCollectionChanged {
event NotifyCollectionChangedEventHandler CollectionChanged;
}
}
要让Person类实现INotifyPropertyChanged接口,People类继承ObservableCollection<Person>,就可以了:
namespace PersonBinding {
class Person : INotifyPropertyChanged {}
class People : ObservableCollection<Person> {}
}
class Person : INotifyPropertyChanged {}
class People : ObservableCollection<Person> {}
}
5.排序
我们在前面使用到了ICollectionView接口view,可以对其进行排序,每种排序规则都是一个SortDescription对象:
ICollectionView view = GetFamilyView( );
if( view.Sort.Count == 0 ) {
view.Sort.Add(
new SortDescription("Name", ListSortDirection.Ascending));
view.Sort.Add(
new SortDescription("Age", ListSortDirection.Descending));
}
else {
view.Sort.Clear( ); //这里将当前的排序规则清除,使用之前的排序规则
}
if( view.Sort.Count == 0 ) {
view.Sort.Add(
new SortDescription("Name", ListSortDirection.Ascending));
view.Sort.Add(
new SortDescription("Age", ListSortDirection.Descending));
}
else {
view.Sort.Clear( ); //这里将当前的排序规则清除,使用之前的排序规则
}
实现IComparer接口的Compare(x, y)方法,可以自定义排序类,并赋给view的CustomSort属性:
public class PersonSorter : IComparer
{
public int Compare(object x, object y)
{
Person lhs = (Person)x;
Person rhs = (Person)y;
// Sort Name ascending and Age descending
int nameCompare = lhs.Name.CompareTo(rhs.Name);
if (nameCompare != 0) return nameCompare;
return rhs.Age - lhs.Age;
}
}
public partial class Window1 : Window
{
ICollectionView GetFamilyView()
{
People people = (People)this.FindResource("Family");
return BindingOperations.GetDefaultView(people);
}
void sortButton_Click(object sender, RoutedEventArgs e)
{
ListCollectionView view = (ListCollectionView)GetFamilyView();
if (view.CustomSort == null)
{
view.CustomSort = new PersonSorter();
}
else
{
view.CustomSort = null;
}
}
}
{
public int Compare(object x, object y)
{
Person lhs = (Person)x;
Person rhs = (Person)y;
// Sort Name ascending and Age descending
int nameCompare = lhs.Name.CompareTo(rhs.Name);
if (nameCompare != 0) return nameCompare;
return rhs.Age - lhs.Age;
}
}
public partial class Window1 : Window
{
ICollectionView GetFamilyView()
{
People people = (People)this.FindResource("Family");
return BindingOperations.GetDefaultView(people);
}
void sortButton_Click(object sender, RoutedEventArgs e)
{
ListCollectionView view = (ListCollectionView)GetFamilyView();
if (view.CustomSort == null)
{
view.CustomSort = new PersonSorter();
}
else
{
view.CustomSort = null;
}
}
}
这时候,我们使用的是ListCollectionView,而不再是ICollectionView。ListCollectionView是WPF提供的类,已经实现了ICollectionView接口。
6.Filter
WPF使用Delegate来完成数据过滤功能。 ICollectionView view = GetFamilyView( );
if( view.Filter == null ) {
view.Filter = delegate(object item) {
return ((Person)item).Age >= 18;
};
}
else {
view.Filter = null;
}
if( view.Filter == null ) {
view.Filter = delegate(object item) {
return ((Person)item).Age >= 18;
};
}
else {
view.Filter = null;
}
这样就找到大于18岁的所有人——那个item对象很神奇的。