单例窗口重写关闭事件报错:Cannot call close after closing override求助
解决WPF窗口单例模式下Closing事件异步弹窗后Close()抛出异常的问题
这个问题我之前也踩过坑,核心原因是你在Closing事件里先取消了窗口关闭(e.Cancel = true),然后调用异步的ShowMessageAsync,等用户确认后再调用Close()时,又会重新触发Closing事件,导致窗口状态冲突,最终抛出那个异常。下面给你两种靠谱的解决方案:
方案一:用布尔标志位控制Closing事件逻辑
添加一个私有布尔变量标记是否是程序内部触发的关闭操作,这样就能避免重复取消关闭流程:
private bool _isInitiatedClose = false; protected override void OnClosing(CancelEventArgs e) { // 如果是程序内部主动触发的关闭,直接放行 if (_isInitiatedClose) { base.OnClosing(e); return; } // 取消用户触发的关闭,转而弹出确认对话框 e.Cancel = true; // 异步处理设置检查和用户确认 _ = CheckSettingsAndConfirmClose(); } private async Task CheckSettingsAndConfirmClose() { // 弹出MahApps的确认对话框 var result = await this.ShowMessageAsync( "确认", "是否要保存当前设置并关闭窗口?", MessageDialogStyle.AffirmativeAndNegative); if (result == MessageDialogResult.Affirmative) { // 执行你的CheckSettings逻辑,比如保存设置 CheckSettings(); // 标记为内部触发关闭,再调用Close() _isInitiatedClose = true; this.Close(); } // 如果用户取消,什么都不用做,窗口保持打开 }
方案二:优化单例模式的同时处理关闭逻辑
如果你是单例窗口,还可以结合单例的实例管理,确保窗口状态正确:
// 单例实例 private static YourWindow _instance; // 关闭流程标记 private bool _isClosingInProcess = false; public static YourWindow Instance { get { if (_instance == null || !_instance.IsLoaded) { _instance = new YourWindow(); // 窗口关闭后清空实例,支持重新打开 _instance.Closed += (s, e) => _instance = null; } return _instance; } } public YourWindow() { InitializeComponent(); this.Closing += YourWindow_Closing; } private async void YourWindow_Closing(object sender, CancelEventArgs e) { if (_isClosingInProcess) return; e.Cancel = true; _isClosingInProcess = true; try { var dialogResult = await this.ShowMessageAsync( "提示", "确定要关闭窗口吗?", MessageDialogStyle.AffirmativeAndNegative); if (dialogResult == MessageDialogResult.Affirmative) { CheckSettings(); // 执行你的设置检查逻辑 _isClosingInProcess = false; this.Close(); // 此时关闭不会再触发取消逻辑 } else { _isClosingInProcess = false; // 用户取消,窗口继续保持打开 } } catch (Exception ex) { _isClosingInProcess = false; // 处理异常,比如弹出错误提示 await this.ShowMessageAsync("错误", $"处理关闭逻辑时出错:{ex.Message}"); } }
关键注意点
- 异步方法的使用:不要在
OnClosing里直接用async void(事件处理程序除外),用async Task配合_ =调用,避免潜在的线程问题。 - 标志位的重置:如果用户取消关闭,一定要把标志位重置为
false,否则下次用户尝试关闭时会直接放行。 - 单例的实例管理:窗口关闭后要清空单例实例,否则下次调用
Instance时会返回已关闭的窗口实例,导致异常。
这样修改后,你调用win.Close()时就不会再抛出那个“cannot set visibility or call show or showdialog after window has closed”的异常了。
内容的提问来源于stack exchange,提问作者PRENDETEVELO NEL CULO
相关产品推荐
相关产品推荐

