You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Uno Platform Cat API示例Windows端Dispatcher为空问题求助

Uno Platform中Dispatcher为空的问题修复

我正在实现Uno Platform的Cat API示例,在以下代码中遇到Dispatcher为空的问题:WASM版本运行正常,但Windows版本无法工作。试过相关解决方案,仅生效一次后Dispatcher再次为空。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.UI.Dispatching;

namespace TheCatApiClient.Models.ViewModels
{
    public abstract class DispatchedBindableBase : INotifyPropertyChanged
    {
        // Insert variables below here
        protected DispatcherQueue Dispatcher => DispatcherQueue.GetForCurrentThread();

        // Insert variables below here
        public event PropertyChangedEventHandler? PropertyChanged;

        // Insert SetProperty below here
        protected virtual bool SetProperty<T>(ref T backingVariable, T value, [CallerMemberName] string? propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingVariable, value)) return false;

            backingVariable = value;
            RaisePropertyChanged(propertyName);

            return true;
        }

        // Insert RaisePropertyChanged below here
        protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
        {
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
            DispatchAsync(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
        }

        // Insert DispatchAsync below here
        protected async Task DispatchAsync(DispatcherQueueHandler callback)
        {
            var hasThreadAccess =
#if __WASM__
        true;
#else
            Dispatcher.HasThreadAccess;
#endif

            if (hasThreadAccess)
            {
                callback.Invoke();
            }
            else
            {
                var completion = new TaskCompletionSource();
                Dispatcher.TryEnqueue(() =>
                {
                    callback();
                    completion.SetResult();
                });
                await completion.Task;
            }
        }
    }
}

问题原因

当前代码中Dispatcher属性每次访问都会调用DispatcherQueue.GetForCurrentThread(),如果ViewModel在非UI线程初始化,或者后续在非UI线程访问该属性,都会返回null;之前的临时解决方案只覆盖了单次场景,无法解决后续的线程访问问题。

修复方案

1. 缓存Dispatcher到私有字段(推荐)

在ViewModel构造时获取并缓存UI线程的Dispatcher,避免每次访问都重新获取:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.UI.Dispatching;

namespace TheCatApiClient.Models.ViewModels
{
    public abstract class DispatchedBindableBase : INotifyPropertyChanged
    {
        private readonly DispatcherQueue _dispatcher;

        protected DispatcherQueue Dispatcher => _dispatcher;

        public event PropertyChangedEventHandler? PropertyChanged;

        protected DispatchedBindableBase()
        {
            // 确保ViewModel在UI线程初始化,否则抛出异常提示
            _dispatcher = DispatcherQueue.GetForCurrentThread() 
                ?? throw new InvalidOperationException("ViewModel必须在UI线程实例化");
        }

        protected virtual bool SetProperty<T>(ref T backingVariable, T value, [CallerMemberName] string? propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingVariable, value)) return false;

            backingVariable = value;
            RaisePropertyChanged(propertyName);

            return true;
        }

        protected void RaisePropertyChanged([CallerMemberName] string? propertyName = null)
        {
            // 忽略异步警告,无需等待分发完成
            _ = DispatchAsync(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)));
        }

        protected async Task DispatchAsync(DispatcherQueueHandler callback)
        {
            var hasThreadAccess =
#if __WASM__
                true;
#else
                _dispatcher.HasThreadAccess;
#endif

            if (hasThreadAccess)
            {
                callback.Invoke();
            }
            else
            {
                var completion = new TaskCompletionSource();
                _dispatcher.TryEnqueue(() =>
                {
                    callback();
                    completion.SetResult();
                });
                await completion.Task;
            }
        }
    }
}

2. 确保ViewModel在UI线程实例化

在页面的构造函数或Loaded事件中初始化ViewModel,保证操作在UI线程执行:

public MainPage()
{
    this.InitializeComponent();
    // 此处为UI线程,初始化ViewModel
    DataContext = new YourCatViewModel();
}

3. 备选:手动传入Dispatcher(适合非UI线程初始化场景)

如果必须在后台线程创建ViewModel,可以从UI线程提前获取Dispatcher并传入ViewModel构造函数:

// 页面中(UI线程)
var uiDispatcher = DispatcherQueue.GetForCurrentThread();
DataContext = new YourCatViewModel(uiDispatcher);

// ViewModel构造函数修改
protected DispatchedBindableBase(DispatcherQueue dispatcher)
{
    _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}

内容的提问来源于stack exchange,提问作者Ace

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:15:03