当前位置: 首页 > news >正文

WPF--Application.Current.Dispatcher.BeginInvoke

1.代码示例

private void LogInfoList_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{// 直接在这里修改集合会引发递归if (e.Action == NotifyCollectionChangedAction.Add){if (logInfoList.Count > 200){logInfoList.RemoveAt(0);  // 这里会触发 CollectionChanged}}
}

修改为 `Dispatcher.BeginInvoke` 主要是为了避免在 `CollectionChanged` 事件处理程序中直接修改集合,导致递归调用的错误。`ObservableCollection` 会在修改时触发 `CollectionChanged` 事件,因此如果在事件处理程序中再次修改该集合,事件会被重新触发,这就可能导致无限递归或抛出错误。

具体原因和背景:

1. 避免递归调用:
   在 `CollectionChanged` 事件处理程序中直接修改集合会触发另一次 `CollectionChanged` 事件。这样就会进入递归调用的死循环,导致栈溢出或其他问题。

   
   例如,如果你在 `CollectionChanged` 中调用 `logInfoList.RemoveAt(0)`,这会导致 `logInfoList` 发生变化,从而再次触发 `CollectionChanged` 事件,导致程序再次执行该事件处理程序。通过 `Dispatcher.BeginInvoke` 延迟操作,我们确保修改集合的操作会等到事件处理程序执行完后再执行,从而避免递归触发事件。
2. UI 线程的调度: 
   `Dispatcher.BeginInvoke` 是用来在 UI 线程上执行任务的,它将操作推迟到下一个空闲时刻。即使你现在在处理事件,`Dispatcher.BeginInvoke` 也会让 `logInfoList.RemoveAt(0)` 的调用等到当前事件处理程序完成后执行。这样就不会发生集合修改时引发的事件触发问题。
工作原理:

-Application.Current.Dispatcher.BeginInvoke(new Action(() => { ... }))` 是一个异步方法,它会在 UI 线程的消息队列中排队,并在当前事件处理程序(如 `CollectionChanged`)完成后执行传入的操作。
- 这就确保了在执行 `RemoveAt(0)` 时不会再次触发 `CollectionChanged` 事件,从而避免了递归调用的错误。

例子说明:

假设你有一个 `logInfoList`,并且在 `CollectionChanged` 中添加了移除第一个元素的逻辑。如果直接在事件中调用 `RemoveAt(0)`,则会触发 `CollectionChanged`,导致无限循环:

private void LogInfoList_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{// 直接在这里修改集合会引发递归if (e.Action == NotifyCollectionChangedAction.Add){if (logInfoList.Count > 200){logInfoList.RemoveAt(0);  // 这里会触发 CollectionChanged}}
}

为了避免这个递归,我们通过 `Dispatcher.BeginInvoke` 延迟对集合的修改操作:

private void LogInfoList_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{if (e.Action == NotifyCollectionChangedAction.Add){// 使用 Dispatcher 来延迟移除第一个元素的操作Application.Current.Dispatcher.BeginInvoke(new Action(() =>{if (logInfoList.Count > 200){logInfoList.RemoveAt(0);  // 这时不会触发 CollectionChanged 事件}}));}
}

总结:
通过 `Dispatcher.BeginInvoke` 延迟集合的修改,能够避免直接在事件处理过程中修改集合引发的递归调用问题,确保代码能稳定运行。

事件递归问题补充:

问题核心:  
当 `RemoveAt(0)` 执行时,确实会触发一个新的 `CollectionChanged` 事件。由于使用了 `BeginInvoke` 延迟执行,所以这个新的事件处理程序会等当前的 `CollectionChanged` 事件处理完毕后才执行。那么,新的 `CollectionChanged` 事件在执行时会不会引发递归呢?

关键点:
1. 延迟执行的机制:
   使用 `Dispatcher.BeginInvoke` 延迟执行操作,它不会在当前事件处理过程中立刻执行。而是将操作推入到消息队列中,待当前事件处理结束之后再执行。因此,新的 `CollectionChanged` 事件的处理程序 **不会在当前事件处理中执行**,也就是说,新的事件处理程序不会在递归的调用栈中。

2. 新的事件处理是否会递归:
   当新的 `CollectionChanged` 事件处理程序执行时,它会触发一个新的事件,但是此时 **它已经不在当前事件的调用栈上。新的 `CollectionChanged` 事件是 **在当前事件完全结束后** 执行的,所以不会继续递归回到同一个事件处理程序。

3. 为什么不会递归:
   递归的前提是事件在处理过程中不断触发新的事件,进入同一个事件处理程序。由于我们通过 `BeginInvoke` 延迟执行操作,新的事件触发是在当前事件处理程序 **执行完后**,并且事件处理程序本身已经结束,所以 **新的事件不会重新进入相同的事件处理程序**,从而避免了递归

举个例子:

void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{Console.WriteLine("Collection changed!");// 延迟删除第一个元素Application.Current.Dispatcher.BeginInvoke(new Action(() =>{logInfoList.RemoveAt(0);  // 删除第一个元素}));
}

1. 第一次 `CollectionChanged` 事件触发,进入 `CollectionChangedHandler`。
2. 在事件处理中,`BeginInvoke` 将 `RemoveAt(0)` 延迟执行,操作被排入消息队列中。
3. 第一次 `CollectionChanged` 事件处理完毕后,`BeginInvoke` 中的 `RemoveAt(0)` 被执行,触发新的 `CollectionChanged` 事件**。
4. 这个新的事件处理程序不会立即进入当前的 `CollectionChangedHandler`,因为当前事件处理已经结束。新的事件会等到当前的所有操作都完成后再执行。
5. 由于新的事件已经不在当前事件处理程序的上下文中,它不会再递归触发。

 总结:
-新的 `CollectionChanged` 事件会被触发,但它的事件处理程序会在当前事件处理程序完全执行完毕后才开始。
由于新的事件处理程序不在当前的事件处理上下文中,它不会再次进入同一个事件处理程序**,从而避免了递归。
 

http://www.xdnf.cn/news/1018531.html

相关文章:

  • 在Jupyter Notebook中使用Conda虚拟环境
  • 使用 PyMuPDF 和 PySide6/PyQt6 编写的 PDF 查看器 (显示树状书签和缩略图列表,没有文字选择功能)
  • Monte Carlo衍生品定价(金融工程)
  • Spring Boot3流式访问Dify聊天助手接口
  • PHP语法基础篇(二):输出函数与字符串操作
  • 《第五章-心法进阶》 C++修炼生涯笔记(基础篇)指针与结构体⭐⭐⭐⭐⭐
  • 6月计算机新书:深度学习、大模型、DeepSeek
  • Blender 3D建模工具的快捷键总结--选择、视图、对象、编辑、UV贴图、模型材质、动画与渲染、工具
  • 238. 除自身以外数组的乘积
  • Linux运维-ansible-python开发-获取inventroy信息
  • 第二十五章 25.Network Architecture(CCNA)
  • 简析MDM在餐饮设备中的部署与应用
  • 快速掌握Django框架设计思想(图解版)
  • java_oss_微信小程序_通过临时签名url访问oss中存储的图像
  • 微信小程序中跨页面调用函数来刷新页面
  • 深入理解JavaScript设计模式之策略模式
  • @Profile, @Conditional, @ConditionalOnMissingBean, @ConditionalOnClass
  • nodejs 语言特性(面试系列2)
  • 【Pandas】pandas DataFrame droplevel
  • java中跨域问题及解决方案
  • Spring XML 常用命名空间配置
  • React Native 项目实战 —— 记账本应用开发指南
  • 【React Native 性能优化:虚拟列表嵌套 ScrollView 问题全解析】
  • Java-数组-异常(基础)
  • 包含40个购物网站UI界面的psd适用于电商项目
  • 在 Linux 系统中通过 yum 安装 Sublime Text
  • 平压印刷机设计原理与关键技术研究
  • 网络安全防护:点击劫持
  • Linux 系统设置时区
  • Token 的流动性:为什么它是项目的关键?