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
DispatcherQueue.GetForCurrentThread()
is null
I also tried:
this.DispatcherQueue.TryEnqueue(() =>
{
AeParty.OnSyncHub = false;
});
but it wont compile:
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));
}
}