2

To start with I'm from a WPF world where I'm used to using MVVM, however my knowledge isn't up to date with regards to the new .net API and WinUI.

I'm currently trying to create a new POC using UNO Platform (which is more or less WinUI api) and MVVM, however I'm currently stuck. The problem is that I'm firing an event in my model that the view model subscribes to. The event is running on a background thread so the UI will not get updated when the view model updates the properties from the event, due to the background thread. WPF's approach to get the UI thread isn't available.

The suggestion I found is to use DispatcherQueue.GetForCurrentThread(); in the constructor of the view model. However, I'm using microsofts IOC Microsoft.Extensions.Hosting to register the view model, so the constructor is called on a background thread and therefor having the DispatcherQueue.GetForCurrentThread(); to get the UI thread doesn't work. Another solution would be to store the dispatcherQueue in the App.cs, but that just doesn't seems like a great idea to access the dispatcherQueue through App.

So in short, how do I update the view models properties on the UI thread when receiving an event from the model ?

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
dennis_ler
  • 659
  • 1
  • 9
  • 36

2 Answers2

2

Another solution would be to store the dispatcherQueue in the App.cs, but that just doesn't seems like a great idea to access the dispatcherQueue through App

Why not? That's what you do in WPF when you use the Application.Current.Dispatcher API.

You could add this property to your App class:

public DispatcherQueue DispatcherQueue { get; } = DispatcherQueue.GetForCurrentThread();

...and then use the DispatcherQueue as usual:

Task.Run(async () =>
{
    var dispatcherQueue = (App.Current as App).DispatcherQueue;
    dispatcherQueue.TryEnqueue(() => {
        //set property...
    });
});

If you don't want to access the DispatcherQueue using the App class, you could of course inject it into your container like you would do with any other object. Just make sure that you initialize the DispatcherQueue on the UI thread.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thx. I was just hoping for a more decoupled way, but you are right it isn't far from in WPF. Injecting into the container would be nice, then I just need to wrap the DispatcherQueue as the IOC requires an interface as far as I see it. – dennis_ler Jun 23 '23 at 07:30
1

mm8's answer above is definitely one way to go, however different MVVM libraries have different built-in solutions to this problem.

For example, ReactiveUI allows you to specify the thread commands should run on:

var command = ReactiveCommand.Create(
    () => Console.WriteLine(Environment.CurrentManagedThreadId), 
    outputScheduler: RxApp.MainThreadScheduler
);

It also allows you to specify threads to use when subscribing to observable changes:

FetchStuffAsync()
  .ObserveOn(RxApp.MainThreadScheduler)
  .Subscribe(x => this.SomeViewModelProperty = x);

And it has built-in support for testing, where it creates test-only scheduling contexts:

new TestScheduler().With(sheduler => 
{
    // Code run in this block will have both RxApp.MainThreadScheduler
    // and RxApp.TaskpoolScheduler assigned to the new TestScheduler.
});
Blindy
  • 65,249
  • 10
  • 91
  • 131