真的好累了 ,笑了。做回自己吧 ------------- Aaronyang技术分享 www.ayjs.net
博文摘要:
- 详细介绍了WPF中视图的种类和开始学之前的准备工作
- 视图的 分页视图导航 DEMO1
- 详细讲解了 视图中xaml的声明方式,以及xaml的排序和分组 DEMO2
- 实例讲解了DataTable的BindingListCollectionView的类似操作 DEMO3
- 讲解了LINQ中的过滤 Predicate委托,以及过滤的几种方式
- 讲解了 视图中后台的排序和分组以及过滤,GroupStyle,GroupItem的讲解
- 全方位的推广了WPF4.5的实时成型的知识,动态更新界面上的分组,排序,过滤的数据的方式,而非手动后台Refresh DEMO4
- 其他的细节的讲解
知识引入:
所有视图都继承自CollectionView类,其中有2个特殊的子类:ListCollectionView和BindingListCollectionView。
视图选择:如果数据源实现了IBindingList接口,就会创建BindingListCollectionView,当绑定ADO.NET中的DataTable对象时会创建该视图。
如果实现了IList接口,就会创建ListCollectionView,当绑定到ObservableCollection集合就会创建该视图
如果以上都没实现,但实现了IEnumerable接口,就会得到基本的CollectionView,如果有大量的更删改不推荐使用该视图,使用ObservableCollection最简单。
学之前的准备
1. 基本的DEMO布局
<Window x:Class="TemplateDemo.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="数据视图DEMO" Width="1000" Height="600"> <UniformGrid Columns="2" Rows="2"> <DockPanel Grid.Column="0" Grid.Row="0" Background="#FFF7F6F6" LastChildFill="True" x:Name="demo1"> <Grid DockPanel.Dock="Top" Background="AliceBlue" Height="28"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO1 阅读Ay的博客</TextBlock> </Grid> <Grid DockPanel.Dock="Bottom" Height="28" HorizontalAlignment="Stretch" Background="AliceBlue" > <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" > <TextBlock x:Name="tbPageInfo" VerticalAlignment="Center" Margin="0,0,5,0"> 1/20 </TextBlock> <Button Width="100" Margin="2">上一个</Button> <Button Width="100" Margin="2">下一个</Button> </StackPanel> </Grid> <Canvas> <TextBlock>内容</TextBlock> </Canvas> </DockPanel> <DockPanel Grid.Column="0" Grid.Row="1"> </DockPanel> <DockPanel Grid.Column="1" Grid.Row="0"> </DockPanel> <DockPanel Grid.Column="1" Grid.Row="1" Background="#FFF7F6F6"> </DockPanel> </UniformGrid> </Window>
大致图:
后台创建数据源:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace TemplateDemo { /// <summary> /// Window1.xaml 的交互逻辑 /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { //创建数据源 DateTime dt=DateTime.Now.AddHours(-12); BlogCollection b = new BlogCollection(); for (int i = 1; i <= 100; i++) { AyBlog ay = new AyBlog(); ay.Name = "wpf的博客" + i; ay.Content = "wpf是window上最好的桌面技术,真的很好" + i; ay.CreateTime = dt.AddMinutes(i); b.Add(ay); } demo1.DataContext = b; } } public class AyBlog{ public string Name { get; set; } public string Content { get; set; } public DateTime CreateTime { get; set; } } public class BlogCollection : ObservableCollection<AyBlog> { } }
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
到目前为止:准备工作已经完成,开始吧
1. DEMO1 数据导航
由于创建的是ObservableCollection,所以视图选择是ListCollectionView,我们在窗口后台代码的顶部声明。我们还要记下view中的count
private ListCollectionView view; int maxCount = 0;
并在窗体加载时候,获得视图CollectionViewSource.GetDefaultView(数据源)
private void Window_Loaded(object sender, RoutedEventArgs e) { //创建数据源 DateTime dt=DateTime.Now.AddHours(-12); BlogCollection b = new BlogCollection(); for (int i = 1; i <= 100; i++) { AyBlog ay = new AyBlog(); ay.Name = "wpf的博客" + i; ay.Content = "wpf是window上最好的桌面技术,真的很好" + i; ay.CreateTime = dt.AddMinutes(i); b.Add(ay); } demo1.DataContext = b; view = (ListCollectionView)CollectionViewSource.GetDefaultView(demo1.DataContext); view.CurrentChanged += view_CurrentChanged; maxCount = view.Count; } void view_CurrentChanged(object sender, EventArgs e) { throw new NotImplementedException(); }
我们继续view. 出来东西,发现貌似分页的东西,第一个,最后一个,下一个,上一个,移动到指定位置,移动到指定对象,OK,我们继续修改前台代码,让分页更复杂
<DockPanel Grid.Column="0" Grid.Row="0" Background="#FFF7F6F6" LastChildFill="True" x:Name="demo1"> <Grid DockPanel.Dock="Top" Background="AliceBlue" Height="28"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO1 阅读Ay的博客</TextBlock> </Grid> <Grid DockPanel.Dock="Bottom" Height="28" HorizontalAlignment="Stretch" Background="AliceBlue" > <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" > <TextBox Width="30" Height="20" x:Name="txtCurrentPosition" Text="1" TextAlignment="Center"></TextBox> <TextBlock VerticalAlignment="Center" Margin="5,0,5,0">/</TextBlock> <TextBlock x:Name="tbPageCount" VerticalAlignment="Center" Margin="0,0,5,0">100</TextBlock> <Button Width="30" Height="20" x:Name="btnMoveTo" Margin="0,0,15,0" Click="btnMoveTo_Click">前往</Button> <Button Width="60" Margin="2" x:Name="btnFirst" Click="btnFirst_Click">第一个</Button> <Button Width="60" Margin="2" x:Name="btnPreview" Click="btnPreview_Click">上一个</Button> <Button Width="60" Margin="2" x:Name="btnNext" Click="btnNext_Click">下一个</Button> <Button Width="60" Margin="2" x:Name="btnEnd" Click="btnEnd_Click">最后一个</Button> </StackPanel> </Grid> <Canvas> <StackPanel Orientation="Horizontal"> <TextBlock>标题:</TextBlock> <TextBlock Text="{Binding Path=Name}"></TextBlock> <TextBlock Margin="10,0,0,0" Text="{Binding Path=CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></TextBlock> </StackPanel> <TextBlock Text="{Binding Content}" Canvas.Top="30" TextWrapping="Wrap"> </TextBlock> </Canvas> </DockPanel>
修改后前台的样子:
接下来点击前往,我们要判断文本框是否数字,而后面的4个按钮要该改变文本框的值,我们可以在view的CurrentChanged事件后更改文本框的值。并且那个100在后台 tbPageCount.Text = maxCount.ToString();
void view_CurrentChanged(object sender, EventArgs e) { txtCurrentPosition.Text = (view.CurrentPosition+1).ToString(); //第一项等于0的时候,禁用上一个,最后一个,禁用下一个按钮 btnPreview.IsEnabled = view.CurrentPosition > 0; btnNext.IsEnabled = view.CurrentPosition < view.Count - 1; if (view.CurrentPosition == 0) { btnFirst.Visibility = Visibility.Collapsed; }else{ btnFirst.Visibility = Visibility.Visible; } if (view.CurrentPosition == view.Count - 1) { btnEnd.Visibility = Visibility.Collapsed; } else { btnEnd.Visibility = Visibility.Visible; } }
OK,接下来实现其他几个按钮:
private void btnMoveTo_Click(object sender, RoutedEventArgs e) { string a=txtCurrentPosition.Text; if(a.Length==0){ MessageBox.Show("请输入位置号"); txtCurrentPosition.Focus(); return; } int p; if(int.TryParse(a,out p)){ if (p > maxCount) { MessageBox.Show("指定位置不能大于"+maxCount); txtCurrentPosition.Focus(); return; } view.MoveCurrentToPosition(p-1); } } private void btnFirst_Click(object sender, RoutedEventArgs e) { view.MoveCurrentToFirst(); } private void btnPreview_Click(object sender, RoutedEventArgs e) { view.MoveCurrentToPrevious(); } private void btnNext_Click(object sender, RoutedEventArgs e) { view.MoveCurrentToNext(); } private void btnEnd_Click(object sender, RoutedEventArgs e) { view.MoveCurrentToLast(); }
效果图:
OK到目前位置,我们的大致初步认识了视图的作用,接下来,我们继续了解导航中的MoveCurrentTo方法,参数是一个对象,所以演示最好的就是ComboBox了,绑定对象集合,SelectedItem正好最为他的参数
我们增加combobox,并只显示name,我们可以在它的选择改变事件中,添加
<ComboBox x:Name="cboBlogName" ItemsSource="{Binding ElementName=demo1,Path=DataContext}" Width="80" DisplayMemberPath="Name" Height="20" Margin="0,0,5,0" SelectionChanged="cboBlogName_SelectionChanged"></ComboBox>
private void cboBlogName_SelectionChanged(object sender, SelectionChangedEventArgs e) { view.MoveCurrentTo(cboBlogName.SelectedItem); }
当然还有更简单的办法,只要在combobox上加上IsSynchronizedWithCurrentItem=true,那么后台代码都不要了
<ComboBox x:Name="cboBlogName" ItemsSource="{Binding ElementName=demo1,Path=DataContext}" Width="80" DisplayMemberPath="Name" Height="20" Margin="0,0,5,0" IsSynchronizedWithCurrentItem="True"></ComboBox>
简单效果图:其实这里的下拉,我们可以显示ID,只要对象里有ID号
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
2. xaml中定义视图方式,排序,分组,过滤
为了演示分组和排序,我们修改ayblog的创建CreateTime的方式
//demo2 DateTime dt2 = DateTime.Now.AddMonths(-13); Random monthRandom = new Random(); Random dayRandom = new Random(); Random minRandom = new Random(); BlogCollection b2 = new BlogCollection(); for (int i = 1; i <= 100; i++) { AyBlog ay = new AyBlog(); ay.Name = "wpf的博客" + i; ay.Content = "wpf是window上最好的桌面技术,真的很好" + i; ay.CreateTime = dt2.AddMonths(monthRandom.Next(1, 14)).AddDays(dayRandom.Next(-30, 60)).AddMinutes(minRandom.Next(-60,120)); b2.Add(ay); }
前面第二个demo基本布局
<DockPanel Grid.Column="0" Grid.Row="1" LastChildFill="True" x:Name="demo2"> <Grid DockPanel.Dock="Top" Background="YellowGreen" Height="28"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO2 博客按月份分组</TextBlock> </Grid> <Canvas> <ListBox x:Name="bloglist"> </ListBox> </Canvas> </DockPanel>
接下来,我们需要在xaml中定义个视图,关于 CollectionViewSource有2个属性:View封装了视图对象,Source封装了数据源,还有其他的属性,例如排序,分组,过滤事件。WPF对Listbox绑定的CollectionViewSource对象自动进行了处理,不需要指定CollectionViewSource.View
<Window.Resources> <CollectionViewSource x:Key="BlogView"> </CollectionViewSource> </Window.Resources>
接着在listbox指定使用这个视图
<ListBox x:Name="bloglist" ItemsSource="{Binding Source={StaticResource BlogView}}">
接下来,我们需要在后台给这个视图赋值
运行项目,效果如下:
接下来,我们简单设置listbox模板,并调整宽高,这个我们在上一篇博客讲过了,这里不讲解了
<ListBox x:Name="bloglist" ItemsSource="{Binding Source={StaticResource BlogView}}" Height="252"> <ListBox.ItemTemplate> <DataTemplate> <Border Name="bd" Width="460"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}"></TextBlock> <TextBlock Margin="10,0,0,0" Text="{Binding Path=CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></TextBlock> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
此时博客还没有按照时间排序,只是根据集合的添加顺序显示的,接下来我们排序下,但这次不在后台排序集合
我们需要使用 SortDescription,但这个命名空间在别的地方,我们需要引入空间
xmlns:component="clr-namespace:System.ComponentModel;assembly=WindowsBase"
修改视图细节
<CollectionViewSource x:Key="BlogView"> <CollectionViewSource.SortDescriptions> <component:SortDescription PropertyName="CreateTime" Direction="Descending"/> </CollectionViewSource.SortDescriptions> </CollectionViewSource>
接着,我们按照年月分组,我们需要使用GroupDescriptions
但是分组没那么简单,我们需要一个分组规则,就是保证创建的组名都一致,这样listbox就可以分组显示了,接着我们处理下组名的样式,表明这不是个选择项,是一个组名字,这个demo就完成了
在上篇博客中,我们说到了 binding的二次处理,其中有个思路就是 值转换器,WPF的分组中提供了Converter参数,所以我们可以试着新建一个实现IValueConverter类,以达到产生很多组名,就好比每个视图中的每个对象有了新属性组名字,这样listbox指定了分组样式后就知道了如何进行分组,从而显示了
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Data; namespace TemplateDemo { public class BlogMonthGroupConverter:IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { DateTime dt=(DateTime)value; return dt.Year + "年-" + dt.Month+"月"; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } }
转换器的使用,都是先声明资源,然后使用
<local:BlogMonthGroupConverter x:Key="blogMonthConverter" /> <CollectionViewSource x:Key="BlogView"> <CollectionViewSource.SortDescriptions> <component:SortDescription PropertyName="CreateTime" Direction="Descending"/> </CollectionViewSource.SortDescriptions> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="CreateTime" Converter="{StaticResource blogMonthConverter}"/> </CollectionViewSource.GroupDescriptions> </CollectionViewSource>
此时视图已经自动处理了数据,貌似加了分组的组名新特性,让使用者可以分组显示了,但是让使用者,例如本demo中使用者是listbox,所以我们还要指定组样式,不然组那一条信息显示不出来。
<ListBox x:Name="bloglist" ItemsSource="{Binding Source={StaticResource BlogView}}" Height="252"> <ListBox.ItemTemplate> <DataTemplate> <Border Name="bd" Width="460"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}"></TextBlock> <TextBlock Margin="10,0,0,0" Text="{Binding Path=CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></TextBlock> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="#9ACD32" Margin="0,5,0,0" Padding="3"/> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox>
效果图:
接下来就是演示过滤的用法,需求:只要大于2014年的博客
view的filter对象 需要的是一个Predicate委托
Predicate委托跟Func差不多,内置常用委托,一个输入参数,返回bool的委托,所以你定义个结构一样的方法作为参数传进去就行了。
view的过滤方法中,每次都把本质是Ayblog对象的listboxitem的对象作为参数传入FilterBlog,如果返回true,就显示,否则剔除
效果图:
predicate的等效方法
view2.Filter = delegate(object obj) { AyBlog ay = (AyBlog)obj; if (ay.CreateTime.Year > 2014) { return true; } else { return false; } };
BindingListCollectionView的过滤,典型的从DataTable的DataView获取。
OK,我们创建DEMO3,写一个DataTable的过滤的例子,复制demo2的代码,修改部分name属性,防止重复名字报错
<DockPanel Grid.Column="1" Grid.Row="0" LastChildFill="True" x:Name="demo3"> <Grid DockPanel.Dock="Top" Background="YellowGreen" Height="28"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO3 博客按月份分组(DataTable过滤)</TextBlock> </Grid> <Canvas> <ListBox x:Name="bloglist3" Height="252"> <ListBox.ItemTemplate> <DataTemplate> <Border Name="bd" Width="460"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}"></TextBlock> <TextBlock Margin="10,0,0,0" Text="{Binding Path=CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></TextBlock> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="#9ACD32" Margin="0,5,0,0" Padding="3"/> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox> </Canvas> </DockPanel>
在这段代码中,我没有使用xaml中的视图CollectionViewSource,因为我想演示后台view怎样才能增加分组和排序的代码。DataTable的过滤不是使用Filter而是使用CustomFilter,写法跟Sql的where的条件差不多
在后台,第一步,我们需要创建一个DataTable的数据源才行,然后绑定listbox的ItemsSource,从而得到View
//DEMO3 aaronyang 二〇一五年一月二十九日 15:05:16 //创建空表 DataTable datatable = new DataTable("AyBlog"); //创建列 datatable.Columns.Add("Name", System.Type.GetType("System.String")); datatable.Columns.Add("Content", System.Type.GetType("System.String")); datatable.Columns.Add("CreateTime", System.Type.GetType("System.DateTime")); for (int i = 1; i <= 100; i++) { DataRow dr = datatable.NewRow(); dr["Name"] = "wpf的博客" + i; dr["Content"] = "wpf是window上最好的桌面技术,真的很好" + i; dr["CreateTime"] = dt2.AddMonths(monthRandom.Next(1, 14)).AddDays(dayRandom.Next(-30, 60)).AddMinutes(minRandom.Next(-60, 120)); datatable.Rows.Add(dr); } //指定listbox的ItemsSource bloglist3.ItemsSource = datatable.DefaultView; ////获得视图 ICollectionView cv = CollectionViewSource.GetDefaultView(this.bloglist3.ItemsSource); //添加排序和分组 cv.SortDescriptions.Add(new SortDescription("CreateTime", ListSortDirection.Descending)); cv.GroupDescriptions.Add(new PropertyGroupDescription("CreateTime", new BlogMonthGroupConverter()));
此时运行项目已经可以了,就是不带过滤而已,接着增加过滤的代码
////添加过滤 BindingListCollectionView view3 = cv as BindingListCollectionView; if (view3 != null) { view3.CustomFilter = "CreateTime>‘2014-12-31 23:59:59‘"; }
到目前为止,效果图:
接下来,我们来显示每组的数量,我们在DEMO3的listbox,修改GroupStyle的数据模板。
<ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch"> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="#9ACD32" Margin="0,5,0,0" Padding="3"/> <TextBlock Text="{Binding Path=ItemCount,StringFormat=‘ 共{0}篇‘}" FontWeight="Bold" Foreground="White" Background="#9ACD32" Margin="0,5,0,0" Padding="3"/> </StackPanel> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle>
效果图:
这里绑定的Name属性是PorpertyGroupDescription对象的Name属性。
提示1:
DataTable方式的过滤和排序的细节是存在DataTable的DataView中的,所以共享一个DataTable值时候,效果是同步的。ListCollectionView的CustomSort排序支持一个类,继承IComparer的类,类似LINQ中的比较器,对象的某个属性怎么和另一个对象的某个属性比,返回int的比较。
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
分组补充:当使用分组时候,列表为每个分组创建了单独的GroupItem对象,并且为列表添加了这些GroupItem对象。GroupItem是内容控件,所以每个Group对象都包含了一个适当的具有实际数据的容器(例如ListBoxItem),显示分组就需要格式化GroupItem元素。例如上例中使用了GroupStyle
关于分组虚拟化,就是分组后,有的组中的数据很多,即使ListBox控件支持了虚拟化,但也不会在启用虚拟化时候使用,我们需要在容器中使用VirtualizingPanel.IsVirtualizingWhenGrouping
<ListBox x:Name="bloglist3" Height="252" VirtualizingPanel.IsVirtualizingWhenGrouping="True">
此时,我们把DEMO3,创建10万个数据,然后分组,前提我们使用了VirtualizingPanel.IsVirtualizingWhenGrouping="true",时间大约2-3秒加载完了,还算是可以的
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
DEMO4 实时成型(WPF4.5)
需求:例如DEMO2,如果,如果此时在后台修改了某条已经被过滤掉的博客的时间到2015年,而视图是有过滤器的,此时是不会立即变化的,不会重新归组,排序等,你需要增加一个按钮,然后单击按钮,执行ICollectionViewSource.Refresh()方法刷新视图。才会有变化。
那么在WPF4.5中,增加了实时成型的知识,就是说 当后台修改某条博客的时间,前台会自动变化。或者增加一个博客到集合中,也会自动分组和排序和过滤等。
要使用实时成型,需要满足3个条件:
-
数据对象必须实现INotifyPropertyChanged
public class AyBlogLive:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(PropertyChangedEventArgs e) { if (PropertyChanged != null) { PropertyChanged(this, e); } } private string name; public string Name { get { return name; } set { name = value; OnPropertyChanged(new PropertyChangedEventArgs("Name")); } } private string content; public string Content { get { return content; } set { content = value; OnPropertyChanged(new PropertyChangedEventArgs("Content")); } } private DateTime createTime; public DateTime CreateTime { get { return createTime; } set { createTime = value; OnPropertyChanged(new PropertyChangedEventArgs("CreateTime")); } } }
-
集合必须实现ICollectionViewLiveShaping,而ListCollectionView和BindingListCollectionView已经实现了。
public class BlogCollectionLive : ObservableCollection<AyBlogLive> { }
- 明确启用实时成型,设置视图的IsLiveFiltering,IsLiveSorting,IsLiveGrouping,都是bool的,比如IsLiveFiltering设置true,集合将会检查那些影响当前设置的过滤条件的变化,启用后指定监视哪些属性LiveFilteringProperties,LiveGroupingProperties,LiveSortingProperties
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
2.开始练习
在上面讲解标准中,我已经单独创建新的集合BlogCollectionLive,新的博客类AyBlogLive
前台界面,我们拷贝DEMO2的部分代码,更改Key的名字为BlogViewLive
<CollectionViewSource x:Key="BlogViewLive"> <CollectionViewSource.SortDescriptions> <component:SortDescription PropertyName="CreateTime" Direction="Descending"/> </CollectionViewSource.SortDescriptions> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="CreateTime" Converter="{StaticResource blogMonthConverter}"/> </CollectionViewSource.GroupDescriptions> </CollectionViewSource>
拷贝DEMO2的前台代码并修改,指定新的BlogViewLive视图,调整宽高,底部加上2个按钮
<DockPanel Grid.Column="0" Grid.Row="1" LastChildFill="True" x:Name="demo4" Background="#FFF7F6F6"> <Grid DockPanel.Dock="Top" Background="#E5D411" Height="28"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">DEMO4 博客按月份分组(实时成型)</TextBlock> </Grid> <Grid DockPanel.Dock="Bottom" Height="28" HorizontalAlignment="Stretch" Background="#E5D411" > <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" > <Button Height="20" x:Name="btnLiveTestInsert" Margin="0,0,15,0" Click="btnLiveTestInsert_Click">增加一篇3月博客</Button> <Button Height="20" x:Name="btnLiveTestUpdate" Margin="0,0,15,0" Click="btnLiveTestUpdate_Click">修改一篇2014的文章</Button> </StackPanel> </Grid> <Canvas> <ListBox x:Name="bloglist4" ItemsSource="{Binding Source={StaticResource BlogViewLive}}" Height="228"> <ListBox.ItemTemplate> <DataTemplate> <Border Name="bd" Width="460"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}"></TextBlock> <TextBlock Margin="10,0,0,0" Text="{Binding Path=CreateTime,StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"></TextBlock> </StackPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Foreground="White" Background="#9ACD32" Margin="0,5,0,0" Padding="3"/> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox> </Canvas> </DockPanel>
后台-- 由于要增加新的数据测试界面是否实时过滤分组和排序,我们需要把BlogCollectionLive集合声明在外面
private ListCollectionView view; int maxCount = 0; //demo4 BlogCollectionLive b4 = new BlogCollectionLive(); private void Window_Loaded(object sender, RoutedEventArgs e) { ... }
在窗口的Loaded中,我们新增DEMO4的代码
//demo4 for (int i = 1; i <= 100; i++) { AyBlogLive ay = new AyBlogLive(); ay.Name = "Live Ay的WPF博客" + i; ay.Content = "Live Ay的WPF是window上最好的桌面技术,真的很好" + i; ay.CreateTime = dt2.AddMonths(monthRandom.Next(1, 14)).AddDays(dayRandom.Next(-30, 60)).AddMinutes(minRandom.Next(-60, 120)); b4.Add(ay); } CollectionViewSource cvs4 = (CollectionViewSource)this.FindResource("BlogViewLive"); //拿到视图对象,对它的Source进行赋值 cvs4.Source = b4; ListCollectionView view4 = cvs4.View as ListCollectionView; //版本1 Filter view4.Filter = new Predicate<object>(FilterBlogLive); view4.IsLiveFiltering = true; //view4.IsLiveSorting = true; //view4.IsLiveGrouping = true; view4.LiveFilteringProperties.Add("CreateTime");
因为老的FilterBlog方法中,转换类型是AyBlog,所以我们索性重新写个过滤的方法,只是更换过滤时候强制转换的类型
private bool FilterBlogLive(object obj) { AyBlogLive ay = (AyBlogLive)obj; if (ay.CreateTime.Year > 2014) { return true; } else { return false; } }
到目前为止的界面如下:
接下来,我们实现一个增加一篇3月博客的事件
private void btnLiveTestInsert_Click(object sender, RoutedEventArgs e) { DateTime d=DateTime.Now; DateTime n=new DateTime(d.Year,3,d.Day,d.Hour,d.Minute,d.Second); b4.Add(new AyBlogLive { Name="aaronyang测试WPF实时成型",Content="测试啦",CreateTime=n}); }
然后修改一篇2014的文章,因为由于2014年的文章被过滤过了,所以我们修改他为2015年的,看会不会显示
private void btnLiveTestUpdate_Click(object sender, RoutedEventArgs e) { var findone2014blog = b4.FirstOrDefault(x => x.CreateTime.Year == 2014); findone2014blog.Name = "Ay动态修改2014的WPF文章版变成2015年实时成型测试"; DateTime dt = findone2014blog.CreateTime; findone2014blog.CreateTime = new DateTime(2015, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); }
整体效果图演示:
aaronyang 数据视图 案例下载:下载
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========