WPF 解决 ObservableCollection 提示 Cannot change ObservableCollection during a CollectionChanged event 异常
本文告诉大家在使用 ObservableCollection 时,抛出 InvalidOperationException 异常,提示 Cannot change ObservableCollection during a CollectionChanged event 内容,的原因和解决方法
准确来说,这个异常和 WPF 是没有任何关系的。这个异常是 ObservableCollection 类型抛出的,而 ObservableCollection 类型是在 dotnet runtime 定义的,放在 System.ObjectModel 里,而且此异常可以在除 WPF 的其他框架,比如控制台或者 UWP 上复现
想要解决此问题,还请先了解一下此异常抛出的原因
在 ObservableCollection 的设计上,是可以了解列表的变更。而在列表的变更了解,是通过 CollectionChanged 事件实现。然而事件的触发,稍微了解 C# 语法的开发者都知道,是每个方法独立执行。这就让 ObservableCollection 存在一个设计上需要解决的问题,那就是如果事件 CollectionChanged 被加等两次,意味着有两次方法的调用。如果在第一次调用方法时,在此方法内再次修改了 ObservableCollection 列表的元素,那么将会让第二个方法进入的时候,所获取的状态和第一个方法所获取的一定不相同
这个设计上的问题,是很难解决的。既然很难解决,那就不解决了,将问题交给开发者好了,在 ObservableCollection 判断如果 CollectionChanged 事件被加等大于 1 次,同时在事件触发的过程中,进行集合的变更,将会抛出 InvalidOperationException 异常,提示 Cannot change ObservableCollection during a CollectionChanged event 内容
这就是从设计上的原因。那为什么只加等 1 次时不抛出呢?那是因为既然只有一次,那改不改都影响不了当前的进入的方法的状态
由于 CollectionChanged 事件加等的次数决定了 InvalidOperationException 是否抛出,从而让一些开发者拿到错误的结论: 在 CollectionChanged 事件里面修改集合本身是安全的。或者反过来,在 CollectionChanged 事件里面修改集合本身是不安全的
正确的行为是: 当 CollectionChanged 事件加等的委托在 1 个以内时,在 CollectionChanged 事件里面修改集合本身是安全的。如果 CollectionChanged 事件加等的委托大于 1 个时,在 CollectionChanged 事件里面修改集合本身是不安全的
从代码上,在 ObservableCollection 的各个更改集合的函数,例如 InsertItem ClearItems RemoveItem 等,都会调用 CheckReentrancy 方法,判断是否存在重入。在 CheckReentrancy 方法的实现如下
/// <summary> Check and assert for reentrant attempts to change this collection. </summary>
/// <exception cref="InvalidOperationException"> raised when changing the collection
/// while another collection change is still being notified to other listeners </exception>
protected void CheckReentrancy()
{
if (_blockReentrancyCount > 0)
{
// we can allow changes if there's only one listener - the problem
// only arises if reentrant changes make the original event args
// invalid for later listeners. This keeps existing code working
// (e.g. Selector.SelectedItems).
if (CollectionChanged?.GetInvocationList().Length > 1)
throw new InvalidOperationException(SR.ObservableCollectionReentrancyNotAllowed);
}
}
上面代码的 _blockReentrancyCount
是在 OnCollectionChanged 方法和 BlockReentrancy 方法使用的。在没有重写 ObservableCollection 的情况下,可以认为 _blockReentrancyCount
只有在 OnCollectionChanged 方法更改
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler? handler = CollectionChanged;
if (handler != null)
{
// Not calling BlockReentrancy() here to avoid the SimpleMonitor allocation.
_blockReentrancyCount++;
try
{
handler(this, e);
}
finally
{
_blockReentrancyCount--;
}
}
}
也就是说,在 CollectionChanged 事件触发进入的方法里面,一定是判断 if (_blockReentrancyCount > 0)
通过的。也就说接下来只需要看 if (CollectionChanged?.GetInvocationList().Length > 1)
判断即可。这里的 GetInvocationList 是 CollectionChanged 事件对应的委托的数量,只要超过 1 个就炸
了解了原因,那么解决方法也很简单。要么是在 CollectionChanged 事件里面修改集合时确保只让 CollectionChanged 加等一个委托。要么就是继承 ObservableCollection 类型,重写 OnCollectionChanged 方法,不要修改 _blockReentrancyCount
字段。要么就是等待 CollectionChanged 事件触发完成之后,通过 Dispatcher
的 InvokeAsync 方法调度出去执行
WPF 解决 ObservableCollection 提示 Cannot change ObservableCollection during a CollectionChanged event 异常的更多相关文章
- WPF编译时提示“...不包含适合于入口点的静态‘Main’方法 ...”
今天看了一下wpf的Application类方面的知识,一个windows应用程序由一个Application类的实例表示,该类跟踪在应用程序中打开的所有窗口,决定何时关闭应用程序(属性 Shutdo ...
- UWP ObservableCollection<Evaluate>集合中ObservableCollection<PictureInfo>变更通知到xaml界面
ObservableCollection<Evaluate> EvaluateList = new ObservableCollection<Evaluate>();//评论集 ...
- WPF解决界面全屏化但不遮挡任务栏的问题
原文:WPF解决界面全屏化但不遮挡任务栏的问题 学习C#有一段时间了,现在跟着做项目,碰到有个客户端界面总是全屏,对于客户来说没有任务栏很不习惯,所以做了些略微的修改 </pre>&l ...
- WPF解决按钮上被透明控件遮盖时无法点击问题
原文:WPF解决按钮上被透明控件遮盖时无法点击问题 IsHitTestVisible="False" 在控件上设置如上属性即可,即可让透明控件不触发点击效果
- 解决maven项目Cannot change version of project facet Dynamic web module to 3.0/3.1
解决maven项目Cannot change version of project facet Dynamic web module to 3.0 1.打开项目所在目录下的.settings文件夹 打 ...
- WPF编译时提示"xxx不包含适合于入口点的静态 Main方法xxx"
WPF编译时提示"xxx不包含适合于入口点的静态 Main方法xxx"生成的时候一直报"xxx不包含适合于入口点的静态 Main 方法xxx" 看到这个问题首先 ...
- 解决PowerDesigner提示This data item is already used in a primary identifier
解决PowerDesigner提示This data item is already used in a primary identifier 解决PowerDesigner提示This data i ...
- WPF 解决无边框产生的相关问题
原文:WPF 解决无边框产生的相关问题 最大化: 在设置WindowStyle="None" AllowsTransparency="True"后,最大化会覆盖 ...
- 【WPF】WPF中的List<T>和ObservableCollection<T>
在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...
- 【转】关于编写WPF UserControl时提示The name 'InitializeComponent' does not exist in the current contextr的解决!
1.打开.csproj(工程)文件. 2.找到<Import Project="$(MSBuildBinPath)/Microsoft.CSharp.targets" /&g ...
随机推荐
- uni学习笔记分享
目录介绍 01.遇到问题汇总 02.关于布局设置 03.基础语法总结 04.关于交互问题 06.关于回传数据 07.关于网络请求 08.关于页面刷新 09.关于注意问题 10.待解决和思考 01.遇到 ...
- 【福利】JetBrains 全家桶永久免费使用
Jetbrains系列的IDE公认是最好的集成开发工具,但是收费且挺贵.我们以PhpStorm为例,新用户第一年需要199$,注意是$,还不是人民币,这个价格一上来肯定筛选掉一大批用户.确实好用,所以 ...
- Excel多列求平均值
数据源如下所示 需求描述:现在需要根据水果名称和月份求出对应水果的平均单价 一.添加一列辅助列(=A2&B2) 二.Ctrl+A选择所有数据插入数据透视表,选择辅助列到行,客单价到值(值字段设 ...
- execute immediate 用法小结
1.常规用法 v_sql varchar2(1000); v_sql := 'update Test set name= ''lw112190'' where id= 1'; execute imme ...
- windows系统cmd切换盘符路径命令失效
问题描述:比如当我在C盘想切换到D盘的某个文件夹路径下时 只是输出了那个路径 但是并没有真的切换 这时候需要再多操作一步就会成功了
- HTTP Web安全
验证安全机制 会话管理机制 SQL注入原理 SELECT * FROM test.user WHERE username='' or 1='1' and password='anyxxxxx'; 当u ...
- Linux是什么与如何学习
重点回顾 操作系统(Operation System) 主要在管理与驱动硬件,因此必须要能够管理内存.管理装置. 负责行程管理以及系统呼叫等等.因此,只要能够让硬件准备妥当(Ready)的情况, 就是 ...
- Apache服务器打开网页是乱码解决方案
当 Apache 服务器显示乱码时,可以使用两种方法解决: 1. 服务器端 可以在 Apache 的配置文件中添加以下内容来设置默认编码为UTF-8: AddDefaultCharset utf-8 ...
- 如何自动申请免费的HTTPS证书?
在购买域名的时候我相信很多人都遇到了对于证书的问题,之前我也是使用阿里云的免费一年的证书,那时候感觉还好,一年更换一次,但是近期阿里云对于证书的过期时间直接砍到了三个月!让我难以接受,所以我在想吧他直 ...
- Python分组数据并保存到单独的文件中
当处理大型数据集时,通常需要将数据分组,并将每个分组的数据保存到单独的文件中.下面是一个使用 Python 中的 pandas 库来实现这一目标的示例代码. 步骤 1: 导入所需的库 import o ...