5

I'm a student learning C# with WPF using the MVVM pattern. Recently I have been working on a [art of my application (a custom splash screen) that should not be closed when I don't want it to. I have been searching the web for a good way of doing this without code-behind. Unfortunately after days I still did not find a satisfying way. Then I came to think of a way to do it myself, with help of just one line of code in the constructor of my view. It still makes my code testable and decouples the code from the View. The question is, is there a better way of doing what I'm trying to do:

My interface for my ViewModel

public interface IPreventCloseViewModel
{
    bool PreventClose { get; set; }
}

The extension for the View

public static class PreventCloseViewModelExtension
{
    /// <summary>
    /// Use this extension method in the constructor of the view.
    /// </summary>
    /// <param name="element"></param>
    public static void PreventCloseViewModel(this Window element)
    {
        var dataContext = element.DataContext as IDisposable;
        if (dataContext is IPreventCloseViewModel)
        {
            element.Closing += delegate(object sender, CancelEventArgs args)
                                   {
                                       if (dataContext is IPreventCloseViewModel)
                                       {
                                           args.Cancel = (dataContext as IPreventCloseViewModel).PreventClose;
                                       }
                                   };
        }
    }
}

The code-behind for the View

public partial class SplashScreen
{
    public SplashScreen()
    {
        InitializeComponent();
        this.PreventCloseViewModel();
    }
}
van Nijnatten
  • 404
  • 5
  • 15

2 Answers2

15

MVVM does not mean that you cannot use Code-Behind.

MVVM means that your application logic should not be tied to UI elements.

You can perfectly well handle events in code behind (such as Window.Closing), and "send messages" or execute methods in the ViewModel to react to that.

Here, you are not breaking MVVM by placing the event handler in code behind. You would be breaking MVVM if you were placing the logic that determines whether the application can be closed in code behind. That is a responsibility of the application logic, and the application logic lives in ViewModels, not Views.

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • 1
    The last paragraph is inaccurate, of course you can set properties on the VM from the view, you do it all the time (usually via data-binding). – H.B. Jun 06 '13 at 23:19
  • @H.B right. What I wanted to emphasize is that the application logic (probably based on whether some data is dirty/unsaved or not) should not go into code behind. I should probably reword that. – Federico Berasategui Jun 06 '13 at 23:20
  • Thank you your response. I was suspecting it to be ok, just needed the confirmation. – van Nijnatten Jun 07 '13 at 04:51
4

I usually have a generic Shell class which subclasses Window and does something like:

public Shell()
{
    InitializeComponent();
    this.Closing += (s,e) =>
    {
        var canClose = Content as ICanClose;
        if (canClose != null)
            e.Cancel = !canClose.CanClose;
    }
}

That way it does not matter what kind of view model you put in, if it implements the interface that will be taken into account.

Don't see much point in externalizing the logic, and it's fine in terms of the MVVM pattern.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • H.B. I'm thinking about your last paragraph, and the only reason I externalised is because I want to make it reusable and testable per definition. Would that be ok then, or is it still too much? (not sure about the testability part yet) – van Nijnatten Jun 07 '13 at 04:57
  • @JvN: Well, if you have multiple windows (rarely happens for me, just have one `Shell` wit various view models as its `Content`) you could do that. However you could also have a base window class (not allowed to define XAML for it i think) which implements the logic, there usually are several ways to keep the redundancy down... – H.B. Jun 07 '13 at 05:02
  • Uhm.. Base window class... That sounds promising, why did I not think of that, even though I just created a base ViewModel?? great tip, Thanks! – van Nijnatten Jun 07 '13 at 05:43