【WPF】如何让TreeView实现右键选中的功能

这儿还有更简单的
http://www.cnblogs.com/anqincmt/archive/2008/10/23/1318001.html

有时候我们需要在TreeView中实现这样的功能:

在TreeView上点击右键弹出菜单,同时鼠标点击处的TreeViewItem被选中,然后我们针对选中的数据进行处理。

不过,WPF的TreeView并没有提供右键单击选中的功能。我们需要自己去实现。

【思路】

最基本的思路是,在TreeView右键点击的事件发生时,我们遍历它所有的Item,包括子Item,获取Item所对应的TreeViewItem控件的边界Rect,然后调用Rect的Contains方法判断鼠标是否在该范围内。(另外一种更简单的方式见后面的补充

【特殊点】

需要注意的是,TreeViewItem是一个ItemsControl,当我们选中它的某个子项时,它本身的Rect也包含鼠标,如下图所示,因此,我们需要一直往下找,直到最后一个包含鼠标的TreeViewItem。显然,这个是递归的过程。

当Level_3_1被选中时,Root,Level_1_1,Level_2_1的Rect都会被认为是包含了鼠标位置

【实现】

代码很简单:

Code

 

为了方便使用,我还定义了一个AttachedProperty,这样可以通过一句简单的xaml语句来开启右键选中功能。比如:

Code

 

【注意事项】

在使用AttachedProperty来开启右键选中功能时,需要特别注意是事件的处理顺序。

首先,TreeView会发生PreviewMouseRightButtonDown事件,然后TreeViewHelper中的代码会开始处理,接着是MouseRightButtonDown事件。这个现象的原因在于,在我写的TreeViewHelper里面,是通过监听PreviewMouseRightButtonDown来处理的,而在控件初始化的时候,首先会加上我们自己写的PreviewMouseRightButtonDown事件处理方法,然后才设置附加属性的值,这样导致我们自定义的事件处理发生在TreeViewHelper事件处理之前。

因此,如果是通过附加属性开启的,最好是在MouseRightButtonDown处理方法中写其他的代码。如果是想在PreviewMouseRightButtonDown中处理,则不要使用附加属性,而是手动调用TreeViewHelper.SelectItemByRightClick(treeView)。然后再写其他处理逻辑。这在我的示例代码中有说明。

 

代码下载https://files.cnblogs.com/RMay/TreeViewRightClick/TreeViewRightClick.rar

【附】

另外一个小问题,关于WPF中调用Message.Show()方法时需要注意:

如果是在非UI线程调用该方法,则需要通过Dispatcher.Invoke()来调用,否则,对话框会阻塞这个非UI线程,而不是UI线程,造成一个看上去“非模态”的对话框。

示例:https://files.cnblogs.com/RMay/TreeViewRightClick/MessageBoxTricks.rar

 

【补充】

昨天晚上又想起来,可以利用RoutedEvent的一些特性来更简单的实现TreeView的鼠标右键选中。

我们知道,在WPF里面,我们可以在元素的Parent上监听该元素的事件,诸如:

<TreeView TreeViewItem.PreviewMouseRightButtonDown="TreeViewItem_PreviewMouseRightButtonDown" ……/>

这样,当TreeViewItem发生PreviewMouseRightButtonDown事件时,该事件将会被TreeView所截获,交由我们定义的事件处理方法去处理。

但是,跟普通的事件注册处理有所不同的是,在方法

private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)

里面,sender并不是TreeViewItem,而是TreeView。为了拿到TreeViewItem,我们需要利用e里面的OriginalSource属性。

然而,如果我们就直接写  TreeViewItem item = e.OriginalSource as TreeViewItem 是拿不到的。跟踪断点我们可以发现,e.OriginalSource原来是TextBlock。不是TreeView和TreeViewItem么,怎么莫名其妙跑出来个TextBlock?其实,这个TextBlock是WPF给我们提供一个默认的TreeView的ItemTemplate中的东东,所以,我们只要沿着它的TemplatedParent往上找,就会找到TreeViewItem。

示例代码如下:

 

Code

 

为了方便使用,还是觉得搞成AttachedProperty更合适,那么,如果在代码中让TreeView监听TreeViewItem的事件呢?其实也很简单:

 

Code

 

使用的时候,在xaml里面描述一下就可以了:

 

 

Code

 

并且,这时候,不再跟TreeView的事件有任何冲突了,我们可以放心的在TreeView的PreviewMouseRightButtonDown中写自己想写的逻辑。

代码下载:

https://files.cnblogs.com/RMay/TreeViewRightClick/TreeViewRightClick2.rar 

posted @ 2008-10-23 14:32  大佛脚下  阅读(5248)  评论(5编辑  收藏  举报