WinUI MVVM中如何在UI线程处理模型事件以更新视图模型属性?
解决UNO Platform中后台线程事件更新UI线程属性的问题
方案1:通过依赖注入注入UI线程的DispatcherQueue
由于你使用了Microsoft.Extensions.Hosting,可以在UI线程初始化阶段将UI线程的DispatcherQueue注册为单例服务,让视图模型通过构造函数注入获取正确的调度器:
- 在
App.xaml.cs的启动逻辑(比如OnLaunched方法或Host初始化代码)中添加注册:
var host = Host.CreateDefaultBuilder() .ConfigureServices(services => { // 关键:在UI线程执行此代码,注册当前线程的DispatcherQueue services.AddSingleton(DispatcherQueue.GetForCurrentThread()); // 注册你的视图模型 services.AddTransient<MyViewModel>(); }) .Build();
- 在视图模型中注入并使用DispatcherQueue:
public class MyViewModel : ObservableObject { private readonly DispatcherQueue _uiDispatcher; private readonly MyModel _model; public MyViewModel(DispatcherQueue uiDispatcher, MyModel model) { _uiDispatcher = uiDispatcher; _model = model; _model.BackgroundEvent += OnModelBackgroundEvent; } private string _myProperty; public string MyProperty { get => _myProperty; set => SetProperty(ref _myProperty, value); } private void OnModelBackgroundEvent(object sender, EventArgs e) { // 使用UI线程的DispatcherQueue调度属性更新 _uiDispatcher.TryEnqueue(() => { MyProperty = "从后台事件更新的新值"; }); } }
方案2:使用MVVM Toolkit的Messenger指定UI线程调度
如果使用CommunityToolkit.Mvvm(UNO开发推荐的MVVM库),可以通过WeakReferenceMessenger自动切换到UI线程处理消息,避免直接操作DispatcherQueue:
- 定义消息类:
public class ModelUpdatedMessage : ValueChangedMessage<string> { public ModelUpdatedMessage(string newValue) : base(newValue) { } }
- 模型在后台线程发送消息:
public class MyModel { public void DoBackgroundWork() { Task.Run(() => { // 模拟耗时操作 Thread.Sleep(1000); WeakReferenceMessenger.Default.Send(new ModelUpdatedMessage("后台处理完成")); }); } }
- 视图模型注册消息时指定UI线程调度:
public class MyViewModel : ObservableObject, IRecipient<ModelUpdatedMessage> { private readonly DispatcherQueue _uiDispatcher; private string _myProperty; public string MyProperty { get => _myProperty; set => SetProperty(ref _myProperty, value); } public MyViewModel(DispatcherQueue uiDispatcher) { _uiDispatcher = uiDispatcher; // 注册消息时指定使用UI线程的DispatcherQueue调度 WeakReferenceMessenger.Default.Register<ModelUpdatedMessage>(this, _uiDispatcher, (recipient, message) => { recipient.MyProperty = message.Value; }); } }
关键注意点
- 必须确保
DispatcherQueue.GetForCurrentThread()在UI线程执行,否则注册的会是后台线程的调度器,无法更新UI。 - 优先使用
TryEnqueue而非Enqueue,避免因UI线程阻塞导致异常,TryEnqueue会在调度失败时返回false,方便做容错处理。
内容的提问来源于stack exchange,提问作者dennis_ler
相关产品推荐
相关产品推荐

