WPF 如何控制右键菜单ContextMenu的弹出

      在具体作一些项目的时候,有时候须要须要先左键点击某个节点,而后再右键点击节点的时候才弹出右键菜单,因此直接右键点击时须要禁用掉右键菜单,这里好比咱们为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高级编程》,看到事件的上传和下传。有个例子:this

  前台代码:spa

<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事件。设计

  后台代码以下:xml

   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方法,显式的增长这个事件。blog

---------方法说明:---------------------------------------------------------------------------------事件

UIElement.AddHandler 方法 (RoutedEvent, Delegate, Boolean)ci

  为指定的路由事件添加路由事件处理程序,并将该处理程序添加到当前元素的处理程序集合中。将 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类型,经过这个变量,能够访问这个控件提供的全部本地属性,方法和事件。

相关文章
相关标签/搜索