数据视图

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

当将集合或者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");

WPF进阶技巧和实战03-控件(5-列表、树、网格03)的更多相关文章

  1. WPF进阶技巧和实战03-控件(3-文本控件及列表控件)

    系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...

  2. WPF进阶技巧和实战03-控件(4-基于范围的控件及日期控件)

    系列文章链接 WPF进阶技巧和实战01-小技巧 WPF进阶技巧和实战02-布局 WPF进阶技巧和实战03-控件(1-控件及内容控件) WPF进阶技巧和实战03-控件(2-特殊容器) WPF进阶技巧和实 ...

  3. WPF进阶技巧和实战06-控件模板

    逻辑树和可视化树 System.Windows.LogicalTreeHelper System.Windows.Media.VisualTreeHelper 逻辑树类(LogicalTreeHelp ...

  4. WPF进阶技巧和实战07--自定义元素02

    在01节中,研究了如何开发自定义控件,下节开始考虑更特殊的选择:派生自定义面板以及构建自定义绘图 创建自定义面板 创建自定义面板是一种比较常见的自定义控件开发子集,面板可以驻留一个或多个子元素,并且实 ...

  5. WPF进阶技巧和实战09-事件(2-多点触控)

    多点触控输入 多点触控输入和传统的基于比的输入的区别是多点触控识别手势,用户可以移动多根手指以执行常见的操作,放大,旋转,拖动等. 多点触控的输入层次 WPF允许使用键盘和鼠标的高层次输入(例如单击和 ...

  6. WPF进阶技巧和实战03-控件(1-控件及内容控件)

    所有控件都继承自System.Windows.Controls.Control类,这个类添加一些基本结构: 设置控件内容对齐方式 (HorizontalContentAlignment,Vertica ...

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

    数据模板 样式提供了基本的格式化能力,但是不管如何修改ListBoxItem,他都不能够展示功能更强大的元素组合,因为了每个ListBoxItem只支持单个绑定字段(通过DisplayMemberPa ...

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

    列表控件 ItemsControl为列表项控件定义了基本功能,下图是ItemsControl的继承关系: 在继承自ItemsControl类的层次结构中,还显示了项封装器(MenuItem.TreeV ...

  9. WPF进阶技巧和实战08-依赖属性与绑定03

    数据提供者 在大多数的代码中,都是通过设置元素的DataContext属性或者列表控件的ItemsSource属性,从而提供顶级的数据源.当数据对象是通过另一个类构造时,可以有其他选择. 一种是作为窗 ...

随机推荐

  1. 使用VC6.0开发COM组件 - 傻瓜式,不讲理论,只讲实例

    1.创建一个ATL COM AppWizard工程,如图:

  2. C++ Opencv图像直方图

    Mat image = imread("D:/ju.jpg"); imshow("素材图", image); int bins = 256; //直条为256 ...

  3. web整合Spring

    Spring整合Web开发 时间:2017-2-2 02:17 --导入jar包1.导入Spring开发基本jar包    spring-beans-3.2.0.RELEASE.jar     spr ...

  4. 设计模式<一>

    设计原则1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起. 2.针对接口编程,而不是针对实现编程. 3.多用组合,少用继承. 一:策略模式,定义了算法族,分别封装起来 ...

  5. Java基础和常用框架的面试题

    前言 最近学校也催着找工作了,于是刷了一些面试题,学习了几篇大佬优秀的博客,总结了一些自认为重要的知识点:听不少职场前辈说,对于应届毕业生,面试时只要能说到核心重要的点,围绕这个点说一些自己的看法,面 ...

  6. Python - 面向对象编程 - 子类方法的重写

    继承的详解 https://www.cnblogs.com/poloyy/p/15216652.html 方法的重写 在子类继承父类时,子类会拥有父类的所有属性和方法 但当父类的方法实现不满足子类需要 ...

  7. noip模拟48

    A. Lighthouse 很明显的容斥题,组合式与上上场 \(t2\) 一模一样 注意判环时长度为 \(n\) 的环是合法的 B. Miner 题意实际上是要求偶拉路 对于一个有多个奇数点的联通块, ...

  8. noip模拟46

    A. 数数 排好序从两头贪心即可 B. 数树 首先很容易想到容斥 如果选择的边集的相关点集有点的度数大于 \(1\) 是不合法的 也就是说一定形成若干条长度不一的链 要给这些链上的点安排排列中的数,方 ...

  9. Python - 面向对象编程 - 实战(5)

    前言 主要是针对静态方法.类方法.实例方法.类属性.实例属性的混合实战 需求 设计一个 Game 类 属性 定义一个类属性 top_score 记录游戏的历史最高分,这个属性很明显只跟游戏有关,跟实例 ...

  10. Spring MVC拦截器浅析

    Spring MVC拦截器 重点:Spring MVC的拦截器只会拦截控制器的请求,如果是jsp.js.image.html则会放行. 什么是拦截器 运行在服务器的程序,先于Servlet或JSP之前 ...