简体   繁体   中英

WPF - Closing window using MVVF command that runs new thread

The setup

I have a window and a viewmodel. The viewmodel has a command which executes a Task . When the task completes, I want the effect to be that the window closes.

What is the most acceptable (best-practice) way of doing this?

I tend to think that Dispatcher.Invoke is hacky and bad, but this is a moot point becaues the viewmodel does not have a reference to the window, or to its dispatcher.

edit: To clarify, the window belongs to the UI thread. The command itself calls a wrapper for doing an async http request (which returns a task). The command can append a ContinueWith.

I don't want to tightly couple the viewmodel to the view (such as by passing the Window to view model)

public class Api 
{
    Task MakeHttpCall();
}

public class ViewModel, DependencyObject
{
    private Api _api;

    public Task DoHttpCall() { return _api.MakeHttpCall(); }
    public MyCommand MyCommandInst { get; private set; }
}

public class MyCommand : ICommand
{
    void Execute(object parameter)
    {
       var viewModel = GetViewModel(parameter);
       viewModel.DoHttpCall().ContinueWith( t => HandleCompletionAndSomehowTriggerWindowClose(t));
    }
}

And then the view.xaml: <Button Command={Binding MyCommandInst} CommandParameter={Binding}>Do Stuff</Button>

I use MVVM Light to help facilitate this process. The ViewModel has no reference to the View it just publishes a message to close and the View is registered to receive those messages.

In the code behind of my view, I subscribe to the Messenger service like this:

public class MyView
{
   public MyView()
   {
      InitializeComponent();
      Messenger.Default.Register<NotificationMessage>(this, msg =>
      {
         if ((msg.Sender == this.DataContext) && (msg.Notification.ToUpper() == "CLOSE"))
            this.Close();
      });
   }
}

Then in the ViewModel (either in the callback method from your async process or at the end of the command method if not running async):

Messenger.Default.Send(new NotificationMessage(this, "Close"));

Generally, I believe, closing a window with the MVVM pattern depends on raising some form of Close event on the view-model that the view subscribes to:

public class MyView
{
    public MyView(MyViewModel viewModel)
    {
        this.DataContext = viewModel;
        viewModel.Close += (_, __) => Dispatcher.Invoke(this.Close);
    }
}

Raise the Close event from your task's ContinueWith action, and you're done.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM