WPF 学习笔记 - 11. Binding (4)

8. 集合视图

当绑定到一个集合对象时,WPF 总是默认提供一个视图 (CollectionViewSource)。视图会关联到源集合上,并自动将相关的操作在目标对象上显示出来。

(1) 排序

向 CollectionViewSource.SortDescriptions 属性中插入一个或多个排序条件 (SortDescription) 即可实现单个或多个条件排序。

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>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="15" Sex="Male" />
      <my:Personal Name="Mary" Age="11" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
    </my:PersonalList>
  </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

public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();

    var personals = this.FindResource("personals");
    var view = CollectionViewSource.GetDefaultView(personals);
    view.SortDescriptions.Add(new SortDescription("Age", ListSortDirection.Ascending));
  }
}

 

uploads/200811/12_152846_1.png



对 CollectionViewSource.SortDescriptions 的修改会直接反应在界面显示上。

protected void ButtonClick(object sender, RoutedEventArgs e)
{
  var personals = this.FindResource("personals");
  var view = CollectionViewSource.GetDefaultView(personals);
  var direction = sender == btnDesc ? ListSortDirection.Descending : ListSortDirection.Ascending;

  view.SortDescriptions.Clear();
  view.SortDescriptions.Add(new SortDescription("Age", direction));
}

 

uploads/200811/12_152850_2.png



当然,我们可以直接在 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:model="clr-namespace:System.ComponentModel;assembly=WindowsBase"
  Title="Window1">
  <Window.Resources>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="15" Sex="Male" />
      <my:Personal Name="Mary" Age="11" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
    </my:PersonalList>

    <CollectionViewSource x:Key="cvs" Source="{StaticResource personals}">
      <CollectionViewSource.SortDescriptions>
        <model:SortDescription PropertyName="Age" />
        <model:SortDescription PropertyName="Sex" Direction="Descending" />
      </CollectionViewSource.SortDescriptions>
    </CollectionViewSource>

  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource cvs}">
      <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:model="clr-namespace:System.ComponentModel;assembly=WindowsBase" 命名空间。
  • 使用 CollectionViewSource 在资源中定义视图排序条件,注意使用 Source 属性绑定到 personals 资源。
  • 目标对象数据源(DataContext)绑定到视图(cvs)而不是数据源(personals)。

(2) 分组

CollectionViewSource.GroupDescriptions 属性用来控制对数据源进行分组。

Window1.xaml.cs

public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();

    var personals = this.FindResource("personals");
    var view = CollectionViewSource.GetDefaultView(personals);
    
    view.GroupDescriptions.Add(new PropertyGroupDescription("Sex"));
  }
}


按性别进行分组,只是输出结果没啥直观效果。

uploads/200811/12_152854_3.png



要看到效果,我们还必须为 ListBox 添加一个 GroupStyle,基于一贯偷懒的理由,我们可以直接使用系统内置的 GroupStyle.Default。

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>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="15" Sex="Male" />
      <my:Personal Name="Mary" Age="11" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
    </my:PersonalList>
  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}">

        <ListBox.GroupStyle>
          <x:Static Member="GroupStyle.Default"/>
        </ListBox.GroupStyle>
      
        <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>


这回看上去好多了。

uploads/200811/12_152905_4.png



当然,GroupDescriptions 同样也可以写在 XAML 里。

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:data="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
  Title="Window1">
  <Window.Resources>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="15" Sex="Male" />
      <my:Personal Name="Mary" Age="11" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
    </my:PersonalList>

    <CollectionViewSource x:Key="cvs" Source="{StaticResource personals}">
      <CollectionViewSource.GroupDescriptions>
        <data:PropertyGroupDescription PropertyName="Sex" />
      </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource cvs}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}">
        <ListBox.GroupStyle>
          <x:Static Member="GroupStyle.Default"/>
        </ListBox.GroupStyle>
        <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:data="clr-namespace:System.Windows.Data;assembly=PresentationFramework" 命名空间。

(3) 过滤

利用 CollectionViewSource.Filter 委托属性,我们可以对数据源做出过滤处理。比如过滤掉全部女性。

Window1.xaml.cs

public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();

    var personals = this.FindResource("personals");
    var view = CollectionViewSource.GetDefaultView(personals);

    view.Filter = o =>
    {
      return (o as Personal).Sex != Sex.Female;
    };
  }
}

 

uploads/200811/12_152909_5.png



(MSDN 文档好像对不上)

(4) 导航

这个功能很常用,尤其是在数据库系统开发中。不过需要注意的是我们必须确保 IsSynchronizedWithCurrentItem = true。

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>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="15" Sex="Male" />
      <my:Personal Name="Mary" Age="11" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
      <my:Personal Name="Smith" Age="10" Sex="Male" />
      <my:Personal Name="Li." Age="8" Sex="Female" />
    </my:PersonalList>
  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" >
        <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>

      <Label Content="{Binding /Name}" />
      <Label Content="{Binding /Age}"/>

      <Button x:Name="btnFirst" Click="ButtonClick" Content="First" />
      <Button x:Name="btnPrev" Click="ButtonClick" Content="Prev" />
      <Button x:Name="btnNext" Click="ButtonClick" Content="Next" />
      <Button x:Name="btnLast" Click="ButtonClick" Content="Last" />
      <Button x:Name="btnPostion" Click="ButtonClick" Content="Position: 2" Tag="2" />
    </StackPanel>
  </Grid>
</Window>


Window1.xaml.cs

public partial class Window1 : Window
{
  public Window1()
  {
    InitializeComponent();
  }

  protected void ButtonClick(object sender, RoutedEventArgs e)
  {
    var personals = this.FindResource("personals") as PersonalList;
    var view = CollectionViewSource.GetDefaultView(personals);

    if (sender == btnFirst)
      view.MoveCurrentToFirst();
    else if (sender == btnPrev && view.CurrentPosition > 0)
      view.MoveCurrentToPrevious();
    else if (sender == btnNext && view.CurrentPosition < personals.Count - 1)
      view.MoveCurrentToNext();
    else if (sender == btnLast)
      view.MoveCurrentToLast();
    else if (sender == btnPostion)
      view.MoveCurrentToPosition(Convert.ToInt32(btnPostion.Tag));
  }
}

 

uploads/200811/12_152914_6.png



这很有趣,或许你也注意到了 Label 的 Binding 语法。

<Label Content="{Binding /}" /> 表示绑定到当前选择项。
<Label Content="{Binding /Name}" /> 表示绑定到当前选择项的 Name 属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值