程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF Bug清單之(5)——隱藏模態對話框後變成非模態

WPF Bug清單之(5)——隱藏模態對話框後變成非模態

編輯:關於.NET

發現這個問題時,隱約記得之前有人已經發過這個問題,想把鏈接放到這裡,不過找了半天,實在找不到。日後如果找到了一定加上。

問題描述:用ShowDialog方法彈出一個模態對話框,然後將此對話框的Visibility屬性設置為Hidden,再設置回Visible,發現這個對話框已經不是模態的了。

有人會覺得關就關了得了,也不會有這個問題,干什麼要把Close取消掉然後再顯示出來呢?因為這是有應用環境的。

應用環境:有些對話框,從邏輯上就是單例的,比如Office和Visual Studio裡都有的查找對話框,顯然沒有必要同時顯示兩個。而且也沒有必要每次重新實例化並顯示出來,在用戶關閉窗體時,將窗體隱藏起來會更好,這樣上次查找的關鍵字還存在著。可以省去一些代碼保存這個歷史關鍵字。

當然,這種方式也會有不好的地方,歡迎大家指摘。

寫了一個程序來模擬這個Bug,效果如下面三張圖所示。

圖1. 主窗體,點第一個按鈕

圖2. 彈出的模態對話框,點擊按鈕將自己隱藏

圖3. 再點擊主窗體的最後一個按鈕,顯示出來,已經是非模態對話框了

以前發Bug,一般沒有去看過.NET的源代碼,這次感覺這個Bug 有點兒太不應該了,就看了看源代碼,發現WPF還特意為Dialog(模態的)的Hidden做了單獨的處理,感覺就更不應該有問題了,我們來看看源代碼。

DoDialogHide

[SecurityCritical, SecurityTreatAsSafe]
private void DoDialogHide()
{
   SecurityHelper.DemandUnmanagedCode();
   bool isActiveWindow = false;
   if (this._dispatcherFrame != null)
   {
     this._dispatcherFrame.Continue = false;
     this._dispatcherFrame = null;
   }
   if (!this._dialogResult.HasValue)
   {
     this._dialogResult = false;
   }
   this._showingAsDialog = false;    //Cause this Bug
   isActiveWindow = this._swh.IsActiveWindow;
   this.EnableThreadWindows(true);
   if ((isActiveWindow && (this._dialogPreviousActiveHandle != IntPtr.Zero)) && UnsafeNativeMethods.IsWindow(new HandleRef(this, this._dialogPreviousActiveHandle)))
   {
     UnsafeNativeMethods.SetActiveWindow(new HandleRef(this, this._dialogPreviousActiveHandle));
   }
}

其中直接把_showingAsDialog設置為了false,當再次把窗體的Visibility設置為Visible的時候,Window類又會根據這個變量的值來判斷是否將窗體按模態的方式顯示出來。而MS對這行代碼的的注釋僅僅是“// clears _showingAsDialog”。

從源代碼上來看,WPF的Window似乎是使用下面的代碼將一個窗體從非模態變成模態的。

SetAsModal

try
{
   //telluserswe'regoingmodal
   ComponentDispatcher.PushModal();
   _dispatcherFrame=newDispatcherFrame();
   Dispatcher.PushFrame(_dispatcherFrame);
}
finally
{
   //telluserswe'regoingnon-modal
   ComponentDispatcher.PopModal();
}

但是當我自己在使用裡使用這個方法的時候,卻發現根本達不到目的。後來突然想到一個方法,試了一下,就可以。解決方法是,不使用Visibility = Visible,使窗體再次顯示出來。而且再調用一次ShowDialog方法來顯示這個窗體。這個方法也許只有對WPF不熟悉或是非常熟悉的人才能想得出來(我是死馬當作活馬醫碰對了),因為正常情況下,繼續地第二次調用ShowDialog方法是會拋出異常的。類似的詭異的Window的異常在[WPF]如何在關閉非模態子窗體時用消息框確認——解決最小化窗體時拋出的異常裡也有描述。

另外,在非UI線程彈出的MessageBox也是非模態的。這個解決方法很簡單,只要在Dispatcher裡彈出這個MessageBox就可以了。

本文配套源碼

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