文章目錄
- 前言
- 相關鏈接
- 代碼倉庫
- 項目配置
- 代碼
- 初始代碼
- View
- Person
- ViewModel
- 嘗試老辦法通知
- 解決方案
- ObservableCollection
- BindingList
- ICollectionView
- 總結
前言
我們這次詳細了解一下列表通知的底層是怎么實現的
相關鏈接
十月的寒流
MVVM實戰技巧之:可被觀測的集合(ObservableCollection & BindingList)
代碼倉庫
我為了方便展示源代碼,我將代碼提交到了代碼倉庫里面
B站【十月的寒流】對應課程的代碼 Github倉庫
項目配置
如何使用我這里就不展開說明了
WPF CommunityToolkit.Mvvm
WPF CommunityToolkit.Mvvm Messenger通訊
WPF-UI HandyControl 簡單介紹
WPF-UI HandyControl 控件簡單實戰+IconPacks矢量圖導入
Bogus,.NET生成批量模擬數據
代碼
初始代碼
View
<UserControl.DataContext><viewModels:DemoViewModel /></UserControl.DataContext><DockPanel><StackPanel DockPanel.Dock="Bottom"><Button Command="{Binding AddItemCommand}"Content="添加數據"></Button></StackPanel><DataGrid ItemsSource="{Binding People}"></DataGrid></DockPanel>
Person
public class Person
{public int Id { get; set; }public string FirstName { get; set; }public string LastName { get; set; }public string FullName { get; set; }public DateOnly BirthDay { get; set; }public static Person FakerOne => faker.Generate();public static IEnumerable<Person> FakerMany(int count)=>faker.Generate(count);private static readonly Faker<Person> faker = new Faker<Person>().RuleFor(t=>t.Id,f=>f.IndexFaker).RuleFor(t=>t.FirstName,f=>f.Name.FirstName()).RuleFor(t=>t.LastName,f=>f.Name.LastName()).RuleFor(t=>t.FullName,f=>f.Name.FullName()).RuleFor(t=>t.BirthDay,f=>f.Date.BetweenDateOnly(new DateOnly(1990,1,1),new DateOnly(2010,1,1)));
}
ViewModel
public partial class DemoViewModel:ObservableObject
{[ObservableProperty]private List<Models.Person> people = new List<Models.Person>();[RelayCommand]public void AddItem(){People.Add(Models.Person.FakerOne);}public DemoViewModel() {People = Models.Person.FakerMany(5).ToList();}}
現在的代碼是沒有實現通知,點擊按鈕也不會添加
嘗試老辦法通知
public void AddItem(){People.Add(Models.Person.FakerOne);//沒有效果//OnPropertyChanged(nameof(People));//沒有效果//SetProperty(ref people, people);}
而且在我們點擊ListBox的時候,會報錯。這個就說明,其實List已經修改了,但是這個通知方法不行。原因是List指向的是一個地址空間,這個地址空間并沒有變化。
解決方案
ObservableCollection
簡單的解決方案就是改成ObservableCollection,這里就不展開說明了。
但是有一個問題,這個ObservableCollection只在Count更新的時候觸發自動更新。里面的Person值修改的時候是不會觸發更新的。
如果有聯動更新的需求,可以直接在【CollectionChanged】添加對應的代碼
BindingList
這里我就不展開說明了,直接上視頻的源代碼了。
ICollectionView
WPF 【十月的寒流】學習筆記(1):DataGrid過濾
更好的解決方案就是直接更新。我們直接刷新ItemSorce
<UserControl x:Class="WpfMvvmDemo.Views.DemoView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:WpfMvvmDemo.Views"xmlns:viewModels="clr-namespace:WpfMvvmDemo.ViewModels"mc:Ignorable="d"d:DesignHeight="450"d:DesignWidth="800"><UserControl.DataContext><viewModels:DemoViewModel /></UserControl.DataContext><DockPanel><StackPanel DockPanel.Dock="Bottom"HorizontalAlignment="Left"Orientation="Horizontal"><Button Command="{Binding AddItemCommand}"Margin="5"Content="添加數據"></Button><Button Command="{Binding UpIdCommand}"Margin="5"Content="增加Id"></Button></StackPanel><DataGrid ItemsSource="{Binding PeopleView}"></DataGrid></DockPanel>
</UserControl>
using Bogus;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;namespace WpfMvvmDemo.ViewModels
{public partial class DemoViewModel:ObservableObject{[ObservableProperty]private List<Models.Person> people = new List<Models.Person>();[ObservableProperty]private ICollectionView peopleView;[RelayCommand]public void AddItem(){People.Add(Models.Person.FakerOne);//沒有效果//OnPropertyChanged(nameof(People));//沒有效果//SetProperty(ref people, people);//直接更新整個視圖源PeopleView.Refresh();}[RelayCommand]public void UpId(){foreach (var item in People){item.Id += 10;}PeopleView.Refresh();}public DemoViewModel() {People = Models.Person.FakerMany(5).ToList();PeopleView = CollectionViewSource.GetDefaultView(People);}}
}
為了方便,我們也可以直接新建一個類,這里就把代碼放一下,就不展開說明了
public class CollectionData<T> where T : class{public IEnumerable<T> Data { get; set; }public ICollectionView CollectionView { get; set; }public CollectionData() { }public void Init(){CollectionView = CollectionViewSource.GetDefaultView(Data);CollectionView.Refresh();}}
總結
我覺得當時【十月的寒流】那個視頻一直在想用MVVM去通知更新,當然他的主題也是使用MVVM自動更新。但是ItemSorce隨時都有可能發生修改。要么就是每次事件之后修改,要么就給每個可能會觸發的屬性添加通知。