WPF 之 ObservableCollection 动态数据集合的使用(二)

一、 ObservableCollection 动态数据集合

在许多情况下,我们使用的数据是对象的集合。 例如,数据绑定中的常见方案是使用 ItemsControl ListBoxListView 或等 TreeView 来显示记录集合。

但是,若要设置动态绑定,以便集合中的插入或删除操作 UI 自动更新,该集合必须实现 INotifyCollectionChanged 接口。 此接口需要公开 CollectionChanged 事件,即每当基础集合发生更改时应引发的事件。

WPF 提供 ObservableCollection 类,该类是实现接口的数据集合的内置实现 INotifyCollectionChanged 。它表示一个动态数据集合,它可在添加、删除项目或刷新整个列表时提供通知。

二、ObservableCollection 与 UI 的绑定

例如,我们实现一个如下需求:

(1)显示一个数据集,内容包括学生的姓名、年龄、所属班级信息;(2)能够对该数据集进行增加、删除、移动、清空等操作。具体 UI 如下图所示:

img

我们先声明一个 Model,用于保存学生信息,再声明一个 ViewModel ,用于 Model 与 UI 之间进行信息同步(具体绑定步骤见 WPF 之 INotifyPropertyChanged 接口的使用 (一)):

 1     /// <summary>
 2     /// Model
 3     /// </summary>
 4     public class Student
 5     {
 6         public string Name { get; set; }
 7         public long Age { get; set; }
 8         public string Grades { get; set; }
 9     }
10     
11     /// <summary>
12     /// ViewModel
13     /// </summary>
14     public class Window2VM:NotifyProperty
15     {
16         private ObservableCollection<Student> _nameCollection;
17 
18         public ObservableCollection<Student> NamesCollection
19         {
20             get => _nameCollection;
21             set => SetProperty(ref _nameCollection, value);
22         }
23 
24         public Window2VM()
25         {
26             NamesCollection = new ObservableCollection<Student>()
27             {
28                new Student(){Name = "dwayne",Age = 12,Grades = "7年级·3班",},
29                new Student(){Name = "john",Age = 15,Grades = "8年级·5班",},
30                new Student(){Name = "linq",Age = 14,Grades = "8年级·1班",},
31                new Student(){Name = "cool",Age = 11,Grades = "7年级·6班",},
32                new Student(){Name = "team",Age = 16,Grades = "9年级·3班",},
33             };
34         }
35     }

然后把该 ViewModel 绑定到 XAML 上

 1 <Window x:Class="UI.Window2"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6         xmlns:local="clr-namespace:UI"
 7         mc:Ignorable="d"
 8         d:DataContext="{d:DesignInstance local:Window2VM}"
 9         Title="Window2" Height="400" Width="500">
10     <StackPanel>
11        <UniformGrid Columns="2" Height="120">
12            <Button Margin="5" Content="Add" Click="ButtonAdd_OnClick"></Button>
13            <Button Margin="5" Content="Remove" Click="ButtonRemove_OnClick"></Button>
14            <Button Margin="5" Content="Move" Click="ButtonMove_OnClick"></Button>
15            <Button Margin="5" Content="Clear" Click="ButtonClear_OnClick"></Button>
16        </UniformGrid>
17         <DataGrid Margin="5" Height="200" ColumnWidth="150" ItemsSource="{Binding NamesCollection}"></DataGrid>
18     </StackPanel>
19 </Window>

增删操作代码:

 1     /// <summary>
 2     /// Window2.xaml 的交互逻辑
 3     /// </summary>
 4     public partial class Window2 : Window
 5     {
 6         private Window2VM vm;
 7         public Window2()
 8         {
 9             InitializeComponent();
10 
11             this.DataContext=vm=new Window2VM();
12         }
13 
14 
15         private void ButtonAdd_OnClick(object sender, RoutedEventArgs e)
16         {
17 
18             vm.NamesCollection.Add(new Student()
19             {
20                 Name=$"Test-{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()}",
21                 Age = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()/1000,
22                 Grades = $"{new Random().Next(7,10)}年级·{new Random().Next(1, 10)}班",
23             });
24         }
25 
26         private void ButtonRemove_OnClick(object sender, RoutedEventArgs e)
27         {
28             vm.NamesCollection.RemoveAt(0);
29         }
30 
31         private void ButtonMove_OnClick(object sender, RoutedEventArgs e)
32         {
33             vm.NamesCollection.Move(vm.NamesCollection.Count-1,0);
34         }
35 
36         private void ButtonClear_OnClick(object sender, RoutedEventArgs e)
37         {
38             vm.NamesCollection.Clear();
39         }
40     }

三、ObservableCollection 的 CollectionChanged 事件

如果我们增加一个搜素功能,即输入学生姓名或年龄或班级,自动显示相关的集合,如下图所示:

img

我们可以一个集合用来保存学生信息,一个集合用来显示,同时当保存学生信息的集合发生变化时,显示信息的集合要同步变化,这就需要用到ObservableCollection 的 CollectionChanged 事件,具体实现如下:

 1     /// <summary>
 2     /// ViewModel
 3     /// </summary>
 4     public class Window2VM:NotifyProperty
 5     {
 6         private ObservableCollection<Student> _displayName;
 7         private string _searchContent;
 8 
 9         /// <summary>
10         /// 保存学生信息的集合
11         /// </summary>
12         public ObservableCollection<Student> NamesCollection { get; set; }
13 
14         /// <summary>
15         /// 展示学生信息的集合
16         /// </summary>
17         public ObservableCollection<Student> DisplayName
18         {
19             get => _displayName;
20             set => SetProperty(ref _displayName, value);
21         }
22 
23         /// <summary>
24         /// 搜索内容
25         /// </summary>
26         public string SearchContent
27         {
28             get => _searchContent;
29             set => SetProperty(ref _searchContent, value);
30         }
31 
32         public Window2VM()
33         {
34             _searchContent = "";
35             _displayName = new ObservableCollection<Student>();
36             NamesCollection = new ObservableCollection<Student>();
37 
38             // 同步集合
39             NamesCollection.CollectionChanged += (sender, e) =>
40             {
41                 {
42                     switch (e.Action)
43                     {
44                         case NotifyCollectionChangedAction.Add:
45                             var items = (object[])(e.NewItems.SyncRoot);
46                             foreach (var item in items)
47                             {
48                                 _displayName.Add(item as Student);
49                             }
50                             break;
51                         case NotifyCollectionChangedAction.Remove:
52                             _displayName.RemoveAt(e.OldStartingIndex);
53                             break;
54                         case NotifyCollectionChangedAction.Reset:
55                             _displayName.Clear();
56                             break;
57                         case NotifyCollectionChangedAction.Move:
58                             _displayName.Move(e.OldStartingIndex, e.NewStartingIndex);
59                             break;
60                         case NotifyCollectionChangedAction.Replace:
61                             break;
62                     }
63                 };
64             };
65             NamesCollection.Add(new Student() { Name = "dwayne", Age = 12, Grades = "7年级·3班", });
66             NamesCollection.Add(new Student() { Name = "john", Age = 15, Grades = "8年级·5班", });
67             NamesCollection.Add(new Student() { Name = "linq", Age = 14, Grades = "8年级·1班", });
68             NamesCollection.Add(new Student() { Name = "cool", Age = 11, Grades = "7年级·6班", });
69             NamesCollection.Add(new Student() { Name = "team", Age = 16, Grades = "9年级·3班", });
70         }
71     }

搜索功能,我们可以优化一下,当搜索文本框内容发生变化时,显示集合自动发生变更,先添加 XAML代码如下:

1         <StackPanel Orientation="Horizontal" Height="50">
2             <TextBox Margin="5" Width="360" BorderBrush="Black" VerticalContentAlignment="Center" Text="{Binding Path=SearchContent,UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBoxBase_OnTextChanged"></TextBox>
3             <Button Margin="5" Width="100" Content="Search" Click="ButtonSearch_OnClick"></Button>
4         </StackPanel>

后台代码如下:

 1         private void ButtonSearch_OnClick(object sender, RoutedEventArgs e)
 2         {
 3             SearchCommand();
 4         }
 5 
 6         private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
 7         {
 8             SearchCommand();
 9         }
10 
11         private void SearchCommand()
12         {
13             vm.DisplayName = new ObservableCollection<Student>(vm.NamesCollection.Where(student =>
14                 (student.Name.Contains(vm.SearchContent) || student.Age.ToString().Contains(vm.SearchContent) ||
15                  student.Grades.Contains(vm.SearchContent))).ToList());
16         }
原文地址:https://www.cnblogs.com/dongweian/p/14361943.html