WPF的訊息機制(二)- WPF內部的5個視窗之隱藏訊息視窗

來源:互聯網
上載者:User
 目錄

WPF的訊息機制(一)-讓應用程式動起來

WPF的訊息機制(二)-WPF內部的5個視窗

(1)隱藏訊息視窗

(2)處理啟用和關閉的訊息的視窗和系統資源通知視窗

(3)用於使用者互動的可見視窗

(4)用於UI視窗繪製的可見視窗

WPF的訊息機制(三)-WPF輸入事件的來源

WPF的訊息機制(四)-WPF中UI的更新

 

WPF內部的5個視窗

對於Windows系統來說,它是一個訊息系統,訊息系統的核心就是視窗。對於WPF來說也是如此。那麼WPF內部為什麼需要視窗,又存在哪些視窗呢?

在上一篇,我們頻繁的提及“線程”,“Dispatcher”其實,運行WPF應用程式所在的線程就是WPF所謂的UI線程,在Application.Run之後,調用Dispatcher.Run時會檢查當前線程是否已經存在了一個Dispatcher對象,如果沒有就構造一個,在這裡,一個線程對應一個Dispatcher。因此,WPF的對象在擷取this.Dispatcher屬性時,不同對象取的都是同一個Dispatcher執行個體。另外,前面提到的“訊息迴圈”,“訊息佇列”等都是Win32應用程式的概念,我們知道,提起這些概念,必然會跟Win32的“視窗”,“Handle”,“WndProc”之類的概念離不開,那麼WPF裡面究竟有沒有“表單”,“Handle”,“WndProc”呢?

我想說的是:有,還不止一個,只不過沒有暴露出來,外面不需要關心這些。

通常情況下,一個WPF應用程式在運行起來的時候,後台會建立5個Win32的視窗,協助WPF系統來處理作業系統以及應用程式內部的訊息。在這5個視窗中,只有一個是可見的,可以處理輸入事件與使用者互動,其他4個視窗都是不可見的,協助WPF處理來自其他方面的訊息。接下來我會來介紹究竟這5個Win32的視窗如何協助WPF處理訊息,我會根據每個視窗建立的順序來介紹。

 

隱藏訊息視窗

建立時機:在Application的建構函式調用基類DispatcherObject的建構函式的時候,會建立一個Dispatcher對象,在Dispatcher的私人建構函式當中。

用途:實現WPF執行緒模式的非同步呼叫。

談到非同步呼叫,相信許多人都不陌生。WinForm下,我們通常為了使一些花費較多時間的方法調用不影響UI的響應,會將這個操作分為很多步,然後使用BeginInvoke調用每一步,這樣UI響應就不會被阻塞。BeginInvoke的本質是往訊息佇列當中PostMessage,而不是直接調用,與此同時,UI行為(MouseMove)導致系統也往訊息佇列當中PostMessage更新UI,但由於彼此花費的時間很短,就感覺兩個訊息是被同時處理似的,介面就不會覺得被阻塞了。WPF同樣面臨這樣的問題,他是如何解決的呢?在這裡Window 1#起著至關重要的作用。通過下面一副圖我們來看看Window 1#在做什麼事情?

WPF也是通過BeginInvoke來解決的,而Wpf的BeginInvoke是在Dispatcher上面暴露了,因為整個訊息系統都是Dispatcher在協調。從上面圖可以看出Dispatcher在調用BeginInvoke之後所經曆的流程,最終是什麼時候Foo()被真正執行的。

第一步,就是將調用的Delegate和優先順序封裝成一個DispatcherOperation放入Dispatcher維護的優先順序隊列當中,這個Queue是按DispatcherPriority排序的,總是高優先順序的DispatcherOperation先被處理。關於優先順序相關知識可以參考MSDN對WPF執行緒模式的解釋。

第二步,往當前線程的訊息佇列當中Post一個名為MsgProcessQueue的Message。這個訊息是WPF自己定義的,見Dispatcher的靜態建構函式當中的

_msgProcessQueue = UnsafeNativeMethods.RegisterWindowMessage("DispatcherProcessQueue");

這個訊息被Post到訊息佇列之前,還要設定MSG.Handle,這個Handle就是Window 1#的Handle。指定Handle是為了在訊息迴圈Dispatch訊息的時候,指定哪個視窗的WndProc(視窗過程)處理這個訊息。在這裡所有BeginInvoke引起的訊息都是Window1#的視窗過程來處理的。

第三步,訊息迴圈讀取訊息。

第四步,系統根據擷取訊息的Handle,發現跟Window1#的Handle相同,那麼這個訊息派發到Window1#的視窗過程,讓其處理。

第五步,在視窗過程中,優先順序隊列當中取一個DispatcherOperation。

第六步,執行DispatcherOperation.Invoke方法,Invoke方法的核心就是調用DispatcherOperation構造時傳入的Delegate,也就是Dispatcher.BeginInvoke傳入的Delegate。最終這個Foo()方法就被執行了。

通過上面的六步過程,一次Dispatcher.BeginInvoke就被處理完成。而這個過程需要訊息不斷的流動,就必須加入訊息佇列,最後還要特定的視窗過程處理,而核心的東西就是這個隱藏的Window1#,他在WPF當中只負責處理非同步呼叫,其他的訊息他不關心,剩餘的4個視窗在處理。這個Window1#在WPF當中被包了一層殼子,如果感興趣,你可以去查看類型MessageOnlyHwndWrapper。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.