WinUI3项目异步方法修改ObservableProperty属性抛出COMException问题
WinUI3异步修改ObservableProperty触发COM异常的解决办法
在WinUI3项目里,要是在非UI线程的异步上下文(比如Task.Run启动的后台线程)中修改用CommunityToolkit.Mvvm包的[ObservableProperty]特性标记的属性,会抛出System.Runtime.InteropServices.COMException异常。
示例代码:
private void MyButton_ClickEvent(object sender, RoutedEventArgs args) { Task.Run(async () => { await AsyncTask(); ViewModel.MyProperty = "新值"; // 这里会抛出COM异常 }); }
问题原因
WinUI3的UI绑定必须在UI线程更新,而[ObservableProperty]自动生成的属性变更通知默认在当前线程触发,后台线程直接改属性属于跨线程操作UI绑定,自然触发异常。
解决办法
用Dispatcher切回UI线程
通过WinUI3的DispatcherQueue把属性修改操作转到UI线程执行:private void MyButton_ClickEvent(object sender, RoutedEventArgs args) { Task.Run(async () => { await AsyncTask(); // 切换到UI线程更新属性 await DispatcherQueue.TryEnqueue(() => { ViewModel.MyProperty = "新值"; }); }); }在ViewModel里处理线程切换
借助CommunityToolkit.Mvvm提供的MainThreadDispatcher,确保属性变更在UI线程触发:using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Dispatching; public partial class MyViewModel : ObservableObject { [ObservableProperty] private string myProperty; public async Task UpdatePropertyAsync() { await AsyncTask(); // 切换到UI线程更新属性 await MainThreadDispatcher.Default.EnqueueAsync(() => { MyProperty = "新值"; }); } }页面点击事件里直接调用ViewModel的方法就行:
private async void MyButton_ClickEvent(object sender, RoutedEventArgs args) { await ViewModel.UpdatePropertyAsync(); }用ConfigureAwait(true)保留UI上下文
如果异步操作不需要在后台线程执行,直接在UI线程await并保留上下文(ConfigureAwait(true)是默认行为,可显式写出),后续代码就会在UI线程运行:private async void MyButton_ClickEvent(object sender, RoutedEventArgs args) { await AsyncTask().ConfigureAwait(true); ViewModel.MyProperty = "新值"; // 此时在UI线程,不会抛异常 }注意:这种方法不适合
Task.Run包裹的场景,因为Task.Run会强制在后台线程执行,ConfigureAwait(true)也没法回到原UI上下文。
内容的提问来源于stack exchange,提问作者alejandro-ordonez
相关产品推荐
相关产品推荐

