文章目录
- 前言
- 相关链接
- 代码仓库
- 项目配置
- 代码
- 初始代码
- 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随时都有可能发生修改。要么就是每次事件之后修改,要么就给每个可能会触发的属性添加通知。