WPF的消息機(jī)制(一)-讓應(yīng)用程序動(dòng)起來(lái)
WPF的消息機(jī)制(二)-WPF內(nèi)部的5個(gè)窗口
(1)隱藏消息窗口
(2)處理激活和關(guān)閉的消息的窗口和系統(tǒng)資源通知窗口
(3)用于用戶交互的可見窗口
(4)用于UI窗口繪制的可見窗口
WPF的消息機(jī)制(三)-WPF輸入事件的來(lái)源
WPF的消息機(jī)制(四)-WPF中UI的更新
對(duì)于Windows系統(tǒng)來(lái)說(shuō),它是一個(gè)消息系統(tǒng),消息系統(tǒng)的核心就是窗口。對(duì)于WPF來(lái)說(shuō)也是如此。那么WPF內(nèi)部為什么需要窗口,又存在哪些窗口呢?
在上一篇,我們頻繁的提及“線程”,“Dispatcher”其實(shí),運(yùn)行WPF應(yīng)用程序所在的線程就是WPF所謂的UI線程,在Application.Run之后,調(diào)用Dispatcher.Run時(shí)會(huì)檢查當(dāng)前線程是否已經(jīng)存在了一個(gè)Dispatcher對(duì)象,如果沒(méi)有就構(gòu)造一個(gè),在這里,一個(gè)線程對(duì)應(yīng)一個(gè)Dispatcher。因此,WPF的對(duì)象在獲取this.Dispatcher屬性時(shí),不同對(duì)象取的都是同一個(gè)Dispatcher實(shí)例。另外,前面提到的“消息循環(huán)”,“消息隊(duì)列”等都是Win32應(yīng)用程序的概念,我們知道,提起這些概念,必然會(huì)跟Win32的“窗口”,“Handle”,“WndProc”之類的概念離不開,那么WPF里面究竟有沒(méi)有“窗體”,“Handle”,“WndProc”呢?
我想說(shuō)的是:有,還不止一個(gè),只不過(guò)沒(méi)有暴露出來(lái),外面不需要關(guān)心這些。
通常情況下,一個(gè)WPF應(yīng)用程序在運(yùn)行起來(lái)的時(shí)候,后臺(tái)會(huì)創(chuàng)建5個(gè)Win32的窗口,幫助WPF系統(tǒng)來(lái)處理操作系統(tǒng)以及應(yīng)用程序內(nèi)部的消息。在這5個(gè)窗口中,只有一個(gè)是可見的,可以處理輸入事件與用戶交互,其他4個(gè)窗口都是不可見的,幫助WPF處理來(lái)自其他方面的消息。接下來(lái)我會(huì)來(lái)介紹究竟這5個(gè)Win32的窗口如何幫助WPF處理消息,我會(huì)根據(jù)每個(gè)窗口創(chuàng)建的順序來(lái)介紹。
創(chuàng)建時(shí)機(jī):在Application的構(gòu)造函數(shù)調(diào)用基類DispatcherObject的構(gòu)造函數(shù)的時(shí)候,會(huì)創(chuàng)建一個(gè)Dispatcher對(duì)象,在Dispatcher的私有構(gòu)造函數(shù)當(dāng)中。
用途:實(shí)現(xiàn)WPF線程模型的異步調(diào)用。
談到異步調(diào)用,相信許多人都不陌生。WinForm下,我們通常為了使一些花費(fèi)較多時(shí)間的方法調(diào)用不影響UI的響應(yīng),會(huì)將這個(gè)操作分為很多步,然后使用BeginInvoke調(diào)用每一步,這樣UI響應(yīng)就不會(huì)被阻塞。BeginInvoke的本質(zhì)是往消息隊(duì)列當(dāng)中PostMessage,而不是直接調(diào)用,與此同時(shí),UI行為(MouseMove)導(dǎo)致系統(tǒng)也往消息隊(duì)列當(dāng)中PostMessage更新UI,但由于彼此花費(fèi)的時(shí)間很短,就感覺(jué)兩個(gè)消息是被同時(shí)處理似的,界面就不會(huì)覺(jué)得被阻塞了。WPF同樣面臨這樣的問(wèn)題,他是如何解決的呢?在這里Window 1#起著至關(guān)重要的作用。通過(guò)下面一副圖我們來(lái)看看Window 1#在做什么事情?
WPF也是通過(guò)BeginInvoke來(lái)解決的,而Wpf的BeginInvoke是在Dispatcher上面暴露了,因?yàn)檎麄€(gè)消息系統(tǒng)都是Dispatcher在協(xié)調(diào)。從上面圖可以看出Dispatcher在調(diào)用BeginInvoke之后所經(jīng)歷的流程,最終是什么時(shí)候Foo()被真正執(zhí)行的。
第一步,就是將調(diào)用的Delegate和優(yōu)先級(jí)包裝成一個(gè)DispatcherOperation放入Dispatcher維護(hù)的優(yōu)先級(jí)隊(duì)列當(dāng)中,這個(gè)Queue是按DispatcherPriority排序的,總是高優(yōu)先級(jí)的DispatcherOperation先被處理。關(guān)于優(yōu)先級(jí)相關(guān)知識(shí)可以參考MSDN對(duì)WPF線程模型的解釋。
第二步,往當(dāng)前線程的消息隊(duì)列當(dāng)中Post一個(gè)名為MsgProcessQueue的Message。這個(gè)消息是WPF自己定義的,見Dispatcher的靜態(tài)構(gòu)造函數(shù)當(dāng)中的
_msgProcessQueue = UnsafeNativeMethods. 這個(gè)消息被Post到消息隊(duì)列之前,還要設(shè)置MSG.Handle,這個(gè)Handle就是Window 1#的Handle。指定Handle是為了在消息循環(huán)Dispatch消息的時(shí)候,指定哪個(gè)窗口的WndProc(窗口過(guò)程)處理這個(gè)消息。在這里所有BeginInvoke引起的消息都是Window1#的窗口過(guò)程來(lái)處理的。 第三步,消息循環(huán)讀取消息。 第四步,系統(tǒng)根據(jù)獲取消息的Handle,發(fā)現(xiàn)跟Window1#的Handle相同,那么這個(gè)消息派發(fā)到Window1#的窗口過(guò)程,讓其處理。 第五步,在窗口過(guò)程中,優(yōu)先級(jí)隊(duì)列當(dāng)中取一個(gè)DispatcherOperation。 第六步,執(zhí)行DispatcherOperation.Invoke方法,Invoke方法的核心就是調(diào)用DispatcherOperation構(gòu)造時(shí)傳入的Delegate,也就是Dispatcher.BeginInvoke傳入的Delegate。最終這個(gè)Foo()方法就被執(zhí)行了。 通過(guò)上面的六步過(guò)程,一次Dispatcher.BeginInvoke就被處理完成。而這個(gè)過(guò)程需要消息不斷的流動(dòng),就必須加入消息隊(duì)列,最后還要特定的窗口過(guò)程處理,而核心的東西就是這個(gè)隱藏的Window1#,他在WPF當(dāng)中只負(fù)責(zé)處理異步調(diào)用,其他的消息他不關(guān)心,剩余的4個(gè)窗口在處理。這個(gè)Window1#在WPF當(dāng)中被包了一層殼子,如果感興趣,你可以去查看類型MessageOnlyHwndWrapper。 ComponentOne Studio WPF 是專為桌面應(yīng)用程序開發(fā)所準(zhǔn)備的一整套控件包,崇尚優(yōu)雅和創(chuàng)新,以“觸控優(yōu)先”為設(shè)計(jì)理念,內(nèi)含輕量級(jí)高性能表格控件,和大量類型豐富的2D和3D圖表控件,能使開發(fā)的應(yīng)用程序更富創(chuàng)意。開發(fā)工具
本文是由葡萄城控件技術(shù)開發(fā)團(tuán)隊(duì)發(fā)布,轉(zhuǎn)載請(qǐng)注明出處:葡萄城控件
了解更多開發(fā)工具和技巧,請(qǐng)前往葡萄城控件官網(wǎng)
了解企業(yè)級(jí)報(bào)表和Web應(yīng)用,請(qǐng)前往葡萄城企業(yè)軟件網(wǎng)站
聯(lián)系客服