1. 程式人生 > >父視窗與擁有者視窗(Parent VS Owner)

父視窗與擁有者視窗(Parent VS Owner)

Owner 在視窗建立時指定,以後不能更改。“一旦擁有,一直擁有”。

Parent 可以再建立時指定,以後可以更改。“父親可以是繼父”。

一、 視窗的Parent、Owner關係

    視窗有兩種可能的上下級關係,一種是Owner,一種是parent。

    建立視窗時,有WS_POPUP屬性的視窗,它的父視窗其實是Owner視窗。建立之後,可以通過SetParent,為它設定父視窗,這樣子他就有父視窗(子視窗位置限制在父視窗中)又有POPUP屬性。

    建立視窗時,有WS_CHILD屬性的視窗,它的父視窗就是父視窗。可以通過SetWindowLongPtr給它加上POPUP屬性。

二、當Owner、parent視窗最小化時,子視窗的反應

    Owner關係的視窗。

        Owner視窗隱藏,子視窗不會隱藏。

        Owner視窗最小化,子視窗隱藏。

    Parent關係的視窗。

         Parent視窗隱藏,子視窗隱藏。

         Parent視窗最小化,子視窗隱藏。

         子視窗的隱藏是通過傳送WM_SHOWWINDOW訊息做的。

         所以,當有一個功能是:owner視窗隱藏,子視窗也要隱藏時,就需要自己在owner視窗的隱藏訊息處理裡去實現子視窗的隱藏。

         當父視窗最小化、隱藏時,通過Spy++,可以發現子視窗還有WS_VISIBLE屬性。

         當Owner視窗最小化,通過SPY++,可以發現子視窗沒有WS_VISIBLE屬性。

三、Owner和Parent的其它相關知識

       GetParent( HWND hWnd ),獲取視窗的父視窗或者owner視窗。如果視窗是child視窗,則返回parent視窗控制代碼,如果視窗是頂層視窗,則返回owner視窗。如果頂層視窗沒有owner或者函式失敗,則返回0.

      頂層視窗:應該是指使用spy++可以抓到的視窗,WS_CHILD屬性的視窗就抓不到。

      ShowOwnedPopups();函式,可以讓因最小化而隱藏的owner子視窗顯示。

四、之前遇到的誤解

      半年多以前在WTL下做開發的時候,對子視窗的WM_SHOWWINDOW的處理,沒有把bHandled修改為0,導致DefWindowProc函式沒有被呼叫,按照MSDN的說法,DefWindowProc函式負責了視窗的隱藏、顯示,於是當因為父視窗最小化時,子視窗雖然收到了WM_SHOWWINDOW的隱藏訊息但並沒有隱藏。當時在網路上找到了一篇文章《VC/MFC 程式最小化後不能還原的原因與解決方法》,以為是這個原因導致的,於是在父視窗最小化之前,先把子視窗隱藏,父視窗還原,再讓子視窗顯示。陰錯陽差的以為是這個原因,最近再做測試,才發現,根本不是這麼回事,那篇文章說的是另一種情況。

       又想到另一個問題,為什麼直接呼叫ShowWindow,可以使得視窗隱藏,而主視窗Mini觸發的子視窗WM_SHOWWINDOW訊息,卻會因為訊息響應函式裡的處理導致視窗不隱藏?有可能是windows系統內部對主動呼叫ShowWindow的做了處理,而對訊息觸發的則通過DefWindowProc做處理。

      《VC/MFC 程式最小化後不能還原的原因與解決方法》這篇文章所講的情況,我測試了A擁有B,或者A有子視窗B,在正常操作的情況下都沒出現。所以,我懷疑所說的應該是另一種情況:A擁有(Owner)B,B擁有(Owner)C的情況下,把Activate切換到C視窗,然後對A視窗最小化,這時候C視窗就無法最小化,而且無法從工作列直接還原,需要使用系統選單還原,這就是所謂的訊息被activate子視窗攔截了。在非正常操作的情況下,這篇文章所講的就很容易出現了,譬如:owner視窗最小化,但產品功能要求popup子視窗依舊顯示,活動切到子視窗,這時候如果不做處理,那麼父視窗就無法單擊工作列還原了。那麼如何處理呢?當popup子視窗顯示,而owner視窗最小化到了工作列,這個時候,你想通過點選工作列恢復owner視窗,該如何處理?我覺得是無法處理的,至少我沒想到簡單便捷的邏輯去實現,看下邊五的分析。

五、當popup子視窗顯示而owner最小化在工作列時,無法點選工作列還原owner視窗(2012-7-11補充)

      假設通過某些方式,讓popup子視窗顯示,而owner視窗最小化在工作列,我想通過點選工作列還原owner視窗,我認為這是無法解決的問題。用spy++抓到的訊息顯示,當點選工作列的圖示時,系統只給活動視窗所線上程的每個視窗傳送了WM_WINDOWPOSCHANGING、WM_ACTIVATEAPP訊息,也就是說,這種情況下點選工作列圖示,我的程式並不知道我此時是點選了工作列還是拖動了popup子視窗。於是這個問題沒法解決(也許可以通過滑鼠位置判斷當前是否在點選工作列區域,但打補丁的方式總會容易有陷阱)。那麼是否有軟體能做到呢?目前我沒看到能解決這個問題的例子,如果它們有點小聰明,那麼會想到“為什麼我要解決呢?”,問題本身就不該存在,那麼避開就行了。

  注意到QQ影音播放器會有一個選項,讓你最小化到托盤區或者最小化到工作列,當最小化到托盤區時,可以讓播放器設定視窗單獨顯示,而最小化到工作列就則不可以讓播放器設定視窗單獨顯示,這就避開了這個問題。

  曾經的百度ting客戶端,最小化到工作列,然後從托盤區的“全域性設定”項開啟“設定”視窗,會發現無法通過點選工作列選單還原ting的主介面。

     之前用過的金山毒霸,也存在子視窗顯示,父視窗最小化在托盤區和工作列,點選工作列無法還原主視窗的情況。

  暴風影音,可以看到托盤區有兩個開啟視窗的選項,一個是“下載管理”,一個是“高階設定”,下載管理是獨立的視窗,跟播放器主視窗沒有關係,所以開啟下載視窗的時候,播放器主視窗後續的操作不受影響。“高階設定”視窗的owner視窗是播放器主視窗,它為了避開這個無法解決的問題,就不會讓你單獨開啟高階設定視窗,開啟設定視窗時,播放器主視窗都會出現。類似的快播播放器,也是讓設定視窗顯示時,主視窗也顯示,避免了這個問題。