首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 非模態子視窗關閉確認對話框的最小化異常解決方案

     編輯:關於.NET

在 WPF 非模態子視窗的 Closing 事件中直接呼叫 MessageBox.Show 確認關閉,看似正常卻隱藏嚴重 Bug:若使用者在對話框彈出期間最小化主視窗(子視窗跟隨最小化),再還原主視窗時會拋出 InvalidOperationException,且子視窗畫面變黑無法恢復。

Bug 重現步驟

  1. 開啟主視窗與非模態子視窗
  2. 點擊子視窗關閉鈕,彈出確認對話框(不按任何鍵)
  3. 點擊工作列主視窗圖示 → 主視窗最小化,子視窗跟隨最小化
  4. 再次點擊工作列主視窗圖示 → 主視窗還原
  5. 異常拋出,子視窗內容變黑

根因分析

MessageBox.Show 會阻塞 UI 執行緒,導致子視窗的 Closing 程序無法完成。此期間 Windows 自動將子視窗隨主視窗最小化,破壞了視窗關閉流程的狀態機,造成後續還原時狀態不一致。

解決方案:非同步延遲確認

利用 Dispatcher.BeginInvoke 將確認邏輯排程到 UI 執行緒稍後執行,立即返回 Closing 事件,避免阻塞:

public static void OnWindowClosingAdv(object sender, CancelEventArgs e) {
    // 防止遞迴呼叫
    if (new StackTrace().GetFrames().Any(frame => frame.GetMethod().Name == "ConfirmClose")) return;
    
    e.Cancel = true; // 先取消關閉
    Window window = sender as Window;
    window.Dispatcher.BeginInvoke(new Action<Window>(ConfirmClose), window);
}

private static void ConfirmClose(Window window) {
    if (MessageBox.Show($"Are you sure to close the {window.Title}?", "Confirm",
        MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) {
        window.Close(); // 使用者確認後真正關閉
    }
}

關鍵設計點

  • e.Cancel = true 先攔截關閉,避免視窗進入不可逆狀態
  • Dispatcher.BeginInvoke 讓 MessageBox 在下一個 Dispatcher 迴圈顯示,Closing 事件順利結束
  • 遞迴防護:StackTrace 檢查或簡單旗標變數,防止 window.Close() 再次觸發 Closing 進入死循環
  • 適用於非模態子視窗,模態視窗 (ShowDialog) 不存在此問題

替代方案比較

方案優點缺點
Dispatcher.BeginInvoke (本文)程式碼少、不阻塞 UI、相容性好需處理遞迴防護
自訂非模態確認視窗完全控制 UI/邏輯開發成本高
關閉前預檢 (無對話框)零風險使用者體驗差
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved