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:

  1. Prevent the View’s Window from closing, even if the user clicks in the “X” at the top of the Window.
  2. Receive notification in the ViewModel when the user tries to close (which allows for “Are you sure?” message boxes, and other similar things)
  3. 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.

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

6 Responses to “Using Behaviors to Allow the ViewModel to Manage View Lifetime in M-V-VM”
  1. Brent says:

    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!

  2. Tom McKearney says:

    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

    • Reed says:

      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

      • Dave says:

        How would you use this to warn a user they are going to lose their changes if they don’t save?

        • Reed says:

          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.

Trackbacks

Check out what others are saying about this post...
  1. [...] on The Code Project.  It is a port of the MVVM-friendly Blend Behavior I wrote about in a previous article to use Attached Properties instead of a Blend [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!