在具體做一些項目的時候,有時候需要需要先左鍵點擊某個節點,然后再右鍵點擊節點的時候才彈出右鍵菜單,所以直接右鍵點擊時需要禁用掉右鍵菜單,這里比如我們為Grid添加了ContextMenu,但是我們需要設置一個bool型的變量isSelected,當我們執行到MouseLeftButtonDown事件中的時候,我們就可以將isSelected設置為true,然后在Grid中添加PreviewMouseRightButtonUp="OnMouseRightButtonUp"(隧道事件路由)事件或者是MouseRightButtonUp(冒泡事件路由)事件,在本項目中兩者都可以,但是不要添加在<Grid.ContextMenu/>及其子節點下面,這個稍后會介紹,然后在主程序中執行下面的代碼:
private void OnMouseRightButtonUp(object sender,MouseButtonEventArgs e) { if(isSelected==true) { //使消息可以傳遞 e.Handled=false; } else { e.Handled=true; } isSelected=false; }
這樣當我們先左鍵點擊某一個節點的時候,然后再點擊右鍵的時候才能彈出ContextMenu,這里我們需要正確理解Handled的含義,Handled將路由事件標記為已處理的值。如果 Handled 的值為 true,則可以防止事件路由路徑上的大多數處理程序再次處理同一事件。這在實際的事件處理中是非常有用的,因為WPF的這種層層嵌套的UI設計思想,使得某一個事件,例如UIElement.MouseLeftButtonDown在很多元素上都會觸發,根據事件的不同類型,可以有冒泡觸發或者是隧道的這種方式來觸發,但事實上只有一個元素最終執行該事件,如果某一個元素已經執行過該事件,那么它就會將Handled 的值設為 true,這樣事件就不會向下或者向上路由了,如果想讓事件繼續路由就要將Handled設置為false這個是非常重要的,下面的內容參考一下別人寫的一篇博客,很有意義。
在讀《WPF高級編程》,看到事件的上傳和下傳。有個例子:
前台代碼:
<Window x:Class="TunnelingBubbling.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" > <Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown" Width="313" Height="271" Name="mygrid"> <Button PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown" MouseLeftButtonDown="Button_MouseLeftButtonDown" Click="Button_Click" Name="btnGo"> <TextBox Name="textBox1" Width="173" Height="27" MouseLeftButtonDown="textBox1_MouseLeftButtonDown" PreviewMouseLeftButtonDown="textBox1_PreviewMouseLeftButtonDown"> </TextBox> </Button> </Grid> </Window>
分別為Grid,Button,TextBox定義了PreviewMouseLeftButtonDown和MouseLeftButtonDown事件,並且為Button定義的Click事件。
后台代碼如下:
public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("Grid_PreviewMouseLeftButtonDown"); } private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("Button_PreviewMouseLeftButtonDown"); } private void textBox1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("textBox1_PreviewMouseLeftButtonDown"); } private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("Grid_MouseLeftButtonDown"); } private void Button_Click(object sender, RoutedEventArgs e) { Debug.WriteLine("Button_Click"); } private void textBox1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("textBox1_MouseLeftButtonDown"); } private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("Button_MouseLeftButtonDown"); } }
但是在執行的時候,MouseLeftButtonDown事件總是無法觸發。查看了一些資料,才知道這是一個設計原則上的問題。控件在捕獲了MouseLeftButtonDown事件后,會將該事件的“Handled”設置為True,這個屬性是用在事件路由中的,當某個控件得到一個RoutedEvent,就會檢測Handled是否為true,為true則忽略該事件。
並且,控件本身的Click事件,相當於將MouseLeftButtonDown事件抑制(Supress)掉了,轉換成了Click事件。所以,如果一定要使用這個事件的話,需要在初始化的函數里利用UIElement的AddHandler方法,顯式的增加這個事件。
---------方法說明:---------------------------------------------------------------------------------
UIElement.AddHandler 方法 (RoutedEvent, Delegate, Boolean)
為指定的路由事件添加路由事件處理程序,並將該處理程序添加到當前元素的處理程序集合中。將 handledEventsToo 指定為 true 時,可為已標記為由其他元素在事件路由過程中處理的路由事件調用所提供的處理程序。
參數說明:
handledEventsToo類型:System..::.Boolean
如果為 true,則將按以下方式注冊處理程序:即使路由事件在其事件數據中標記為已處理,也會調用該處理程序;如果為 false,則使用默認條件注冊處理程序,即當路由事件被標記為已處理時,將不調用處理程序。
默認值為 false。
修改后的程序代碼如下:
public Window1() { InitializeComponent(); mygrid.AddHandler(Grid.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.Grid_MouseLeftButtonDown), true); btnGo.AddHandler(Button.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.Button_MouseLeftButtonDown), true); textBox1.AddHandler(TextBox.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.textBox1_MouseLeftButtonDown), true); }
再次運行,調用成功,事件的執行順序是:
次序 |
控件 |
事件 |
事件類型 |
1 |
Grid | PreviewMouseLeftButtonDown | 下傳 |
2 |
Button | PreviewMouseLeftButtonDown | 下傳 |
3 |
TextBox | PreviewMouseLeftButtonDown | 下傳 |
4 |
TextBox | MouseLeftButtonDown | 上傳 |
5 |
Button | MouseLeftButtonDown | 上傳 |
6 |
Grid | MouseLeftButtonDown | 上傳 |
如果點擊Button,則事件的執行順序是:
次序 |
控件 |
事件 |
事件類型 |
1 |
Grid | PreviewMouseLeftButtonDown | 下傳 |
2 |
Button | PreviewMouseLeftButtonDown | 下傳 |
3 |
Button | MouseLeftButtonDown | 上傳 |
4 |
Grid | MouseLeftButtonDown | 上傳 |
5 |
Button | Click |
|
最后補充一點:
在事件處理程序中,可以取消事件處理,查找事件的源元素
例如在下面的代碼中:
private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Debug.WriteLine("Button_PreviewMouseLeftButtonDown"); // e.Handled = true; // Button myButton = e.Source as Button; }
1.取消事件處理: 設置 e.Handled = true;
2.在事件處理程序中查找源元素: e.Source 傳遞一個實際對象實例,可以將其強制轉換為System.Windows.Controls.Button類型,通過這個變量,可以訪問這個控件提供的所有本地屬性,方法和事件。