【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都会被认为是包含了鼠标位置。
【实现】
代码很简单:

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

【注意事项】
在使用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。
示例代码如下:

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

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

并且,这时候,不再跟TreeView的事件有任何冲突了,我们可以放心的在TreeView的PreviewMouseRightButtonDown中写自己想写的逻辑。
代码下载:
https://files.cnblogs.com/RMay/TreeViewRightClick/TreeViewRightClick2.rar
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗