views:

458

answers:

5

I'd like to handle the "Closing" event (when a user clicks the upper right 'X' button) of my window in order to eventually display a confirm message or/and cancel the closing.

I know how to do this in the code-behind : subscribe to the "Closing" event of the window then use the "CancelEventArgs.Cancel" property.

But I'm using MVVM so I'm not sure it's the good approach.

I think the good approach would be to bind the Closing event to a Command in my ViewModel.

I tried that :

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <cmd:EventToCommand Command="{Binding CloseCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

With an associated RelayCommand in my ViewModel but it doesn't work (the command's code is not executed).

A: 

I would be tempted to use an event handler within your App.xaml.cs file that will allow you to decide on whether to close the application or not.

For example you could then have something like the following code in your App.xaml.cs file:

protected override void OnStartup(StartupEventArgs e)
{
  base.OnStartup(e);
  // Create the ViewModel to attach the window to
  MainWindow window = new MainWindow();
  var viewModel = new MainWindowViewModel();

  // Create the handler that will allow the window to close when the viewModel asks.
  EventHandler handler = null;
  handler = delegate
  {
    //***Code here to decide on closing the application****
    //***returns resultClose which is true if we want to close***
    if(resultClose == true)
    {
      viewModel.RequestClose -= handler;
      window.Close();
    }
  }
  viewModel.RequestClose += handler;

  window.DataContaxt = viewModel;

  window.Show();

}

Then within your MainWindowViewModel code you could have the following:

    #region Fields
       RelayCommand closeCommand;
      #endregion

  #region CloseCommand
/// <summary>
/// Returns the command that, when invoked, attempts
/// to remove this workspace from the user interface.
/// </summary>
public ICommand CloseCommand
{
    get
    {
        if (closeCommand == null)
            closeCommand = new RelayCommand(param => this.OnRequestClose());

        return closeCommand;
    }
}
#endregion // CloseCommand

#region RequestClose [event]

/// <summary>
/// Raised when this workspace should be removed from the UI.
/// </summary>
public event EventHandler RequestClose;

/// <summary>
/// If requested to close and a RequestClose delegate has been set then call it.
/// </summary>
void OnRequestClose()
{
    EventHandler handler = this.RequestClose;
    if (handler != null)
    {
        handler(this, EventArgs.Empty);
    }
}

#endregion // RequestClose [event]
ChrisBD
Thanks for the detailed answer. However, I don't think that solves my problem: I need to handle the window closing when the user clicks the upper right 'X' button. It would be easy to do this in the code-behind (i'd just link the Closing event and set the CancelEventArgs.Cancel to true of false) but I'd like to do this in MVVM style. Sorry for the confusion
Olivier PAYEN
A: 

Basically, window event may not be assigned to MVVM. In general, the Close button show a Dialog box to ask the user "save : yes/no/cancel", and this may not be achieved by the MVVM.

You may keep the OnClosing event handler, where you call the Model.Close.CanExecute() and set the boolean result in the event property. So after the CanExecute() call if true, OR in the OnClosed event, call the Model.Close.Execute()

Echtelion
A: 

I haven't done much testing with this but it seems to work. Here's what I came up with:

namespace OrtzIRC.WPF
{
    using System;
    using System.Windows;
    using OrtzIRC.WPF.ViewModels;

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private MainViewModel viewModel = new MainViewModel();
        private MainWindow window = new MainWindow();

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            viewModel.RequestClose += ViewModelRequestClose;

            window.DataContext = viewModel;
            window.Closing += Window_Closing;
            window.Show();
        }

        private void ViewModelRequestClose(object sender, EventArgs e)
        {
            viewModel.RequestClose -= ViewModelRequestClose;
            window.Close();
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            window.Closing -= Window_Closing;
            viewModel.RequestClose -= ViewModelRequestClose; //Otherwise Close gets called again
            viewModel.CloseCommand.Execute(null);
        }
    }
}
Brian Ortiz
A: 

Hi,

We use AttachedCommandBehavior for this. You can attach any event to a command on your view model avoiding any code behind.

We use it throughout our solution and have almost zero code behind

http://marlongrech.wordpress.com/2008/12/13/attachedcommandbehavior-v2-aka-acb/

Cheers

Chris Adams