Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM
One common issue people face in the Model-View-ViewModel pattern is how to cleanly deal with the lifetime of the View from within the ViewModel. Keeping all of the logic in the ViewModel for preventing the View from closing can be a daunting task, especially at first.
After some discussions about controlling the lifetime of the View without introducing Code Behind in Model-View-ViewModel, I went ahead and created a behavior and sample application demonstrating its use. I also submitted this to the Microsoft Expression Gallery: WindowCloseBehavior. All of the code is available on the Expression Gallery for download.
The basic idea is to use a Behavior, introduced with Expression Blend 3, to allow the View to hook into commands defined completely within the ViewModel. By attaching a behavior to your View, and binding to the ViewModel, you can allow the ViewModel to do some very important things:
- Prevent the View’s Window from closing, even if the user clicks in the “X†at the top of the Window.
- Receive notification in the ViewModel when the user tries to close (which allows for “Are you sure?†message boxes, and other similar things)
- Receive notification in the ViewModel when the View’s Window has successfully closed
How does this work? First, we need to have our ViewModel provide at least one, and potentially two, ICommand implementations. The first will bind to the behavior’s CloseCommand property, the second to the CloseFailCommand property.
The first is the Close Command itself. When the user tries to close the Window, the behavior will use the CloseCommand’s CanExecute() method to determine whether to allow the Window to close. If CanExecute() returns true, the Window will be allowed to close. If not, then it will stay open.
If the CloseCommand’s CanExecute() method returns false, and the user bound the Behavior’s CloseFailCommand to an ICommand, it will execute at that point.
If the CloseCommands’s CanExecute() method returns true, the window will be allowed to close. When the window finally closes, the CloseCommand will be executed.
Using it in code is easy – for example, our MainView (the View in our sample project), just adds this behavior to the main UserControl:
<UserControl x:Class="DemonstrateCloseBehavior.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:BehaviorLibrary="clr-namespace:WindowCloseBehavior;assembly=WindowCloseBehavior" Height="Auto" Width="400" HorizontalContentAlignment="Center"> <Interactivity:Interaction.Behaviors> <BehaviorLibrary:WindowCloseBehavior CloseCommand="{Binding CloseCommand}" CloseFailCommand="{Binding CloseFailCommand}" /> </Interactivity:Interaction.Behaviors> <Grid> ...
That’s all that has to happen in the View.
The ViewModel, similarly, just needs to provide a couple of commands:
public ICommand CloseCommand { get; private set; } public ICommand CloseFailCommand { get; private set; }
In the sample project, I implemented these by using a custom Command class which takes delegates for Execute and CanExecute:
public MainViewModel() { this.CloseCommand = new DelegateCommand( () => MessageBox.Show("Window close method activated in ViewModel"), () => this.AllowWindowToClose); this.CloseFailCommand = new DelegateCommand( () => MessageBox.Show("Please check the appropriate check box to close this Window."), () => true); }
This just lets us show messages to the user demonstrating the behavior.
Please feel free to use this as needed, and provide any feedback on its usefulness.
Awesome! I’ve been searching for an elegant way to do this for some time. I really need to learn more about Behaviors. Thanks for sharing this!
I would not put a MessageBox (UI code) inside a ViewModel. You need an abstraction for that part too, or you’re violating the idea behind MVVM
Tom,
The MessageBox here was just to demonstrate that this worked, in concept. I completely agree that messaging should be abstracted. Personally, I use Dependency Injection and a message service, but messaging models work as well.
Thanks for the feedback.
-Reed
How would you use this to warn a user they are going to lose their changes if they don’t save?
Dave,
The behavior, as is, just provides a single point where you can prevent the Window from being closed. It does this by checking the CanExecute on the ICommand.
One simple option:
The command’s CanExecute property, here, could prompt the user with a OK/Cancel button (and return false on Cancel). This would warn the user, and they’d have to hit “OK” to allow it to close without saving.
Do you have a Silverlight 4 version of this behavior for MVVM?
Unfortunately, I do not have one available at this time. A direct port would make little sense, as this is working against the Window class, which is unavailable in Silverlight. However, the concepts could be ported to other uses very easily.
Beautiful! I’ve been searching for a solution to this for a couple of hours now, trying different things, but your solution is what finally worked for me. Thanks.
I adapted this for use in a WPF app, by the way, to handle closing a Window type. The only thing I had to change was your WindowClassBehavior class declaration:
public class WindowCloseBehavior : Behavior
Perfect, simple and does the job, great MVVM, ive used this on a custom control, simply swapped my border with the CustomControl to a UserControl and there you have it.
This is a great article. Brilliant!
Reed,
I am also interested if you have had discussed the following issue already?
Dependency Injection and a message service or messaging models?
Thanks,
den
Den,
I’ve written a bit about DI (with MEF) – you can click on the MEF category to see the articles.
I haven’t really tackled message services or messaging models. There’s a lot out there that already handles this, and once you understand the basics of MVVM, toolkits like MVVM Light, etc, should make a lot more sense. They handle those topics very nicely.
-Reed
It’s really great post
But the link to (Expression Gallery: WindowCloseBehavior.) isn’t available anymore!
Can you post it here or upload it somewhere?!
Thanks in advance
Here is the new link:
https://code.msdn.microsoft.com/Window-Close-Attached-fef26a66#content
Cheers
I really wanted to keep reading! I will absolutely apply soke of thee tips myself,
you’re brilliant. Have you got a mail list that I am able to check out?