WINUI——CommunityToolkit.Mvvm Messenger接收消息时报错:Cannot access a disposed object.

背景

WINUI开发时使用CommunityToolkit.Mvvm的Messemger让UI展示一些信息时出现错误:

System.ObjectDisposedException:"Cannot access a disposed object.

ObjectDisposed_ObjectName_Name"

详细见下述截图:

开发环境

WIN11

WINUI3

CommunityToolkit.Common 7.1.2

原因探究

AI给出的产生原因

在 C# 中使用 WeakReferenceMessenger(或者类似的弱引用消息传递机制)时,遇到 System.ObjectDisposedException 异常通常意味着你尝试访问了一个已经被释放(disposed)的对象。在 MVVM 架构中,这通常发生在视图模型(ViewModel)或视图(View)的生命周期管理不当的情况下。

在 WinUI(或任何 UI 框架)中,当视图(或页面)被卸载或关闭时,与其关联的视图模型(或数据上下文)可能也会被释放。如果你在这些对象被释放后仍然尝试通过消息传递机制(如 WeakReferenceMessenger)与它们通信,就可能会遇到 ObjectDisposedException

为了解决这个问题,你需要在适当的时候注销(Unregister)你的消息接收器。在 WinUI 中,这通常意味着在视图或视图模型被释放之前,你需要调用 Unregister 方法来取消注册你的消息处理器。

以下是一些可能的解决方案:

  1. 在视图模型中注销

    在视图模型的 OnNavigatedFrom 方法(如果你使用的是导航框架)或相应的生命周期事件中,确保调用 Unregister 方法来取消注册消息处理器。

  2. 使用 IDisposable 接口

    实现 IDisposable 接口并在 Dispose 方法中注销消息处理器。这样,当视图模型被垃圾回收或显式释放时,消息处理器也会被自动注销。

  3. 使用弱引用

    虽然 WeakReferenceMessenger 已经使用了弱引用来减少内存泄漏的风险,但确保你的消息处理器不会持有对即将被释放对象的强引用也是很重要的。

  4. 检查消息传递的时机

    确保在视图或视图模型仍然有效时发送消息。如果可能的话,避免在视图或视图模型的生命周期之外发送消息。

  5. 使用调试工具

    使用调试工具(如 Visual Studio 的诊断工具)来跟踪对象的生命周期和内存使用情况,这有助于识别何时何地对象被释放以及为何会出现 ObjectDisposedException

  6. 查看文档和示例

    查阅 MVVM Toolkit 的文档和示例代码,看看是否有关于如何正确使用 WeakReferenceMessenger 的最佳实践和推荐做法。

以上是AI给出的一些建议,至于它说提到的原因,更觉得是有些扯淡。因为消息接收是在Page页直接接收相应的消息,同时将接收到的文本显示在Page上的Button上,而这个Page正是当前在使用的页面,那么也就是说不存在 它所说的访问了一个已经被释放(disposed)的对象

猜想:可能的真实原因

此很可能是由于Toolkit的bug或WINUI的Bug所致,但更大可能是Toolkit的bug导致了上述问题,因为在后续的测试中,假设是由于对象被释放时产生的问题,那么Messenger还在接收信息,那么肯定是会出现Cannot access a disposed object.(不能进入释放对象)这个错误的。然Page是肯定没有释放的,那么对象没有释放,会不会是它注册到Messenger内的对象除了当前的这个Page还有一个之前的当前Page呢?而之前的当前Page在再次进入这个Page时肯定是会被回收了,而Messenger内的未被回收,于是就可能导致这个问题的产生......

解决方法

虽然否定了AI给出的解释,但是它提到的:

在 WinUI(或任何 UI 框架)中,当视图(或页面)被卸载或关闭时,与其关联的视图模型(或数据上下文)可能也会被释放。如果你在这些对象被释放后仍然尝试通过消息传递机制(如 WeakReferenceMessenger)与它们通信,就可能会遇到 ObjectDisposedException

按上述说法,也就是说只要将注册的Messenger取消注册即可。

注册与取消注册

在VM/发送位置中注册:

WeakReferenceMessenger.Default.Register<string, string>(this, $"token", (r, msg) => YourAction);

Page/接收位置取消注册:

WeakReferenceMessenger.Default.Unregister<string, string>(this, $"token");

按上述操作操作后,再重新生成整个解决方案,确实是解决了问题。

也就是原来的猜想是有点靠谱。先留下此坑,后续待完全弄懂此问题原因,再更新或重开文章说明真正的原因,以解决此困惑,否则它将成为手中刺一样,不时让人痛苦一下,很是让人难受。

相关推荐
神仙别闹16 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
向宇it10 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo10 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC11 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf202311 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
直裾12 小时前
Scala全文单词统计
开发语言·c#·scala
ZwaterZ13 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
ZwaterZ15 小时前
el-table-column自动生成序号&&在序号前插入图标
前端·javascript·c#·vue
SRC_BLUE_1718 小时前
SQLI LABS | Less-55 GET-Challenge-Union-14 Queries Allowed-Variation 2
oracle·c#·less
yngsqq19 小时前
037集——JoinEntities连接多段线polyline和圆弧arc(CAD—C#二次开发入门)
开发语言·c#·swift