WPF RoutedEvent and HitTest - 簡書
阿新 • • 發佈:2019-04-10
知識點 開源 graphic param ref lin sta edev orien 原文:WPF RoutedEvent and HitTest - 簡書
學習的時候切忌心浮氣躁,慢慢的過每一個知識點,不要漏掉任何細節。不然當遇到細節問題的時候,會惱,會鬧,會悔不該當初——花一下午調bug最後只改了一個參數有感。
相信很多用過WPF的人都知道WPF中的路由事件。一般看書的話,這個知識點也會在前幾章講到。總的來說,也就是
- WPF的控件都存在於一顆Visual tree當中。
- 事件在控件中的傳遞,其實也就是事件在Visual tree中的傳遞
- 隧道事件從上往下,由樹根開始向葉子傳播
- 冒泡事件從下往上,由子節點開始向根傳播
假設我們有一個Visual tree長這樣:
MainWindow
|_Border
|_Grid
|_TextBlock
那麽如果用戶點擊了TextBlock
。那麽會產生什麽事件,然後會怎麽傳遞呢?
答案是
- 會產生
PreviewMouseDown
和MouseDown
事件 -
PreviewMouseDown
是隧道事件,事件的順序是MainWindow
->Border
->Grid
->TextBlock
-
MouseDown
是冒泡事件,事件的順序與之前相反,是TextBlock
->Grid
->Border
->MainWindow
Tips:
example
如何查看WPF中的事件?有一個開源工具snoop可以幫助你。下圖是一個實際示例,UI結構以及操作和上述一致。
好,我們再看一個例子。
MainWindow
|_Border
|_Grid
|_TextBlock(Margin="32")
和上個例子不同的地方在於,我們把TextBlock
的邊距擴大了。這就意味著,我們可以點擊在TextBlock
的邊距上,那麽會發生什麽呢?先自己想想哦。
註意這裏的border是window中自帶的,不是我們自己聲明的。所以正確答案是,只傳播到了
MainWindow
。為了區別,我給聲明的Border
隨便起了個名字。
要是答對了的同學,那不是一般的棒!
我們這裏有兩個問題:
-
PreviewMouseDown
只傳遞到了MainWindow
。作為一個隧道事件,沒有繼續往下傳遞。 - 觸發事件的是
MainWindow
,不是Border
也不是Grid
。
先解答第一個問題。路由事件的準確觸發順序應該是
- 隧道事件從根開始,傳播到產生事件的控件為止。如果中間有控件處理(
e.Handled = true
)掉,就停止傳播。 - 冒泡事件從產生事件的控件開始,傳播到根節點為止。如果中間有控件處理(
e.Handled = true
)掉,就停止傳播。 - 系統提供的Preview事件先觸發。如果被處理(
e.Handled = true
)掉,不會在產生對應的冒泡事件。
第二個問題就很惱人了。總的來說就是
- 如果沒有控件沒有被渲染,那麽該控件不能被HitTest,也不能被路由事件觸發。(參見這裏)
也就是說,Border
和Grid
沒能觸發,是因為他們沒有Background
,沒有被渲染。如果加上,即使你加的是TransParent
,也會有效。
添加了TransParent為Border的背景
添加了TransParent為Grid的背景
也就是說,如果你希望下面的控件能夠觸發事件。那麽讓上面的控件不能被HitTest就可以了。我今天遇到的坑是,上面一層自己畫的一個框,用的函數是
dc.DrawGeometry(Brushes.Transparent, new Pen(brush, GraphicsLineWidth), PathGeometry);
改成
dc.DrawGeometry(null, new Pen(brush, GraphicsLineWidth), PathGeometry);
就好了。
WPF RoutedEvent and HitTest - 簡書