WPF/Multithreading: UI Dispatcher in MVVM

2019-02-01 00:48发布

So say in an MVVM environment, I'm in a background thread and I'd like to run an update on a ui control. So normally I'd go myButton.Dispatcher.BeginInvoke(blabla) but I don't have access to myButton (because the viewmodel doesn't have access to the view's controls). So what is the normal pattern for doing this?

(I guess there's always binding, but I'd like to know how to do it via the dispatcher)

6条回答
干净又极端
2楼-- · 2019-02-01 01:23

From Caliburn Micro source code :

public static class Execute
{
    private static Action<System.Action> executor = action => action();

    /// <summary>
    /// Initializes the framework using the current dispatcher.
    /// </summary>
    public static void InitializeWithDispatcher()
    {
#if SILVERLIGHT
        var dispatcher = Deployment.Current.Dispatcher;
#else
        var dispatcher = Dispatcher.CurrentDispatcher;
#endif
        executor = action =>{
            if(dispatcher.CheckAccess())
                action();
            else dispatcher.BeginInvoke(action);
        };
    }

    /// <summary>
    /// Executes the action on the UI thread.
    /// </summary>
    /// <param name="action">The action to execute.</param>
    public static void OnUIThread(this System.Action action)
    {
        executor(action);
    }
}

Before using it you'll have to call Execute.InitializeWithDispatcher() from the UI thread then you can use it like this Execute.OnUIThread(()=>SomeMethod())

查看更多
Viruses.
3楼-- · 2019-02-01 01:23

I tend to have my ViewModels inherit from DependencyObject and ensure that they are constructed on the UI thread, which poises them perfectly to handle this situation - they have a Dispatcher property that corresponds to the UI thread's dispatcher. Then, you don't need to pollute your view with the ViewModel's implementation details.

Some other pluses:

  • Unit testability: you can unit test these without a running application (rather than relying on Application.Current.Dispatcher)
  • Loose coupling between View & ViewModel
  • You can define dependency properties on your ViewModel and write no code to update the view as those properties change.
查看更多
▲ chillily
4楼-- · 2019-02-01 01:24

Pass the UI thread's dispatcher to the ViewModel's constructor and store it in the VM.

Take note that each thread may have its own dispatcher. You are going to need the UI thread's!

查看更多
Anthone
5楼-- · 2019-02-01 01:34

You could raise an event on your View Model (perhaps using a naming convention to indicate it's going to be raised from a non-UI thread - e.g. NotifyProgressChangedAsync). Then your View whom is attached to the event can deal with the dispatcher appropriately.

Or, you could pass a delegate to a synchronizing function to your View Model (from your View).

查看更多
对你真心纯属浪费
6楼-- · 2019-02-01 01:37

The ViewModelBase of Catel has a Dispatcher property that you can use.

查看更多
▲ chillily
7楼-- · 2019-02-01 01:44

I usually use Application.Current.Dispatcher: since Application.Current is static, you don't need a reference to a control

查看更多
登录 后发表回答