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.