[Aaronyang] 写给自己的WPF4.5 笔记8[复杂数据处理三步曲,数据视图精讲1/3]

真的好累了 ,笑了。做回自己吧       -------------      Aaronyang技术分享 www.ayjs.net

博文摘要:

  1. 详细介绍了WPF中视图的种类和开始学之前的准备工作
  2. 视图的 分页视图导航 DEMO1
  3. 详细讲解了 视图中xaml的声明方式,以及xaml的排序和分组 DEMO2
  4. 实例讲解了DataTable的BindingListCollectionView的类似操作 DEMO3
  5. 讲解了LINQ中的过滤 Predicate委托,以及过滤的几种方式
  6. 讲解了 视图中后台的排序和分组以及过滤,GroupStyle,GroupItem的讲解
  7. 全方位的推广了WPF4.5的实时成型的知识,动态更新界面上的分组,排序,过滤的数据的方式,而非手动后台Refresh     DEMO4
  8. 其他的细节的讲解

知识引入:

  所有视图都继承自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个条件:

  1. 数据对象必须实现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"));
                }
            }
        }
  2. 集合必须实现ICollectionViewLiveShaping,而ListCollectionView和BindingListCollectionView已经实现了。
        public class BlogCollectionLive : ObservableCollection<AyBlogLive>
        {
    
        }
  3. 明确启用实时成型,设置视图的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 =========== 安徽 六安 杨洋 ==========   未经允许不许转载 =========

时间: 2024-10-06 12:28:21

[Aaronyang] 写给自己的WPF4.5 笔记8[复杂数据处理三步曲,数据视图精讲1/3]的相关文章

[Aaronyang] 写给自己的WPF4.5 笔记9[复杂数据处理三步曲,数据展示ListView泪奔2/3]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 作者留言:        小小的推荐,作者的肯定,读者的支持.推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献.其实写这篇文章时候已经哭了,最近几篇文章,在我个人看来都是wpf的宝藏文章.每天读者也就200-400多人,也说明了WPF的人越来也少了.但是我的Blend教程和WPF控件开发,3D W

[Aaronyang] 写给自己的WPF4.5 笔记11[自定义控件-AyImageButton篇 1/4]

我的文章一定要对读者负责-否则不是好文章  ----       www.ayjs.net  aaronyang技术分享 文章导航: 介绍vs2013 WPF开发,属性代码相关技巧 实战AyImageButton 1.0细用慢讲,学会用户控件,依赖属性,属性回调事件 诞生AyImageButton 1.1 支持 控件简单写法,支持自定义AyImageButton写法,提供详细的API 效果图: AyImageButton记录 源码下载:http://pan.baidu.com/s/1eQlHly

[Aaronyang] 写给自己的WPF4.5 笔记13[二维自定义控件技巧-可视化状态实战,自定义容器,注册类命令,用户控件补充]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 博文摘要:欢迎大家来支持我的<2013-2015 Aaronyang的又一总结,牧童遥指纳尼村>绝对好文章 关于<写给自己的WPF4.5 笔记14,已在官网发布> 1.讲解了自定义控件加入命令支持的两种手段,补充用户控件的客户定义模板 2.实战的方式讲解了无外观控件,可以让使用者定义模板,讲解模板PART,使用可视化状态组,动画的使用 效果演示:

[Aaronyang] 写给自己的WPF4.5 笔记[2依赖属性]

人生的意义不在于拿一手好牌,而在于打好一手坏牌 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 后面重头戏就是blend中的开发,不想写的千篇一律.如果期待,左侧有关注按钮. 个人感觉,下面的这张图标比较重要,它或许有些帮助.我看东西只看分析出原理,你就可以拓三返一. Tip: 只

[Aaronyang] 写给自己的WPF4.5 笔记[1布局]

挫折时,要像大树一样,被砍了,还能再长:也要像杂草一样,虽让人践踏,但还能勇敢地活下去 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 复习曲线记录-布局 1. 了解Panel,继承Panel的WPF提供的一些核心布局控件 StackPanel.WrapPanel.DockPane

[Aaronyang] 写给自己的WPF4.5 笔记12[自定义控件-AyImageButton的过程 2/4]

 我的文章一定要做到对读者负责,否则就是失败的文章  ---------   www.ayjs.net    aaronyang技术分享 博文摘要:点击前往文章正文 学会怎样给用户提供事件接口,本例子中通过监视Dock的变化事件,让用户可以在dock变化时候,自定义下一步操作 学会如何使用C# 符合枚举,以及如何在Xaml中使用 实战AyImageButton1.2的诞生,增加纯图标或者纯文本,是否开启提示 版本 难DEMO:在第三步的基础上,增加 Dock变化和RenderMode变化的混搭

[Aaronyang] 写给自己的WPF4.5 笔记6[三巴掌-大数据加载与WPF4.5 验证体系详解 2/3]

我要做回自己--Aaronyang的博客(www.ayjs.net) 博客摘要: Virtualizing虚拟化DEMO 和 大数据加载的思路及相关知识 WPF数据提供者的使用ObjectDataProvider 和 XmlDataProvider WPF验证 第一:使用自带的属性SET抛出异常,前台捕捉到异常,描红 第二:我们可以自定义验证规则,替代刚开始的异常捕捉验证 第三:我们可以使用INotifyDataErrorInfo方式,增加异常,并实现了验证通知和还原非法值 第四:我们使用了Er

[Aaronyang] 写给自己的WPF4.5 笔记[3MenuItem中的icon]

敢于尝试,就等于你已经向成功迈出了第一步 --Aaronyang的博客(www.ayjs.net)-www.8mi.me =============时隔两年后再看WPF========== 因为以前的经验,所以继承FrameworkElement,我就简写继承FWE ,继承UIElement就写继承UIE 后面重头戏就是blend中的开发,不想写的千篇一律.如果期待,左侧有关注按钮. 1. v1.0菜单 新建WPF项目,右键项目-属性-资源          导入几个项目用到的图标 第一种:带图

[Aaronyang] 写给自己的WPF4.5 笔记19[Visual类图文并茂讲解]

文章虽小,内容还好,且看且珍惜. 当界面上使用数千个矢量图形,例如实时统计图,粒子碰撞,比如超级玛丽游戏,图像一直在绘,过量的使用WPF的元素系统和Shape类会使用程序变慢,所以我们需要使用Visual类手动进行渲染. Visual类是很多WPF元素的父类.所以掌握它当然很重要了. Visual的开销小于Geometry小于Path Visual作为抽象类,有UIElement这个子类,也有Viewport3DVisual类(3D知识中的) 放置Visual的对象的容器:ContainerVi