How to properly handle window close event in wpf MVVM

I know this question has been asked many times, but I'll try to be as specific as possible.

I am getting started in WPF / MVVM and using MVVM Light Toolkit from Galasoft in my project.

I have a view that contains a form where the user enters some patient data. When they hit the close (X) button, I want to check that they have entered something, and if so, ask them if they want to save before closing with (Yes, No and Cancel). I did some research and found that many of them offer the EventToCommand function, for example

XAML

<Window
   xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
   xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF45"
   DataContext="{Binding Main, Source={StaticResource Locator}}">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="Closing">
         <cmd:EventToCommand Command="{Binding OnClosingCommand}" 
            PassEventArgsToCommand="True"/>
      </i:EventTrigger>
   </i:Interaction.Triggers>
...
</Window>

      

Show model

public class MainViewModel : ViewModelBase
{
   public RelayCommand<CancelEventArgs> OnClosingCommand { get; set; }

   public MainViewModel()
   {
      this.OnClosingCommand = 
         new RelayCommand<CancelEventArgs>(this.OnClosingCommandExecuted);
   }

   private void OnClosingCommandExecuted(CancelEventArgs cancelEventArgs)
   {
      // logic to check if view model has updated since it is loaded
      if (mustCancelClosing)
      {
         cancelEventArgs.Cancel = true;
      } 
   }
}

      

The above example is taken from Confirmation on Close Window with "X" Button with MVVM Highlight

However, the creator of the MVVM Light Toolkit itself says that this breaks the separation of concern that the MVVM pattern tries to create, since it passes event arguments belonging to the view (in this case CancelEventArgs

) to the view model. He said so in this article http://blog.galasoft.ch/posts/2014/01/using-the-eventargsconverter-in-mvvm-light-and-why-is-there-no-eventtocommand-in-the- windows-8-1-version /

So my question is how to properly handle such a problem that does not break the MVVM pattern. Any point in the right direction would be greatly appreciated!

+3


source to share


1 answer


I do not claim to be absolute, but I like the following approach.
The base view model has RelayCommand

/ DelegateCommand

as follows:

public ICommand ClosingCommand { get; }

      

where ICommand.Execute

is implemented as:

    /// <summary>
    /// Executes an action, when user closes a window, displaying this instance, using system menu.
    /// </summary>
    protected virtual void Closing()
    {
    }

      

and ICommand.CanExecute

how:



    /// <summary>
    /// Detects whether user can close a window, displaying this instance, using system menu.
    /// </summary>
    /// <returns>
    /// <see langword="true"/>, if window can be closed;
    /// otherwise <see langword="false"/>.
    /// </returns>
    protected virtual bool CanClose()
    {
        return true;
    }

      

In turn, the UI uses the attached behavior to handle Window.Closing

:

public static class WindowClosingBehavior
{
        public static readonly DependencyProperty ClosingProperty = DependencyProperty.RegisterAttached(
            "Closing", 
            typeof(ICommand), 
            typeof(WindowClosingBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback(ClosingChanged)));

        private static void ClosingChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            var window = target as Window;
            if (window != null)
            {
                if (e.NewValue != null)
                    window.Closing += Window_Closing;
                else
                    window.Closing -= Window_Closing;
            }
        }

        private static void Window_Closing(object sender, CancelEventArgs e)
        {
            var window = sender as Window;
            if (window != null)
            {
                var closing = GetClosing(window);
                if (closing != null)
                {
                    if (closing.CanExecute(null))
                        closing.Execute(null);
                    else
                        e.Cancel = true;
                }
            }
        }
}

      

XAML (assumes view model is DataContext

for window):

behaviors:WindowClosingBehavior.Closing="{Binding ClosingCommand}"

      

+1


source







All Articles