WPF MVVM—在窗口关闭事件中显示MessageBox



[我标记了Catel,但我认为这个问题适用于任何MVVM框架。]

这个网站上有几个关于处理关闭事件的建议(尤其是"红色X"按钮),程序员希望检查应用程序是否可以关闭或显示"你确定吗?"对话框。我已经尝试了三种方法——完全在View中处理事件,使用事件触发器从XAML调用ViewModel上的命令,以及让View将事件处理连接到ViewModel事件处理程序。当用户尝试按预期关闭窗口时,这三种方式都会触发。

我的问题是,如果我试图在事件处理程序中显示MessageBox以获得用户的确认,我会得到以下异常:

System.InvalidOperationException was unhandled
Message: An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
Additional information: Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.

正在关闭的窗口是我的主窗口,所以如果它关闭,我的应用程序也会关闭。我知道我可以通过将CancelEventArgs.cancel值设置为true来取消窗口的关闭。

我如何既捕获Closing事件,又让我的处理程序根据用户的输入来确定是否实际关闭?

如果你需要更多信息,请告诉我。谢谢

编辑:以下是我用来处理退出应用程序菜单命令的内容:

private async void OnExitApplicationExecute()
if (this.IsProcessing)
{
await this._messageService.ShowWarningAsync("Please click on 'Stop Processing' before closing the Processor.", "Stop Processing First");
}
else
{
MessageResult msgResult = await this._messageService.ShowAsync("Exiting the Processor will halt all request processing." + Environment.NewLine + "Are you sure?",
"Exiting Processor...", MessageButton.YesNo, MessageImage.Question);
if (msgResult == MessageResult.Yes)
{
_logger.Info("Main Task: Application ended.");
this._navigationService.CloseApplication();
}
}
}

messageService和navigationService调用是类别服务/方法。记录器是NLog。

这在作为命令处理程序运行时起作用,因为在所有检查完成之前,窗口不会关闭。如果我试图在Closing事件处理中注入相同的逻辑,我会得到上面提到的异常。

以下是堆栈跟踪的一部分:

2017-11-01 14:45:57.3269 [00009] ERROR App:  An unhandled exception occurred and has been logged.  Please contact support. System.InvalidOperationException: Cannot set Visibility to Visible or call Show, ShowDialog, Close, or WindowInteropHelper.EnsureHandle while a Window is closing.
at System.Windows.Window.VerifyNotClosing()
at System.Windows.Window.InternalClose(Boolean shutdown, Boolean ignoreCancel)
at System.Windows.Window.Close()
at Catel.Windows.DataWindow.SetDialogResultAndMakeSureWindowGetsClosed(Nullable`1 result) in C:CI_WSWs105284SourceCatelsrcCatel.MVVMCatel.MVVM.SharedWindowsWindowsDataWindowDataWindow.cs:line 708
at Catel.Windows.DataWindow.<OnDataWindowClosing>b__104_0() in C:CI_WSWs105284SourceCatelsrcCatel.MVVMCatel.MVVM.SharedWindowsWindowsDataWindowDataWindow.cs:line 892
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

在显示MessageBox之前,将CancelEventArgsCancel属性设置为true。这对我来说很好:

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Closing += MainWindow_Closing;
}

private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
if (MessageBox.Show("Do you want to close?", "", MessageBoxButton.YesNoCancel) == MessageBoxResult.Yes)
Environment.Exit(0);
}
}

基本上,如果我想管理应用程序/窗口的打开和关闭过程,我会尝试直接从应用程序过程控制它。以下是一些非常粗略的代码:

public partial class App : Application
{
public MainWindow window = new MainWindow();
void App_Startup(object sender, StartupEventArgs e)
{
window.Show();
window.Closing += manageClosing;
}
void manageClosing(Object sender, System.ComponentModel.CancelEventArgs e)
{
MessageBox.Show("It won't close");
e.Cancel = true;
}
}

如果您使用Catel并使用DataWindow/Windows类,您可以在MainViewModel::SaveAsync中简单地问这个问题。如果您返回false,窗口将不会关闭(Catel已经为您处理好了)。

在事件处理程序中,最好使用Dispatcher来处理任何耗时的代码,包括对话框。这避免了事件处理程序的双重触发。请参阅Dispatcher BeginInvoke语法

最新更新