WPF消息機制

  前言

  談起“消息機制”這個詞瞎领,我們都會想到Windows的消息機制,系統(tǒng)將鍵盤鼠標的行為包裝成一個Windows Message乍桂,然后系統(tǒng)主動將這些Windows Message派發(fā)給特定的窗口侈咕,實際上消息是被Post到特定窗口所在線程的消息隊列,應用程序的消息循環(huán)再不斷的從消息隊列當中獲取消息梳杏,然后再派發(fā)給特定窗口類的窗口過程來處理,在窗口過程中完成一次用戶交互掰读。

  其實,WPF的底層也是基于Win32的消息系統(tǒng)叭莫,那么對于WPF應用程序來說蹈集,它是如何跟Win32的消息交互,這里到底存在一個什么樣的機制雇初?接下來我會通過下面幾篇博文介紹這個消息機制:

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

  WPF的消息機制(二)-WPF內(nèi)部的5個窗口

 ÷K痢(1)隱藏消息窗口

  (2)處理激活和關閉的消息的窗口和系統(tǒng)資源通知窗口

 【甘(3)用于UI窗口繪制的可見窗口

 」帧(4)用于用戶交互的可見窗口

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

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

  讓應用程序動起來

  談到WPF的消息,首先應該知道DispactherObject以及Dispatcher在WPF系統(tǒng)中的作用刊橘。

  WPF大部分的對象都是從DispatcherObject派生的鄙才,從這里派生的對象具有一個明顯的特征,那就是:修改對象時所在的線程促绵,和創(chuàng)建對象時所在線程必須為同一個線程攒庵,這就是微軟所謂的線程親緣性(Thread affinity)的最簡單理解嘴纺。那么誰能保證線程親緣性呢?那就是Dispacher了浓冒。從DispatcherObject派生的類型繼承三個重要的成員:Dispatcher屬性栽渴,CheckAccess(), VerifyAccess()方法。其中后面兩個方法就是檢驗線程親緣性的稳懒。按照WPF的實現(xiàn)闲擦,如果你自己定義了個WPF的類型,并且是DispatcherObject的子類场梆,你就必須在public的成員定義的邏輯開始處墅冷,調(diào)用base.Dispatcher.VerifyAccess(),檢驗線程親緣性辙谜。那么Dispatcher到底還做了什么事情呢俺榆?

  首先,我們看一下一個WPF的Application在啟動之后都走了哪些邏輯:

  通過調(diào)用堆椬岸撸可以看出罐脊,藍色的部分是啟動了一個線程,VisualStudio在Host的進程當中運行當前應用程序蜕琴;紅色的部分是從Application.Main函數(shù)開始執(zhí)行萍桌,經(jīng)過幾個函數(shù)到達Dispatcher.Run(),最后到達Dispather.PushFrameInpl()方法凌简。那么一個Application在Run之后,為什么要調(diào)用Dispatcher.Run()呢上炎,他做了些什么事情你?如果通過Reflector仔細查看Application.Run()雏搂,你會發(fā)現(xiàn)里面實際起作用的代碼并不多藕施,最后都是Dispatcher.Run在做事情。那么一個Application啟動之后凸郑,按照以前對Win32的消息機制的理解裳食,當應用程序啟動后,必須進入消息循環(huán)芙沥,對于WPF诲祸,也是一樣的。那么WPF應用程序是在什么地方進入消息循環(huán)呢而昨?其實這就是Dispatcher.Run()做的事情救氯。查看上圖最后一步Dispacther.PushFrameImpl()的代碼,你會看到有下面的一段代碼:

  很明顯歌憨,橙色的部分是一個循環(huán)着憨,看起來是不是很眼熟,跟Win32編程碰到的消息循環(huán)是否很像务嫡?對了享扔,這就是WPF應用程序進入了消息循環(huán)底桂。循環(huán)調(diào)用GetMessage方法從當前線程的消息隊列當中不停的獲取消息,取出一個msg之后惧眠,交給TranslateAndDispatchMessage方法Dispatch到不同的窗口過程去處理籽懦。這樣以來,任何需要應用程序處理的消息通過這個過程氛魁,被不同的窗口處理了暮顺,應用程序就動起來了。

  下面的一篇我會介紹WPF當中的Win32窗口秀存,正是這些窗口捶码,處理著來自系統(tǒng),或者來自應用程序內(nèi)部的消息或链。



(1)隱藏消息窗口

(2)處理激活和關閉的消息的窗口和系統(tǒng)資源通知窗口

(3)用于用戶交互的可見窗口

(4)用于UI窗口繪制的可見窗口

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

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


WPF內(nèi)部的5個窗口

對于Windows系統(tǒng)來說惫恼,它是一個消息系統(tǒng),消息系統(tǒng)的核心就是窗口澳盐。對于WPF來說也是如此祈纯。那么WPF內(nèi)部為什么需要窗口,又存在哪些窗口呢叼耙?

在上一篇腕窥,我們頻繁的提及“線程”,“Dispatcher”其實筛婉,運行WPF應用程序所在的線程就是WPF所謂的UI線程簇爆,在Application.Run之后,調(diào)用Dispatcher.Run時會檢查當前線程是否已經(jīng)存在了一個Dispatcher對象爽撒,如果沒有就構(gòu)造一個入蛆,在這里,一個線程對應一個Dispatcher硕勿。因此哨毁,WPF的對象在獲取this.Dispatcher屬性時,不同對象取的都是同一個Dispatcher實例首尼。另外挑庶,前面提到的“消息循環(huán)”言秸,“消息隊列”等都是Win32應用程序的概念软能,我們知道,提起這些概念举畸,必然會跟Win32的“窗口”查排,“Handle”,“WndProc”之類的概念離不開抄沮,那么WPF里面究竟有沒有“窗體”跋核,“Handle”岖瑰,“WndProc”呢?

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

通常情況下露戒,一個WPF應用程序在運行起來的時候,后臺會創(chuàng)建5個Win32的窗口捶箱,幫助WPF系統(tǒng)來處理操作系統(tǒng)以及應用程序內(nèi)部的消息智什。在這5個窗口中,只有一個是可見的丁屎,可以處理輸入事件與用戶交互荠锭,其他4個窗口都是不可見的,幫助WPF處理來自其他方面的消息晨川。接下來我會來介紹究竟這5個Win32的窗口如何幫助WPF處理消息证九,我會根據(jù)每個窗口創(chuàng)建的順序來介紹。


隱藏消息窗口

創(chuàng)建時機:在Application的構(gòu)造函數(shù)調(diào)用基類DispatcherObject的構(gòu)造函數(shù)的時候础爬,會創(chuàng)建一個Dispatcher對象甫贯,在Dispatcher的私有構(gòu)造函數(shù)當中。

用途:實現(xiàn)WPF線程模型的異步調(diào)用看蚜。

談到異步調(diào)用叫搁,相信許多人都不陌生。WinForm下供炎,我們通常為了使一些花費較多時間的方法調(diào)用不影響UI的響應渴逻,會將這個操作分為很多步,然后使用BeginInvoke調(diào)用每一步音诫,這樣UI響應就不會被阻塞惨奕。BeginInvoke的本質(zhì)是往消息隊列當中PostMessage,而不是直接調(diào)用竭钝,與此同時梨撞,UI行為(MouseMove)導致系統(tǒng)也往消息隊列當中PostMessage更新UI,但由于彼此花費的時間很短香罐,就感覺兩個消息是被同時處理似的卧波,界面就不會覺得被阻塞了。WPF同樣面臨這樣的問題庇茫,他是如何解決的呢港粱?在這里Window 1#起著至關重要的作用。通過下面一副圖我們來看看Window 1#在做什么事情旦签?

WPF也是通過BeginInvoke來解決的查坪,而Wpf的BeginInvoke是在Dispatcher上面暴露了寸宏,因為整個消息系統(tǒng)都是Dispatcher在協(xié)調(diào)。從上面圖可以看出Dispatcher在調(diào)用BeginInvoke之后所經(jīng)歷的流程偿曙,最終是什么時候Foo()被真正執(zhí)行的氮凝。

第一步,就是將調(diào)用的Delegate和優(yōu)先級包裝成一個DispatcherOperation放入Dispatcher維護的優(yōu)先級隊列當中望忆,這個Queue是按DispatcherPriority排序的覆醇,總是高優(yōu)先級的DispatcherOperation先被處理。關于優(yōu)先級相關知識可以參考MSDN對WPF線程模型的解釋炭臭。

第二步永脓,往當前線程的消息隊列當中Post一個名為MsgProcessQueue的Message。這個消息是WPF自己定義的鞋仍,見Dispatcher的靜態(tài)構(gòu)造函數(shù)當中的

_msgProcessQueue=UnsafeNativeMethods.RegisterWindowMessage("DispatcherProcessQueue");

這個消息被Post到消息隊列之前常摧,還要設置MSG.Handle,這個Handle就是Window 1#的Handle威创。指定Handle是為了在消息循環(huán)Dispatch消息的時候落午,指定哪個窗口的WndProc(窗口過程)處理這個消息。在這里所有BeginInvoke引起的消息都是Window1#的窗口過程來處理的肚豺。

第三步溃斋,消息循環(huán)讀取消息。

第四步吸申,系統(tǒng)根據(jù)獲取消息的Handle梗劫,發(fā)現(xiàn)跟Window1#的Handle相同,那么這個消息派發(fā)到Window1#的窗口過程截碴,讓其處理梳侨。

第五步,在窗口過程中日丹,優(yōu)先級隊列當中取一個DispatcherOperation走哺。

第六步,執(zhí)行DispatcherOperation.Invoke方法哲虾,Invoke方法的核心就是調(diào)用DispatcherOperation構(gòu)造時傳入的Delegate丙躏,也就是Dispatcher.BeginInvoke傳入的Delegate。最終這個Foo()方法就被執(zhí)行了束凑。

通過上面的六步過程晒旅,一次Dispatcher.BeginInvoke就被處理完成。而這個過程需要消息不斷的流動湘今,就必須加入消息隊列敢朱,最后還要特定的窗口過程處理剪菱,而核心的東西就是這個隱藏的Window1#摩瞎,他在WPF當中只負責處理異步調(diào)用拴签,其他的消息他不關心,剩余的4個窗口在處理旗们。這個Window1#在WPF當中被包了一層殼子蚓哩,如果感興趣,你可以去查看類型MessageOnlyHwndWrapper上渴。




處理應用程序激活和系統(tǒng)關閉的窗口(Window 2#)

創(chuàng)建時機:在調(diào)用Application.Run之后岸梨,運行到Application.EnsureHwndSource()方法當中。

用途:派發(fā)Application的Activated稠氮,Deactivated曹阔,SessionEnding事件。

WPF為了安全起見沒有讓UI窗口來處理應用程序激活隔披,反激活赃份,以及操作系統(tǒng)關閉時對應的消息,而是內(nèi)部創(chuàng)建了一個隱藏的窗口奢米,專門用來接收WM_ACTIVATEAPP和WM_QUERYENDSESSION兩個Windows消息抓韩。從線程的消息隊列拿到這兩個消息后,會觸發(fā)WPF的Application.Activated鬓长,Application.Deactivated谒拴,Application.SessionEnding這三個事件。

更詳細的可參考Application類型的EnsureHwndSource(),AppFilterMessage(),這兩個方法涉波。

上面的過程可用下圖描述:




系統(tǒng)資源更改通知窗口(Window 4#)

創(chuàng)建時機:Application的MainWindow的Xaml被反序列化成對象之后英上,需要確認Window的ThemeStyle的時候。

用途:處理當操作系統(tǒng)的Theme發(fā)生改變后啤覆,以及諸如SystemColors善延,SystemFonts,電源城侧,顯示器等跟系統(tǒng)關聯(lián)的資源發(fā)生改變時易遣,更新WPF這邊的表現(xiàn)。

WPF在應用出現(xiàn)的MainWindow在初始化完成后嫌佑,會創(chuàng)建一個隱藏的窗口豆茫,專門處理來自系統(tǒng)相關資源更新后的消息,比如WM_ThemeChanged屋摇,WM_SystemColorChanged揩魂,WM_DisplayChange,WM_PowerBroadcast等等。跟Window2#的初衷類似炮温,為了安全起見火脉,沒有通過可見的UI窗口來處理這些消息,而是內(nèi)容創(chuàng)建了這個隱藏的Window4#窗口來處理這些消息,確保UI窗口可以安全的更新由于系統(tǒng)Theme及相關資源改變后的表現(xiàn)倦挂。

上面的過程可用下圖描述:


也許你會問為什么先講解了Window4畸颅,而沒有講用于用戶交互的可見窗口(Window3)?那是因為Window3的內(nèi)容比較多方援,而Window2#和Window4#相對比較獨立没炒,因此,放在本文當中介紹犯戏,關于Window3的詳細描述送火,將在下一篇介紹, 敬請關注!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末先匪,一起剝皮案震驚了整個濱河市种吸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌呀非,老刑警劉巖骨稿,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姜钳,居然都是意外死亡坦冠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門哥桥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辙浑,“玉大人辕漂,你說我怎么就攤上這事血淌。” “怎么了磁浇?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵送滞,是天一觀的道長侠草。 經(jīng)常有香客問我,道長犁嗅,這世上最難降的妖魔是什么边涕? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮褂微,結(jié)果婚禮上功蜓,老公的妹妹穿的比我還像新娘。我一直安慰自己宠蚂,他們只是感情好式撼,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著求厕,像睡著了一般著隆。 火紅的嫁衣襯著肌膚如雪扰楼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天美浦,我揣著相機與錄音弦赖,去河邊找鬼。 笑死抵代,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的忘嫉。 我是一名探鬼主播荤牍,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼庆冕!你這毒婦竟也來了康吵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤访递,失蹤者是張志新(化名)和其女友劉穎晦嵌,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拷姿,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡惭载,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了响巢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片描滔。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖踪古,靈堂內(nèi)的尸體忽然破棺而出含长,到底是詐尸還是另有隱情,我是刑警寧澤伏穆,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布拘泞,位于F島的核電站,受9級特大地震影響枕扫,放射性物質(zhì)發(fā)生泄漏陪腌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一烟瞧、第九天 我趴在偏房一處隱蔽的房頂上張望偷厦。 院中可真熱鬧,春花似錦燕刻、人聲如沸只泼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽请唱。三九已至弥咪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間十绑,已是汗流浹背聚至。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留本橙,地道東北人扳躬。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像甚亭,于是被迫代替她去往敵國和親贷币。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理亏狰,服務發(fā)現(xiàn)役纹,斷路器,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 目錄 什么是WPF暇唾? WPF的歷史促脉? 為什么要用WPF及WPF作用 WPF與winForm區(qū)別? 什么是WPF策州? ...
    灬52赫茲灬閱讀 5,811評論 2 11
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,386評論 8 265
  • Windows 常用消息大全 表A-1 Windows消息分布 消息范圍說 明 0 ~ WM_USER – 1系統(tǒng)...
    北風知我意閱讀 2,049評論 0 0
  • 這大概是我水彩最放的一次了~水彩是美瘸味,可是我畫不出來╭(°A°`)╮
    陳新如閱讀 642評論 2 8