gr1d3r
gr1d3r

Reputation: 109

MVVM - Close a window from view model

In my application i'm using prism and trying to implement the following concept:

There is a communication window that can have two possible user controls. I have the ViewModels for the window and the user controlls. In every user control i have some buttons. For some buttons i need to perform some logic in the ViewModel and when the logic is done, close the parent window. I tried to send the parant window as a command parameter like this:

CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"

And in the viewModel close the window using the following code:

// Create the command
OpenChannelCommand = new RelayCommand(OpenChannel, IsValidFields);
...
private void OpenChannel()
{
  // do some logic...
  CloseWindow();
}
private GalaSoft.MvvmLight.Command.RelayCommand<object> _closeCommand;
private GalaSoft.MvvmLight.Command.RelayCommand<object> CloseWindow()
{
    _closeCommand = new GalaSoft.MvvmLight.Command.RelayCommand<object>((o) => ((Window)o).Close(), (o) => true);
    return _closeCommand;
}

But still the window aren't closing.

EDIT:

The user control XAML code is:

<Button Content="Open Channel" Command="{Binding OpenChannelCommand}" 
                CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>

The user control ViewModel code is:

public RelayCommand OpenChannelCommand { get; set; } 
ctor() 
{
   OpenChannelCommand = new RelayCommand(OpenChannel, IsValidFields);
}     
private void OpenChannel() 
{    
   // logic 
   CloseWindow(); 
}
private GalaSoft.MvvmLight.Command.RelayCommand<object> CloseWindow()
{
    _closeCommand = new GalaSoft.MvvmLight.Command.RelayCommand<object>((o) => ((Window)o).Close(), (o) => true);
    return _closeCommand;
 }

This is the full implementaion that i currently tried. When setting a breakpoint to the CloseWindow method it hits at the view modet initialization and after calling it again in the button click command is doesn't do a thing.

Upvotes: 3

Views: 2896

Answers (1)

Anton Tykhyy
Anton Tykhyy

Reputation: 20076

Closing the window is the view's responsibility, the view-model should know nothing about it. No wonder you're getting tangled up. If you don't need the window to stay on screen while the "button logic" is running, then just use a CompositeCommand from Prism (I don't like it much because it uses code-behind, but no matter) or equivalent to bind two commands to the button. On the other hand, if you need to keep the window on screen while the "button logic" is running, e.g. to display progress, and if your view model reflects this, then you can add a bool IsButtonLogicComplete property to your view model (don't forget INotifyPropertyChanged) and bind the window's 'closed' state to this property via an attached property/behavior like this one:

public static class AttachedProperties
{
    // in Visual Studio, the `propa` snippet inserts the boilerplate
    public static DependencyProperty ForceCloseProperty =
        DependencyProperty.RegisterAttached ("ForceClose",
        typeof (bool), typeof (AttachedProperties), new UIPropertyMetadata (false, (d, e) =>
        {
            var w  = d as Window ;
            if (w != null && (bool) e.NewValue)
            {
                w.DialogResult = true ;
                w.Close () ;
            }
        })) ;

    public static bool GetForceClose (DependencyObject obj)
    {
        return (bool) obj.GetValue (ForceCloseProperty) ;
    }

    public static void SetForceClose (DependencyObject obj, bool value)
    {
        obj.SetValue (ForceCloseProperty, value) ;
    }
}

<!-- in .xaml -->
<Window
  xmlns:local="clr-namespace:YourNamespace"
  local:AttachedProperties.ForceClose="{Binding IsButtonLogicComplete}" ...

This will keep your view and your view model concerns nicely separated.

Upvotes: 2

Related Questions