Home > Software engineering >  DispatcherQueue null when trying to update Ui property in ViewModel
DispatcherQueue null when trying to update Ui property in ViewModel

Time:12-01

In a WinUI 3 in Desktop app I have a property to update which is bound to the ui via x:Bind.

I want to use the Dispatcher like I do in WPF to get on the UI thread and avoid the thread error im getting when I update the prop:

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

Im just not sure how to do it in WinUI 3, when I try

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

I get this error

enter image description here

DispatcherQueue.GetForCurrentThread() is null

I also tried:

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

but it wont compile:

enter image description here

I then found this GitHub issue, so I tried:

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

}, null);

This works but why can't I get onto the UI thread with the Dispatcher in my VM?

CodePudding user response:

DispatcherQueue.GetForCurrentThread() only returns a DispatcherQueue when being called on a thread that actually has a DispatcherQueue. If you call it on a background thread there is indeed no DispatcherQueue to be returned.

So the trick is to call the method on the UI thread and store the return value in a variable that you then use from the background thread, e.g.:

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));
    }
}
  • Related