You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 12:25:24