1、数据视图。
使用数据视图,可添加导航逻辑并实现过滤、排序和分组。
2、View对象。
当将结合(或DataTable)绑定到ItemsControl控件时,会不加通告地在后台创建数据视图---位于数据源和绑定的控件之间。数据视图是进入数据源的窗口,可以跟踪当前项,并且支持各种功能,如排序、过滤以及分组。使用的视图对象取决于数据对象的类型。所有视图都继承自CollectionView类,并且有两个继承自CollectionView类的特殊实现:ListCollectionView和BindingListCollectionView。下面是CollectionView类的工作原理:
a)、如果数据源实现了IBindList接口,就会创建BindingListCollectionView视图。当绑定到ADO.Net中的DataTable对象时会创建该视图。
b)、如果数据源没有实现IBindList接口,但实现了IList接口,就会创建ListCollectionView视图。当绑定到ObservableCollection集合(如产品列表)时,就会创建该视图。
c)、如果数据视图没有实现IBindingList或IList接口,但实现了IEnumerable接口,就会得到基本的CollectionView视图。
3、检索视图对象。
为得到当前使用的视图对象,可使用System.Windows.Data.CollectionViewSource类的GetDefaultView()静态方法。当调用GetDefaultView()方法时,传入数据源---正在使用的集合或DataTable对象。
如:ListCollectionView listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource);
4、视图导航。
常用属性和常用方法:
Count:得到列表中的项数。
CurrentPosition:视图中的序号位置。
MoveCurrentToFirst():移动到第一条记录上。
MoveCurrentToLast():移动到最后一条记录上。
MoveCurrentToNext():移动到下一条记录上。
MoveCurrentToPrevious():移动到上一条记录。
MoveCurrentToPosition():移动到当前位置。
xml代码:
<RowDefinition Height="50"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="50"></RowDefinition> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品编号:</TextBlock> <TextBox Grid.Row="0" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProId}"></TextBox> <TextBlock Grid.Row="1" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品名字:</TextBlock> <TextBox Grid.Row="1" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProName}"></TextBox> <TextBlock Grid.Row="2" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品价格:</TextBlock> <TextBox Grid.Row="2" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> <TextBlock Grid.Row="3" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品描述:</TextBlock> <TextBox Grid.Row="3" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProDescribe}"></TextBox> <TextBlock Grid.Row="4" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品图片:</TextBlock> <Image Grid.Row="4" Grid.Column="1"> <Image.Source> <Binding Path="ProImagePath"> <Binding.Converter> <local:ImagePathConverter></local:ImagePathConverter> </Binding.Converter> </Binding> </Image.Source> </Image> <Button Name="btnPrevious" Click="btnPrevious_Click" Grid.Row="5" Grid.Column="0" FontSize="30" FontWeight="Bold" Width="30" Content="<"></Button> <TextBlock Name="showLbl" Grid.Row="5" FontSize="20" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock> <Button Name="btnNext" Grid.Row="5" Grid.Column="1" FontSize="30" FontWeight="Bold" Width="30" Content=">" Click="btnNext_Click"></Button> </Grid> </Border>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; //声明一个视图。 private ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.mainGrid.DataContext = productBll.GetCollectionProduct(); //通过GetDefaultView()方法获取数据视图,类型为ListCollectionView。 listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.mainGrid.DataContext); listView.CurrentChanged += listView_CurrentChanged; this.showLbl.Text = (listView.CurrentPosition + 1).ToString() + "/" + listView.Count.ToString(); } //数据视图的CurrentChanged事件。 void listView_CurrentChanged(object sender, EventArgs e) { this.showLbl.Text = (listView.CurrentPosition + 1).ToString() + "/" + listView.Count.ToString(); btnNext.IsEnabled = this.listView.CurrentPosition < listView.Count - 1; btnPrevious.IsEnabled = this.listView.CurrentPosition > 0; } //MoveCurrentToNext()方法。 private void btnNext_Click(object sender, RoutedEventArgs e) { //点击按钮,移动到下一条记录上。 listView.MoveCurrentToNext(); } //MoveCurrentToPrevious()方法。 private void btnPrevious_Click(object sender, RoutedEventArgs e) { //点击按钮,移动到上一条记录上。 listView.MoveCurrentToPrevious(); } }
效果图:
5、ComBoBox的IsSynchronizedWithCurrentItem属性,选择项同步问题。
xml代码:
<Border CornerRadius="5" Background="SteelBlue"> <Grid Name="mainGrid"> <Grid.RowDefinitions> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="auto"></RowDefinition> </Grid.RowDefinitions> <ComboBox Grid.Row="0" Margin="10" Name="comboBox1" DisplayMemberPath="ProName"></ComboBox> <Grid Grid.Row="1" DataContext="{Binding ElementName=comboBox1, Path=SelectedItem}"> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="50"></RowDefinition> <RowDefinition Height="100"></RowDefinition> <RowDefinition Height="50"></RowDefinition> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品编号:</TextBlock> <TextBox Grid.Row="0" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProId}"></TextBox> <TextBlock Grid.Row="1" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品名字:</TextBlock> <TextBox Grid.Row="1" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProName}"></TextBox> <TextBlock Grid.Row="2" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品价格:</TextBlock> <TextBox Grid.Row="2" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> <TextBlock Grid.Row="3" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品描述:</TextBlock> <TextBox Grid.Row="3" Grid.Column="1" FontSize="20" Height="30" Width="200" HorizontalAlignment="Left" Text="{Binding Path=ProDescribe}"></TextBox> <TextBlock Grid.Row="4" Grid.Column="0" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="20">产品图片:</TextBlock> <Image Grid.Row="4" Grid.Column="1" Source="{Binding Path=ProImagePath, Converter={StaticResource ResourceKey=ImagePathConverter}}"></Image> <Button Name="btnPrevious" Click="btnPrevious_Click" Grid.Row="5" Grid.Column="0" FontSize="30" FontWeight="Bold" Width="30" Content="<"></Button> <TextBlock Name="showLbl" Grid.Row="5" FontSize="20" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" Grid.ColumnSpan="2"></TextBlock> <Button Name="btnNext" Grid.Row="5" Grid.Column="1" FontSize="30" FontWeight="Bold" Width="30" Content=">" Click="btnNext_Click"></Button> </Grid> </Grid> </Border>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; //声明一个视图。 private ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.comboBox1.ItemsSource = productBll.GetCollectionProduct(); //通过GetDefaultView()方法获取数据视图,类型为ListCollectionView。 listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.comboBox1.ItemsSource); listView.CurrentChanged += listView_CurrentChanged; this.showLbl.Text = (listView.CurrentPosition + 1).ToString() + "/" + listView.Count.ToString(); } //数据视图的CurrentChanged事件。 void listView_CurrentChanged(object sender, EventArgs e) { this.showLbl.Text = (listView.CurrentPosition + 1).ToString() + "/" + listView.Count.ToString(); btnNext.IsEnabled = this.listView.CurrentPosition < listView.Count - 1; btnPrevious.IsEnabled = this.listView.CurrentPosition > 0; } //MoveCurrentToNext()方法。 private void btnNext_Click(object sender, RoutedEventArgs e) { //点击按钮,移动到下一条记录上。 listView.MoveCurrentToNext(); } //MoveCurrentToPrevious()方法。 private void btnPrevious_Click(object sender, RoutedEventArgs e) { //点击按钮,移动到上一条记录上。 listView.MoveCurrentToPrevious(); } }
效果图:
这个时候需要设置ComBoBox的IsSynchronizedWithCurrentItem属性为true。
<ComboBox Grid.Row="0" Margin="10" Name="comboBox1" DisplayMemberPath="ProName" IsSynchronizedWithCurrentItem="True"> </ComboBox>
再看效果图:
6、过滤(Filter)。
在将集合用作数据源时,可使用视对象的Filter属性设置过滤器。过滤器会检查集合中的的每个数据项,如果被检查的项满足过滤条件,就返回True,否则返回false。
6.1)、普通过滤。
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; //声明视图。 ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = productBll.GetCollectionProduct(); //将集合用作数据源。可以通过视图进行过滤。 listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource); listView.Filter = new Predicate<object>(FileterProduct); } //过滤。 private bool FileterProduct(object obj) { //转换成对象。 Models.Product product = (Models.Product)obj; //返回价格大于8块的商品。 return (product.ProPrice > 8); } }
效果图:
6.2)、手动设置过滤条件。
xml代码:
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="3*"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName"></ListBox> <TextBlock Grid.Row="1" FontSize="20" VerticalAlignment="Center">价格>= </TextBlock> <TextBox Name="priceFilter" Grid.Row="1" HorizontalAlignment="Right" FontSize="20" VerticalAlignment="Center" Text="0" Width="90"></TextBox> <Button Grid.Row="2" Margin="10" Content="点击过滤" Name="btnFilter" Click="btnFilter_Click"></Button> </Grid> <Grid Grid.Column="1" ShowGridLines="True" DataContext="{Binding ElementName=listBox1, Path=SelectedItem}"> <Border CornerRadius="10"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="2*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock FontSize="20" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center">产品编号:</TextBlock> <TextBox FontSize="20" Grid.Row="0" Grid.Column="1" Height="30" Text="{Binding Path=ProId}"></TextBox> <TextBlock FontSize="20" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center">产品名称:</TextBlock> <TextBox FontSize="20" Grid.Row="1" Grid.Column="1" Height="30" Text="{Binding Path=ProName}"></TextBox> <TextBlock FontSize="20" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Center">产品价格:</TextBlock> <TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> <TextBlock FontSize="20" Grid.Row="3" HorizontalAlignment="Right" VerticalAlignment="Center">产品描述:</TextBlock> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProDescribe}"></TextBox> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProImagePath}"></TextBox> <TextBlock FontSize="20" Grid.Row="4" HorizontalAlignment="Right" VerticalAlignment="Center">产品图片:</TextBlock> <Image Grid.Row="4" Grid.Column="1" Source="{Binding Path=ProImagePath, Converter={StaticResource ImagePathConverter1}}"></Image> </Grid> </Border> </Grid> </Grid>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; //声明视图。 ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = productBll.GetCollectionProduct(); //将集合用作数据源。可以通过视图进行过滤。 listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource); listView.Filter = new Predicate<object>(FileterProduct); } //过滤。 private bool FileterProduct(object obj) { //转换成对象。 Models.Product product = (Models.Product)obj; //返回价格大于5块的商品。 return (product.ProPrice > Convert.ToInt32(this.priceFilter.Text)); } //过滤。 private void btnFilter_Click(object sender, RoutedEventArgs e) { //交换机、电脑、小盒子(配置稍微好一点) //用委托。 listView.Filter += new Predicate<object>(delegate(object obj) { //转换成Product对象。 Models.Product product = (Models.Product)obj; //返回true或false。 return (product.ProPrice > Convert.ToInt32(this.priceFilter.Text)); }); //也可用Lambda表达式。 //listView.Filter += new Predicate<object>((object obj) => //{ // //转换成Product对象。 // Models.Product product = (Models.Product)obj; // //返回true或false。 // return (product.ProPrice > Convert.ToInt32(this.priceFilter.Text)); //}); } }
效果图:
6.3、手动编写过滤器的类。
ProductByPriceFilter类:
/// <summary> /// 价格过滤类。 /// </summary> public class ProductByPriceFilter { /// <summary> /// 最低价。 /// </summary> public decimal MinimumPrice { get; set; } public ProductByPriceFilter(decimal _minimunPrice) { //为属性赋值。 MinimumPrice = _minimunPrice; } /// <summary> /// 此方法用于过滤。 /// </summary> /// <param name="_item">过滤的集合中的每一项。</param> /// <returns></returns> public bool FilterItem(object _item) { //将集合中的项转换成Product对象。 Models.Product product = (Models.Product)_item; if (product != null) { return product.ProPrice > MinimumPrice; } else { return false; } } }
后台代码:
private void btnFilter_Click(object sender, RoutedEventArgs e) { decimal minimumPrice; if(Decimal.TryParse(priceFilter.Text,out minimumPrice)) { if (listView != null) { //实例化ProductByPriceFilter对象。 ProductByPriceFilter proFilter = new ProductByPriceFilter(minimumPrice); //过滤。 listView.Filter = new Predicate<object>(proFilter.FilterItem); } } }
效果和之前一样。
6.4)、删除过滤器。
设置Filter属性=null即可删除过滤器。
7、过滤DataTable对象。
对于DataTable对象,过滤工作是不同的,如果以前使用过ADO.Net,可能已经知道每个DataTable对象都与一个DataView对象相关联。 与ListCollectionView不同的是,DataTable对象使用的是BindingListCollectionView视图,此时图不支持Filter属性,但BindingListCollectionView提供了CustomeFilter属性,CustomeFilter属性本身不能做任何工作,只是接收指定的过滤字符串,并使用这个过滤字符串设置DataView.RowFilter属性。使用DataViewRowFilter属性非常容易,但有点混乱。将基于字符串的过滤器表达式作为参数,这个表达式类似于Select查询中构造Where子句的代码块,要遵守所有的SQL约定,如果希望使用多个条件,需要使用 or 或 and 关键字将这些条件结合在一起。
Xaml代码:
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName"></ListBox> <Grid Grid.Column="1" DataContext="{Binding ElementName=listBox1, Path=SelectedItem}"> <Border CornerRadius="10"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="2*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock FontSize="20" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center">产品编号:</TextBlock> <TextBox FontSize="20" Grid.Row="0" Grid.Column="1" Height="30" Text="{Binding Path=ProId}"></TextBox> <TextBlock FontSize="20" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center">产品名称:</TextBlock> <TextBox FontSize="20" Grid.Row="1" Grid.Column="1" Height="30" Text="{Binding Path=ProName}"></TextBox> <TextBlock FontSize="20" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Center">产品价格:</TextBlock> <TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> <TextBlock FontSize="20" Grid.Row="3" HorizontalAlignment="Right" VerticalAlignment="Center">产品描述:</TextBlock> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProDescribe}"></TextBox> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProImagePath}"></TextBox> <TextBlock FontSize="20" Grid.Row="4" HorizontalAlignment="Right" VerticalAlignment="Center">产品图片:</TextBlock> <Image Grid.Row="4" Grid.Column="1" Source="{Binding Path=ProImagePath, Converter={StaticResource ImagePathConverter1}}"></Image> </Grid> </Border> </Grid> </Grid>
后台代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
Bll.ProductBll productBll = App.ProductBll;
//新建bindingListCollectionView对象。
BindingListCollectionView bindingListCollectionView;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.listBox1.ItemsSource = productBll.GetDataTable().DefaultView;
//将数据源转换成BindingListCollectionView对象,而不是ListCollectionView。
bindingListCollectionView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource);
if (bindingListCollectionView != null)
{
//通过CustomFilter属性,过滤条件,过滤价格 > 5的用户。
bindingListCollectionView.CustomFilter = "ProPrice > 5 ";
//多个条件。
//bindingListCollectionView.CustomFilter = "ProPrice > 5 and ProId < 10 ";
}
}
}
效果图:
8、排序。
可以通过视图进行排序,最简单的方式是根据每个数据项中的一个或多个属性的值进行排序。使用System.ComponentModel.SortDescription对象确定希望使用的字段。每个DortDescription对象确定希望用于排序的字段和排序方向(升序或降序)。
后台代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
Bll.ProductBll productBll = App.ProductBll;
//新建bindingListCollectionView对象。
BindingListCollectionView bindingListCollectionView;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.listBox1.ItemsSource = productBll.GetDataTable().DefaultView;
//将数据源转换成BindingListCollectionView对象,而不是ListCollectionView。
bindingListCollectionView = (BindingListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource);
if (bindingListCollectionView != null)
{
//通过CustomFilter属性,过滤条件,过滤价格 > 5的用户。
bindingListCollectionView.CustomFilter = "ProPrice > 5 ";
//多个条件。
//bindingListCollectionView.CustomFilter = "ProPrice > 5 and ProId < 10 ";
//通过SortDescriptions属性添加排序,实例化SortDescription添加字段和升序降序操作。
bindingListCollectionView.SortDescriptions.Add(new System.ComponentModel.SortDescription("ProId", System.ComponentModel.ListSortDirection.Descending));
}
}
}
9、自定义排序。
自定义排序只能应用于ListCollectionView视图,不能应用于BindingListCollectionView。ListCollectionView类提供的Custome属性接收一个ICompaer对象,ICompare对象在两个数据项之间进行比较,并且指示较大项。如果需要构建组合多个属性来得到排序键的排列例程,这种方法是非常的有用。
xaml代码:
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName"></ListBox> <Grid Grid.Column="1" DataContext="{Binding ElementName=listBox1, Path=SelectedItem}"> <Border CornerRadius="10"> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition Height="2*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock FontSize="20" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center">产品编号:</TextBlock> <TextBox FontSize="20" Grid.Row="0" Grid.Column="1" Height="30" Text="{Binding Path=ProId}"></TextBox> <TextBlock FontSize="20" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center">产品名称:</TextBlock> <TextBox FontSize="20" Grid.Row="1" Grid.Column="1" Height="30" Text="{Binding Path=ProName}"></TextBox> <TextBlock FontSize="20" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Center">产品价格:</TextBlock> <TextBox FontSize="20" Grid.Row="2" Grid.Column="1" Height="30" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBox> <TextBlock FontSize="20" Grid.Row="3" HorizontalAlignment="Right" VerticalAlignment="Center">产品描述:</TextBlock> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProDescribe}"></TextBox> <TextBox FontSize="20" Grid.Row="3" Grid.Column="1" Height="30" Text="{Binding Path=ProImagePath}"></TextBox> <TextBlock FontSize="20" Grid.Row="4" HorizontalAlignment="Right" VerticalAlignment="Center">产品图片:</TextBlock> <Image Grid.Row="4" Grid.Column="1" Source="{Binding Path=ProImagePath, Converter={StaticResource ImagePathConverter1}}"></Image> </Grid> </Border> </Grid> </Grid>
SortByProNameLength类:
//实现自定义排序的步骤: //1、新建1个类,继承IComparer接口。 //2、实现接口中的Compare()方法。 //3、实例化SortByProNameLength实例时,代码需要提供使用的名称(作为字符串),之后Compare方法可以使用反射在数据对象中查找该属性。 public class SortByProNameLength:IComparer { public int Compare(object x, object y) { Models.Product ProductX = (Models.Product)x; Models.Product ProductY = (Models.Product)y; return ProductX.ProName.Length.CompareTo(ProductY.ProName.Length); } }
后台代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
Bll.ProductBll productBll = App.ProductBll;
ListCollectionView listView;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.listBox1.ItemsSource = productBll.GetCollectionProduct2();
listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource);
if (listView != null)
{
//实例化SortByProNameLength对象。
listView.CustomSort = new SortByProNameLength();
}
}
}
效果图:
10)、分组。
与排序的方式相同,视图也支持分组。与排序一样,可使用简单的方式进行分组(根据单个属性值),也可以使用复杂的方式进行分组(使用自定义的回调函数)。为执行分组,需要为CollectionView.GroupDescriptions集合添加System.ComponentMode.PropertyGroupDescription对象。
后台代码:前台不变:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
Bll.ProductBll productBll = App.ProductBll;
ListCollectionView listView;
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.listBox1.ItemsSource = productBll.GetCollectionProduct2();
listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource);
if (listView != null)
{
//根据商品名称进行分组。
listView.GroupDescriptions.Add(new PropertyGroupDescription("ProName"));
}
}
}
效果图:
根据上图可以看出:尽管现在数据项根据产品名字安排到不同的分组中,但当查看列表时,很难发现已经应用到了任何分组,但是,该例发生了更多变化--只是默认情况下看不到这些变化。当使用分组时,列表会为每个分组创建单独的GroupItem对象,并且为列表添加了这些GroupItem对象。GroupItem是内容控件,所以每个GroupItem对象都包含了一个适当的具有实际数据的容器(如ListBoxItem对象)。显示分组的秘密是格式化GroupItem元素,使其突出。可使用样式为列表中的所有GroupItem对象引用格式,如果希望显示分组标题,就需要使用模板了。幸运的是,ItemsControl类通过它的ItemsControl.GroupStyle属性简化了这两项任务,该属性提供了一个GroupStyle对象的集合,虽然名称中包含了"Style",但GroupStyle并不是样式。只是一个简单的包,以下是GroupStyle的属性。
GruopStyle类的常用属性 |
|
ContainerStyle |
设置应用到为每个分组生成的GroupItem元素的样式。 |
ContainerStyleSelector |
不是使用ContainerStyle属性,反而可以使用ContainerStyleSelector属性提供一个类,该类根据分组选择准备使用的正确的样式。 |
HeaderTemplate |
允许用户在每个分组开头显示内容创建模板。 |
HeaderTemplateSelector |
不是使用HeaderTemplate属性,而是可以使用HeaderTemplateSelector属性提供一个类,根据分组选择正确的模板。 |
Panel |
用于改变分组的模板。 |
10.1)、添加分组标记。
添加分组标题,需要设置GroupStyle.HeadTemplate属性,可以使用普通的数据模板进行填充,当编写表达式时,不能绑定到列表中的数据对象(在这个示例中是Product对象),而是要绑定到分组的PropertyGroupDescription对象,需要绑定PropertyGroupDescription.Name属性,而不是绑定Product.ProName属性。
XAML代码:
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20" DisplayMemberPath="ProName"> <ListBox.GroupStyle> <GroupStyle> <!--设置HeaderTemplate--> <GroupStyle.HeaderTemplate> <DataTemplate> <!--在这里绑定的是PropertyGroupDescription.Name属性--> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="LightGreen"></TextBlock> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = productBll.GetCollectionProduct2(); listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource); if (listView != null) { listView.GroupDescriptions.Add(new PropertyGroupDescription("ProName")); } } }
效果图:
10.2)、分组和排序一块使用。
改动的Xaml代码:
<ListBox Name="listBox1" HorizontalContentAlignment="Stretch" FontSize="20"> <ListBox.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Path=ProName}"></TextBlock> <TextBlock Grid.Column="1" Text="{Binding Path=ProPrice, StringFormat={}{0:C}}"></TextBlock> </Grid> </DataTemplate> </ListBox.ItemTemplate> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="LightGreen"></TextBlock> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox>
后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = productBll.GetCollectionProduct2(); listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource); if (listView != null) { //根据产品名称进行分组。 listView.GroupDescriptions.Add(new PropertyGroupDescription("ProName")); //根据产品价格进行分组。 listView.SortDescriptions.Add(new System.ComponentModel.SortDescription("ProPrice", System.ComponentModel.ListSortDirection.Descending)); } } }
效果图:
10.3)、放到概念组中。
如果根据价钱来进行分组,那么不同的价钱会建立一个分组,可以通过创建值转换器类来进行分组。如100到150之间、150到200之间。
新建PriceRangeProductGrouper类:
public class PriceRangeProductGrouper:IValueConverter
{
//分组范围。
public int GroupInterval { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
decimal price = (decimal)value;
if (price < GroupInterval)
{
return string.Format(culture, "小于{0:C}的分组", GroupInterval);
}
else
{
int interval = (int)price / GroupInterval;
int lowerLimit = interval * GroupInterval;
int upperLimit = (interval + 1) * GroupInterval;
return string.Format(culture, "{0:C}到{1:C}之间的分组", lowerLimit, upperLimit);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml代码不变,后台代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } Bll.ProductBll productBll = App.ProductBll; ListCollectionView listView; void MainWindow_Loaded(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = productBll.GetCollectionProduct2(); listView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.listBox1.ItemsSource); if (listView != null) { //实例化PriceRangeProductGrouper类。 PriceRangeProductGrouper grouper = new PriceRangeProductGrouper(50); //传入参数。 listView.GroupDescriptions.Add(new PropertyGroupDescription("ProPrice", grouper)); } } }
效果图:
End!