使用WPF / MVVM Light Toolkit处理窗口closures事件

我想处理窗口的“closures”事件(当用户点击右上angular的“X”button),以便最终显示确认消息或/并取消closures。

我知道如何在代码隐藏中做到这一点:订阅窗口的“closures”事件,然后使用“CancelEventArgs.Cancel”属性。

但是我正在使用MVVM,所以我不确定这是不错的方法。

我认为最好的办法是将Closing事件绑定到我的ViewModel中的Command中。

我试过了:

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

在ViewModel中有一个关联的RelayCommand,但它不起作用(该命令的代码没有执行)。

我只是将视图构造函数中的处理程序关联起来:

 MyWindow() { // Set up ViewModel, assign to DataContext etc. Closing += viewModel.OnWindowClosing; } 

然后将该处理程序添加到ViewModel

 public void OnWindowClosing(object sender, CancelEventArgs e) { // Handle closing logic, set e.Cancel as needed } 

在这种情况下,除了复杂性之外,通过使用更加间接的更复杂的模式(5个额外的XML加命令模式行),您只能获得任何东西。

“零代码”的口头禅本身并不是目标,关键是将ViewModel从视图中分离出来 。 即使事件绑定在View的代码隐藏之后, ViewModel也不依赖于View,而闭合逻辑可以通过unit testing

这段代码工作得很好:

ViewModel.cs:

  public ICommand WindowClosing { get { return new RelayCommand<CancelEventArgs>( (args) =>{ }); } } 

和xaml:

 <i:Interaction.Triggers> <i:EventTrigger EventName="Closing"> <command:EventToCommand Command="{Binding WindowClosing}" PassEventArgsToCommand="True" /> </i:EventTrigger> </i:Interaction.Triggers> 

当然,ViewModel被分配给主容器的DataContext。

这个选项更容易,也许适合你。 在您的视图模型构造函数中,您可以像这样订阅主窗口closures事件:

 Application.Current.MainWindow.Closing += new CancelEventHandler(MainWindow_Closing); void MainWindow_Closing(object sender, CancelEventArgs e) { //Your code to handle the event } 

祝一切顺利。

Geez,好像很多代码都在这里。 上面的Stas有最小的努力正确的方法。 这里是我的适应(使用MVVMLight,但应该是可识别的)…哦,并且PassEventArgsToCommand =“真”肯定需要如上所述。

(信贷给Laurent Bugnion http://blog.galasoft.ch/archive/2009/10/18/clean-shutdown-in-silverlight-and-wpf-applications.aspx

  ... MainWindow Xaml ... WindowStyle="ThreeDBorderWindow" WindowStartupLocation="Manual"> <i:Interaction.Triggers> <i:EventTrigger EventName="Closing"> <cmd:EventToCommand Command="{Binding WindowClosingCommand}" PassEventArgsToCommand="True" /> </i:EventTrigger> </i:Interaction.Triggers> 

在视图模型中:

 ///<summary> /// public RelayCommand<CancelEventArgs> WindowClosingCommand ///</summary> public RelayCommand<CancelEventArgs> WindowClosingCommand { get; private set; } ... ... ... // Window Closing WindowClosingCommand = new RelayCommand<CancelEventArgs>((args) => { ShutdownService.MainWindowClosing(args); }, (args) => CanShutdown); 

在ShutdownService中

  /// <summary> /// ask the application to shutdown /// </summary> public static void MainWindowClosing(CancelEventArgs e) { e.Cancel = true; /// CANCEL THE CLOSE - let the shutdown service decide what to do with the shutdown request RequestShutdown(); } 

RequestShutdown看起来像下面的内容,但基本上是RequestShutdown或者它的名称决定是否closures应用程序(无论如何快乐closures窗口):

 ... ... ... /// <summary> /// ask the application to shutdown /// </summary> public static void RequestShutdown() { // Unless one of the listeners aborted the shutdown, we proceed. If they abort the shutdown, they are responsible for restarting it too. var shouldAbortShutdown = false; Logger.InfoFormat("Application starting shutdown at {0}...", DateTime.Now); var msg = new NotificationMessageAction<bool>( Notifications.ConfirmShutdown, shouldAbort => shouldAbortShutdown |= shouldAbort); // recipients should answer either true or false with msg.execute(true) etc. Messenger.Default.Send(msg, Notifications.ConfirmShutdown); if (!shouldAbortShutdown) { // This time it is for real Messenger.Default.Send(new NotificationMessage(Notifications.NotifyShutdown), Notifications.NotifyShutdown); Logger.InfoFormat("Application has shutdown at {0}", DateTime.Now); Application.Current.Shutdown(); } else Logger.InfoFormat("Application shutdown aborted at {0}", DateTime.Now); } } 

提问者应该使用STAS答案,但是对于使用棱镜而没有galasoft / mvvmlight的读者,他们可能想要尝试我使用的:

在窗口或用户控件的顶部定义等定义命名空间:

 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

就在这个定义下面:

 <i:Interaction.Triggers> <i:EventTrigger EventName="Closing"> <i:InvokeCommandAction Command="{Binding WindowClosing}" CommandParameter="{Binding}" /> </i:EventTrigger> </i:Interaction.Triggers> 

属性在您的viewmodel:

 public ICommand WindowClosing { get; private set; } 

在您的viewmodel构造函数中附加delegatecommand:

 this.WindowClosing = new DelegateCommand<object>(this.OnWindowClosing); 

最后,你想要在控制/窗口/closures的地方访问你的代码:

 private void OnWindowClosing(object obj) { //put code here } 

我会试图在你的App.xaml.cs文件中使用一个事件处理程序来决定是否closures应用程序。

例如,你可以在你的App.xaml.cs文件中有如下代码:

 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(); } 

然后在你的MainWindowViewModel代码中,你可以有以下几点:

  #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] 

如果您不想了解ViewModel中的窗口(或其任何事件),请按照MVVM模式进行回答。

 public interface IClosing { /// <summary> /// Executes when window is closing /// </summary> /// <returns>Whether the windows should be closed by the caller</returns> bool OnClosing(); } 

在ViewModel中添加接口和实现

 public bool OnClosing() { bool close = true; //Ask whether to save changes och cancel etc //close = false; //If you want to cancel close return close; } 

在窗口中添加closures事件。 这个代码不会破坏MVVM模式。 视图可以知道视图模型!

 void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { IClosing context = DataContext as IClosing; if (context != null) { e.Cancel = !context.OnClosing(); } } 

基本上,窗口事件可能不会被分配给MVVM。 一般情况下,closuresbutton显示一个对话框,要求用户“保存:是/否/取消”,这可能无法通过MVVM来实现。

您可以保留OnClosing事件处理程序,您可以在其中调用Model.Close.CanExecute()并在事件属性中设置布尔结果。 所以在CanExecute()调用后,如果为true,则在OnClosed事件中,调用Model.Close.Execute()

我没有做这个testing,但它似乎工作。 以下是我想到的:

 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); } } } 

我们为此使用AttachedCommandBehavior。 您可以将任何事件附加到您的视图模型上的命令,避免任何代码隐藏。

我们在整个解决scheme中使用它,几乎没有代码

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

使用MVVM Light Toolkit:

假设在视图模型中有一个Exit命令:

 ICommand _exitCommand; public ICommand ExitCommand { get { if (_exitCommand == null) _exitCommand = new RelayCommand<object>(call => OnExit()); return _exitCommand; } } void OnExit() { var msg = new NotificationMessageAction<object>(this, "ExitApplication", (o) =>{}); Messenger.Default.Send(msg); } 

这是收到的观点:

 Messenger.Default.Register<NotificationMessageAction<object>>(this, (m) => if (m.Notification == "ExitApplication") { Application.Current.Shutdown(); }); 

另一方面,我使用ViewModel的实例在MainWindow处理Closing事件:

 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (((ViewModel.MainViewModel)DataContext).CancelBeforeClose()) e.Cancel = true; } 

CancelBeforeClose检查视图模型的当前状态,如果closures应该停止,则返回true。

希望它可以帮助别人。

 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { MessageBox.Show("closing"); }