WPF笔记(4.3 集合数据绑定)——Data Binding

本节介绍如何绑定一数据集合。

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>


注意,这次Grid绑定的是Famliy这个集合对象,因为只有一个TextBox,所以默认显示第一项"Melissa"。

页面导航技术:

2.绑定到数据列表控件

    <ListBox  ItemsSource="{Binding}"  IsSynchronizedWithCurrentItem="True"  />

没有Path属性,意为绑定到当前的所有对象。
ItemsSource属性设为"{Binding}",不用设置具体是哪一个Source,默认为找到的第一个数据源。
IsSynchronizedWithCurrentItem属性设置为True,保证了自身选项变化,其他绑定控件也相应跟着改变。

运行如下图,可以看到,显示的并不是我们需要的数据,而是直接把每个Object的Type输出了。

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对象。该对象里面的绑定方式同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}" >

4.修改数据集合

以Add为例,这么写是不对的,因为数据源根本不知道有改动:

  void addButton_Click(object sender, RoutedEventArgs e) {
    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 {
    
  }
}

以及使用INotifyPropertyChanged接口:

namespace System.Collections.Specialized {
  
public interface INotifyCollectionChanged {
    
event NotifyCollectionChangedEventHandler CollectionChanged;
  }
}

要让Person类实现INotifyPropertyChanged接口,People类继承ObservableCollection<Person>,就可以了:

namespace PersonBinding {
  
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(  );   //这里将当前的排序规则清除,使用之前的排序规则
    }

实现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 != 0return 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;
    }

这样就找到大于18岁的所有人——那个item对象很神奇的。


posted @ 2007-09-06 20:50  包建强  Views(1679)  Comments(4Edit  收藏  举报