C++/WinRT WinUI3:关闭主窗口前如何显示ContentDialog确认?
WinUI3 Mica窗口关闭前弹出确认对话框的正确实现
你当前代码的核心问题是误用了Closed事件——这个事件是窗口已经关闭后才触发的,此时设置e.Handled(true)完全无法阻止关闭流程。正确的做法是使用Window.Closing事件,它会在窗口关闭前触发,允许你中断默认关闭逻辑并弹出确认对话框。
修正后的代码实现
// 替换原来的Closed事件注册为Closing事件 m_closingRevoker = this->Closing(winrt::auto_revoke, [this](IInspectable const&, WindowClosingEventArgs const& e) { if (!_closing) { // 先阻止默认关闭行为 e.Cancel(true); _closing = true; if (have_saved) { // 已保存,直接关闭窗口 DispatcherQueue().TryEnqueue([this]() { CleanupAndClose(); }); } else { // 未保存,弹出确认对话框 winrt::Microsoft::UI::Xaml::Controls::ContentDialog dialog; dialog.XamlRoot(Content().XamlRoot()); dialog.Title(winrt::box_value(L"是否保存?")); dialog.PrimaryButtonText(L"保存并关闭"); dialog.SecondaryButtonText(L"不保存直接关闭"); dialog.CloseButtonText(L"取消"); dialog.DefaultButton(winrt::Microsoft::UI::Xaml::Controls::ContentDialogButton::Primary); dialog.PrimaryButtonClick([this](auto&&...) { if (save_data(winrt::Lexical_Frequency::implementation::amount)) { DispatcherQueue().TryEnqueue([this]() { CleanupAndClose(); }); } else { // 保存失败,恢复可关闭状态 _closing = false; } }); dialog.SecondaryButtonClick([this](auto&&...) { DispatcherQueue().TryEnqueue([this]() { CleanupAndClose(); }); }); dialog.CloseButtonClick([this](auto&&...) { // 用户取消关闭,恢复可关闭状态 _closing = false; }); dialog.ShowAsync(); } } }); // 提取清理逻辑为独立函数,避免代码重复 void CleanupAndClose() { if (m_backdropController) { m_backdropController.Close(); m_backdropController = nullptr; } if (m_dispatcherQueueController) { m_dispatcherQueueController.ShutdownQueueAsync(); m_dispatcherQueueController = nullptr; } // 手动触发窗口关闭 Close(); }
关键修正点说明
- 事件替换:把
Closed换成Closing,这是能阻止窗口关闭的核心前提。 - 取消默认关闭:在事件处理开头调用
e.Cancel(true),中断系统默认的关闭流程。 - 状态控制:用
_closing变量防止重复触发对话框,避免循环调用。 - 清理逻辑复用:把窗口关闭前的资源清理代码提取为独立函数,减少冗余。
- lambda捕获修正:将
[&]改为[this],避免局部变量的悬垂引用问题(原代码中[&]可能导致引用失效,尤其是在异步操作中)。
如果之前尝试过AppWindow.Closing没效果,大概率是因为没有正确获取当前窗口对应的AppWindow实例。可以用以下代码获取:
auto appWindow = winrt::Microsoft::UI::Windowing::AppWindow::GetFromWindow(this); if (appWindow) { m_appWindowClosingRevoker = appWindow.Closing(winrt::auto_revoke, [this](auto&&, winrt::Microsoft::UI::Windowing::AppWindowClosingEventArgs const& e) { // 处理逻辑和Window.Closing类似,调用e.Cancel(true)阻止关闭 }); }
不过对于XAML窗口来说,直接使用Window.Closing事件更加直接,无需额外的AppWindow互操作。
内容的提问来源于stack exchange,提问作者Extremeer
相关产品推荐
相关产品推荐

