WPF进阶技巧和实战03-控件(5-列表、树、网格03)

系列文章链接

数据视图

数据视图是在后台工作的,用于协调绑定数据的集合。使用数据视图可以添加导航逻辑、实现数据过滤、排序、分组。

当将集合或者DataTable绑定到ItemsControl控件时,会不加通告地在后台创建数据视图(位于数据源和绑定的控件之间)。数据视图是进入数据源的窗口,可以跟踪当前项,并且支持各种功能(排序、过滤、分组等)。这些功能和数据对象本身是相互独立的,这意味着可以在窗口的不同部分使用不同的方式绑定相同的数据源。例如:可将同一产品集合绑定到两个不同的列表,并对产品进行过滤显示不同的记录。

View对象

使用的视图对象取决于数据对象的类型。所有视图都继承自CollectionView类,比较特殊的两个实现是:ListCollectionView和BindingListCollectionView。

  • 如果数据源实现了IBindingList接口,就会创建BindingListCollectionView视图(DataTable对象绑定时)
  • 如果数据源没有实现IBindingList接口,但实现了IList接口,就会创建ListCollectionView视图,当绑定ObservableCollection集合时
  • 如果数据源没有实现IBindingList或IList接口,但是实现了IEnumerable接口时,就会得到基本的CollectionView视图

检索视图对象

得到当前视图对象的方法

ICollectionView view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource);

根据需要转换成合适的类即可。

视图导航

  • 可使用视图对象进行下列操作:
  • 获取列表中的项数(Count属性)
  • 获取当前数据对象的引用(CurrentItem属性)
  • 当前位置索引(CurrentIndex属性)
  • 从一条记录移动到另一条(MoveCurrentToFirst,MoveCurrentToLast,MoveCurrentToNext,MoveCurrentToPrevious,MoveCurrentTo,MoveCurrentToPosition)

以声明方式创建视图

可以在代码中检索视图或者修改视图,还可以在XAML标记中创建CollectionViewSource对象,然后将其绑定到控件上。

CollectionViewSource的两个重要属性就是View和Source,View属性封装了视图的对象,Source属性封装了数据源。还有SortDescriptions和GroupDescriptions

<Window.Resources>
  <local:PriceRangeProductGrouper x:Key="Price50Grouper" GroupInterval="50"/>
  <CollectionViewSource x:Key="GroupByRangeView">
    <CollectionViewSource.SortDescriptions>
      <component:SortDescription PropertyName="UnitCost" Direction="Ascending"/>        
    </CollectionViewSource.SortDescriptions>
    <CollectionViewSource.GroupDescriptions>
      <PropertyGroupDescription PropertyName="UnitCost" Converter="{StaticResource Price50Grouper}"/>
    </CollectionViewSource.GroupDescriptions>
  </CollectionViewSource>
</Window.Resources>

代码中的实现:

CollectionViewSource viewSource = (CollectionViewSource)this.FindResource("GroupByRangeView");
viewSource.Source = products;

过滤、排序、分组

过滤集合

通过过滤可以显示符合特定条件的子集。可使用视图对象的Filter属性设置过滤器。

ListCollectionView view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource) as ListCollectionView;
if (view != null)
{
    filterer = new ProductByPriceFilterer(minimumPrice);
    view.Filter = new Predicate<object>(filterer.FilterItem);
    view.Refresh();
}
public class ProductByPriceFilterer
{
    public decimal MinimumPrice
    {
        get;
        set;
    }
    public ProductByPriceFilterer(decimal minimumPrice)
    {
        MinimumPrice = minimumPrice;
    }
    public bool FilterItem(Object item)
    {
        Product product = item as Product;
        if (product != null)
        {
            if (product.UnitCost > MinimumPrice)
            {
                return true;
            }
        }
        return false;
    }
}

过滤DataTable对象

通过BindingListCollectionView的CustomFilter 属性

decimal minimumPrice;
if (Decimal.TryParse(txtMinPrice.Text, out minimumPrice))
{
    BindingListCollectionView view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource) as BindingListCollectionView;
    if (view != null)
    {
        view.CustomFilter = "UnitCost > " + minimumPrice.ToString();
    }
}

排序

使用视图进行排序,最简单的办法就是根据每个数据项中的一个或者多个属性的值进行排序。使用SortDescriptions对象来确定希望排序的字段。包含了希望排序的字段和排序方向(升序或者降序),按照希望排序的先后进行添加即可。

ICollectionView view = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource);
view.SortDescriptions.Add(new SortDescription("ModelName", ListSortDirection.Ascending));

还可以自定义排序,只能用于ListCollectionView视图(不能用于BindingListCollectionView)。ListCollectionView提供了CustomSort属性来接收一个IComparer对象,这个对象在两个数据项之间进行比较,并且指示较大项。

public class SortByModelNameLength : System.Collections.IComparer
{
    public int Compare(object x, object y)
    {
        Product productX = (Product)x;
        Product productY = (Product)y;
        return productX.ModelName.Length.CompareTo(productY.ModelName.Length);
    }
}
ListCollectionView view = (ListCollectionView)CollectionViewSource.GetDefaultView(lstProducts.ItemsSource);
view.CustomSort = new SortByModelNameLength();

分组

简单方式分组(根据单个属性),复杂方式分组(自定义回调函数)

通过视图的GroupDescriptions属性,添加分组依据。

ICollectionView view = CollectionViewSource.GetDefaultView(lstProducts.It
view.SortDescriptions.Add(new SortDescription("CategoryName", ListSortDir
view.SortDescriptions.Add(new SortDescription("ModelName", ListSortDirect
view.GroupDescriptions.Add(new PropertyGroupDescription("CategoryName"));

当时用分组后,列表为每个分组创建了单独的GroupItem对象,并且为列表添加了这些GroupItem对象。GroupItem是内容控件,所以每个GroupItem对象都包含一个适当的具有实际数据的容器(如ListBoxItem对象),显示分组的秘密是格式化GroupItem对象,使其突出显示。

可以使用样式来为列表中的所有GroupItem对象应用格式。可以通过ItemsControl的GroupStyle属性来实现,GroupStyle类包含了如下属性:

名称 说明
ContainerStyle 设置被应用到每个分组生成的GroupItem的样式
ContainerStyleSelector 通过代码来设置每个分组的GroupItem的正确样式
HeaderTemplate 允许用户为每个分组的头显示内容并创建模板
headerTemplateSelector 通过代码来为每个分组的头设置并创建模板
Panel 改变用于分组的模板,比如使用WrapPanel代替StackPanel,创建从左到右然后向下平铺分组的列表
<ListBox Grid.Row="1" Margin="7,3,7,10" Name="lstProducts" DisplayMemberPath="ModelName">
  <ListBox.GroupStyle>
    <GroupStyle>
      <GroupStyle.HeaderTemplate>
        <DataTemplate>                
            <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" 
		  Foreground="White" Background="LightGreen" Margin="0,5,0,0" Padding="3"/>
          </DataTemplate>
      </GroupStyle.HeaderTemplate>
    </GroupStyle>
  </ListBox.GroupStyle>
</ListBox>
  • 范围分组

需要提供一个转换器,检查数据源中的一个字段或者多个字段,并返回组标题。只需要多个数据对象使用相同的组标题,就可以被放到相同的分组中。

  • 分组和虚拟化

为了降低控件的内存开销,绑定较长的列表时能够提升速度,需要使用控件支持虚拟化。通过VirtualizingStackPanel.IsVirtualizingWhenGrouping=true来讲分组列表和未分组列表获取相同的虚拟化性能提升效果。

实时成型

如果改变正在使用的视图的过滤、排序、分组,就需要调用ICollectionViewSource.Refresh()方法来刷新视图,并确保正确的项出现在列表中。

实时成型的功能:监视特定属性中的变化,如果发生变化,就确定响应更改会影响当前视图并触发刷新动作。

使用实时成型需要满足的3个标准:

  • 数据对象必须实现INotifyPropertyChanged,当属性变化时,使用该接口发出通知
  • 集合必须实现ICollectionViewLiveShaping,标准的ListCollectionView和BindingListCollectionView都实现了这个接口
  • 必须明确启用实时成型

实时成型会增加额外的开销,因此需要设置3个独立的属性:IsLiveFiltering、IsLiveSorting、IsLiveGrouping。通过这3个属性来设置哪些动作启用实时成型,除此之外,还需要设置哪些属性的变化会触发实时成型。

ListCollectionView lcview = CollectionViewSource.GetDefaultView(lstProducts.ItemsSource) as ListCollectionView;// Now if you edit and reduce the price (below the filter condition) the record will disappear automatically.lcview.IsLiveFiltering = true;lcview.LiveFilteringProperties.Add("UnitCost");
posted @ 2021-06-11 15:22  蜗牛的希望  阅读(357)  评论(0编辑  收藏  举报