tags:

views:

57

answers:

3

I'm building a WPF MVVM application and I'd like to keep what I can conforming to MVVM (I know some stuff is over-engineering, putting a best effort here though).

I've run into a small dillema, I need to Hide / Close a window based on some logic in one the the methods in the view model.

I can't for the life of me come up with a reliable way to do it. I've bound a Visibility property to the window's Visibility DP, which works (sorta) but how would I call Close() on the dialog?

I'm using View-First MVVM so the view model has no knowledge of the view, the view instantiates a view-model (through DI/IoC) and sets it as the DataContext.

Commands work the wrong way, events are out of the question unless I bind to it in code behind which is something id rather not do if there's an MVVM way to do it that isn't entirely convoluted.

Any ideas from the greater SO community?

Or maybe I'm missing something about MVVM in general? Either way, let me know :o

+2  A: 

What's wrong about events and subscribing in code-behind? Closing window is a presentation-specific thing and it certainly belongs to the View.

It's testability and separation of concerns that are primary goals here, and you can easily verify that particular event is raised by ViewModel.

Dmitry Ornatsky
+1  A: 

Using the Mediator pattern or other event handling mechanisms, you can raise certain viewmodel events (maybe "SaveDone" or "NoDataRemaining", etc). Then make your view to listen to these events; based on them, the window can decide to close itself.

If there is a more explicit way for the user to say "Close" (such as by clicking a close button in the window), hopefully that can be handled in the window exclusively.

Patrick Szalapski
Yeah, it was a logical decision to close the view. Not user-defined. The mediator pattern probably is the best way to go about this but Dmitry has a point about it being a presentation-specific concern, +1 to you both.
Aren
+4  A: 

As is often the case, there are many ways to skin this cat.

One way to do this without codebehind in the view is to use an attached behaviour, such as the following:

public static class CloseBehavior 
{
    public static bool GetCloseWhen(DependencyObject obj)
    {
        return (bool)obj.GetValue(CloseWhenProperty);
    }

    public static void SetCloseWhen(DependencyObject obj, bool value)
    {
        obj.SetValue(CloseWhenProperty, value);
    }

    public static readonly DependencyProperty CloseWhenProperty =
        DependencyProperty.RegisterAttached(
            "CloseWhen", typeof(bool), typeof(CloseBehavior), 
            new UIPropertyMetadata(OnCloseWhenChanged));
    // the lone parameter in the UIPropertyMetadata is a callback
    // for when the property value changes

    static void OnCloseWhenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // if false, we're not concerned with it
        if(!(bool)e.NewValue) return;

       // if attached to something other than a window, this doesn't make sense
        var win = d as Window;

        if(d == null) return;

            // close the window
        win.Close();
    }
}

In your XAML:

<Window x:Class="WpfApplication1.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WpfApplication1" 

     local:CloseBehavior.CloseWhen="{Binding ViewModelWorkComplete}"
>
<! -- content -->
</Window>

Where ViewModelWorkComplete is just a boolean property in the view model.

The effect is that when the viewmodel sets ViewModelWorkComplete to true (and raises the appropriate INotifyPropertyChanged event), the window will be closed.

Jay
Oh, I like this idea, this is like the reverse something I'm doing already to tie window events into commands on my view model. +1
Aren