尝试在 ViewModel 中更新 Ui 属性 时 DispatcherQueue 为 null

DispatcherQueue null when trying to update Ui property in ViewModel

在 WinUI 3 桌面应用程序中,我有一个 属性 要更新,它通过 x:Bind.

绑定到 ui

我想像在 WPF 中一样使用 Dispatcher 进入 UI 线程并避免在更新 prop 时出现线程错误:

System.Runtime.InteropServices.COMException: 'The application called an interface that was marshalled for a different thread. (0x8001010E (RPC_E_WRONG_THREAD))'

我只是不确定如何在 Win 中执行此操作UI 3,当我尝试时

DispatcherQueue.GetForCurrentThread().TryEnqueue(() =>
{
    AeParty.OnSyncHub = false; // Prop bound in ui using x:Bind
});

我收到这个错误

DispatcherQueue.GetForCurrentThread() 为空

我也试过:

this.DispatcherQueue.TryEnqueue(() =>
{
    AeParty.OnSyncHub = false;
});

但无法编译:

然后我发现 this GitHub 问题,所以我尝试了:

SynchronizationContext.Current.Post((o) =>
{
    AeParty.OnSyncHub = false;

}, null);

这行得通,但为什么我不能在我的 VM 中使用 Dispatcher 进入 UI 线程?

DispatcherQueue.GetForCurrentThread() 在实际具有 DispatcherQueue 的线程上调用时仅 return 是 DispatcherQueue。如果你在后台线程上调用它,确实没有 DispatcherQueue 被 returned.

所以诀窍是在 UI 线程上调用该方法并将 return 值存储在一个变量中,然后您可以从后台线程使用该变量,例如:

public sealed partial class MainWindow : YourBaseClass
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    public ViewModel ViewModel { get; } = new ViewModel();
}

public class ViewModel : INotifyPropertyChanged
{
    private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();

    public ViewModel()
    {
        Task.Run(() => 
        {
            for (int i = 0; i < 10; i++)
            {
                string val = i.ToString();
                _dispatcherQueue.TryEnqueue(() =>
                {
                    Text = val;
                });
                Thread.Sleep(2000);
            }
        });

    }
    private string _text;
    public string Text
    {
        get { return _text; }
        set { _text = value; NotifyPropertyChanged(nameof(Text)); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}