程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> [WPF]如何在關閉非模態子窗體時用消息框確認:解決最小化窗體時拋出的異常

[WPF]如何在關閉非模態子窗體時用消息框確認:解決最小化窗體時拋出的異常

編輯:關於.NET

又是一個看起來很簡單的問題。像下面這樣在Closing裡彈出個MessageBox確認一下不就行了?

public static void OnWindowClosing(object sender, CancelEventArgs e)
{
  if (MessageBox.Show(string.Format("Are you sure to close the {0}?", (sender as Window).Title),
    "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
  {
    e.Cancel = true;
  }
}

代碼簡單得不能再簡單了,而且試了一下可以達到目的,似乎是沒有什麼問題啊。但是很多代碼需要大量的測試才能發現問題。這個代碼就是其中之一。

在說明BUG之前,先給幾個信息,大家可以猜猜如何重現這個問題。

1.只有非模態的子窗體有這個BUG。主窗口是沒有問題的。

2.彈出MessageBox的時候,UI消息處理線程會被阻塞。

3.拋出的異常,是InvalidOperationException。

4.子窗體與主窗體有從屬關系,主窗體最小化時,子窗體也跟著最小化。

5.最後一個信息,已經是把BUG告訴大家了。就是Error Message,如下圖所示。

圖1. Exception信息

看了Error Message,應該都明白了。主窗體沒有問題,因為關主窗體時彈出消息框之後,根本沒有什麼UI操作可以最小化主窗體。(自己寫另一個程序去最小化這個窗體不在考慮范圍之內。)所以不會有這個BUG,但是為什麼子窗體有呢。

下面描述一個這個Bug的產生過程。

1.主窗體和子窗體都顯示出來。

2.點擊關閉按鈕關閉子窗體,此時會彈出消息框問要不要關。不去理這個消息框。

3.點擊任務欄上的主窗體,使主窗體最小化。這裡子窗體也最小化了。

4.再點任務欄上的主窗體,使主窗體還原。異常拋出。而且,即使handle了這個異常,這個子窗體也會變黑的。

可以發現,其實罪魁禍首是上面的第4條信息。操作系統在主窗體最小化時,自動最小化其子窗體,結果幫了倒忙。這種自動做事幫倒忙的事情應該還有不少。還發現過的一例就是在WPF Bug清單裡的RadioButton無法綁定的BUG,也是系統自動做事造成的。但是邏輯上來講,這麼做也的確是對的。

最後想辦法解決問題的,只能是我們自己。Exception的Message上說在窗體Closing的時候,不能做這做那,但是我們又要做,那怎麼辦呢?其實是我們的MessageBox阻塞了窗體Close的過程才有這個異常。那麼解決方案就出來,讓MessageBox不阻塞UI線程不就得了。代碼如下 :

public static void OnWindowClosingAdv(object sender, CancelEventArgs e)
{
  Prevent Recursion#region Prevent Recursion
  if (new StackTrace().GetFrames().Any((frame) => { return frame.GetMethod().Name == "ConfirmClose"; }))
    return;
  #endregion
  
  e.Cancel = true; //Cancel every close at once.
  
  Window window = sender as Window;
  window.Dispatcher.BeginInvoke(new Action<Window>(ConfirmClose), window);
}
  
private static void ConfirmClose(Window window)
{
  if (MessageBox.Show(string.Format("Are you sure to close the {0}?", window.Title),
    "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes)
  {
    window.Close();
  }
}

注:其中的Prevent Recursion代碼段是為了防止遞歸調用的。完全可以用一個臨時變量代碼代替,而且不會出現硬編碼的字符串。分析調用棧只是為了突出代碼相對於原版的丑惡。^_^

這次還好,為了Fix系統的邏輯BUG,並沒有多寫多少代碼。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved