WPF技巧:命中测试在视觉树中的使用
我们有时候又需求从当前视觉树中找一些东西,比如鼠标按下的时候,看看鼠标下的元素都有什么。又比如某块区域下有哪些元素?某个坐标点下有哪些元素?
这些需求在使用 命中测试的时候,可以非常方便和快速的去找到我们需要的内容。
简单命中测试
我们写一个最简单的命中测试的示例,来了解命中测试。我在一个画板上在不同的位置放了3个圆形。给他们放置了不同的位置和填充不同的颜色,我们通过命中测试判断如果鼠标在圆上抬起了,我们读取当前圆的填充颜色。
<Window x:Class="WPFVisualTreeHelper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFVisualTreeHelper"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp">
<Canvas>
<Ellipse Canvas.Left="30" Canvas.Top="200" Width="130" Height="130" Fill="Blue"/>
<Ellipse Canvas.Left="110" Canvas.Top="0" Width="130" Height="130" Fill="Red"/>
<Ellipse Canvas.Left="220" Canvas.Top="100" Width="130" Height="130" Fill="Yellow"/>
<TextBlock Canvas.Left="0" Canvas.Top="0" Text="抬起鼠标左键,开始对鼠标所在点进行命中测试" />
</Canvas>
</Grid>
</Window>
我们给Grid 添加了左键抬起的事件。
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes; namespace WPFVisualTreeHelper
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var ellipse = GetVisual(e.GetPosition(this));
;
MessageBox.Show(ellipse?.Fill?.ToString());
} private Ellipse GetVisual(Point point)
{
HitTestResult hitResult = VisualTreeHelper.HitTest(this, point);
var ellipse = hitResult.VisualHit as Ellipse;
return ellipse;
}
}
}
如果在圆上抬起,我们会显示圆的填充色。

我们简单命中测试使用VisualTreeHelper来进行命中测试。HitTest方法传入2个参数,一个参数在什么元素上查找,第二个参数是坐标。返回的对象是命中测试的返回结果,是DependencyObject类型的对象。我们的ui元素都是继承自DependencyObject的。找到的结果是HitTestResult,需要我们自己转换VisualHit到我们需要的类型,我们的方法如果不是该类型就返回为空。因为是简单命中测试。后面会封装一个命中测试的方法。因为在命中测试过程中,会有元素的层级关系,多个同类型元素集合,等等的需求。所以这里只了解什么是命中测试。能看懂代码就可以了,理解什么是命中测试。就可以了。
复杂命中测试
我们通过在VisualTreeHelper的HitTest上F12我们可以看到有3个方法的重载。

VisualTreeHelper类使用其他重载版本可以执行更复杂的命中测试。我们可以检索位于特定点的所有可视化对象,也可以使用特定区域的所有可视化对象。我们使用第一个重载方法。
因为在视觉树下,有层级和同级多个元素的问题。为了使用这个功能,我们需要创建回调函数,也就是第一个HitTest的第三个参数resultCallback。我们通过自上而下遍历所有可视对象,如果发现了匹配的对象就使用回调函数传递相关的内容直到找到所有的对象。

我们写个例子,我们在上面的例子上多添加几个圆形,并且把他们叠加起来,然后我们创建一个10*10像素的形状,去检索这个形状坐标下的所有圆形。为了让他们显示层叠关系我设置了opacity属性。并使用鼠标右键来执行复杂命中测试。 <Window x:Class="WPFVisualTreeHelper.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFVisualTreeHelper"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid MouseLeftButtonUp="Grid_MouseLeftButtonUp" MouseRightButtonUp="Grid_MouseRightButtonUp">
<Canvas>
<Ellipse Canvas.Left="30" Canvas.Top="200" Width="130" Height="130" Fill="Blue"/>
<Ellipse Opacity="0.6" Canvas.Left="70" Canvas.Top="50" Width="130" Height="130" Fill="Violet"/>
<Ellipse Opacity="0.6" Canvas.Left="150" Canvas.Top="50" Width="130" Height="130" Fill="Orange"/>
<Ellipse Opacity="0.6" Canvas.Left="110" Canvas.Top="0" Width="130" Height="130" Fill="Red"/>
<Ellipse Canvas.Left="220" Canvas.Top="100" Width="130" Height="130" Fill="Yellow"/>
<TextBlock Canvas.Left="0" Canvas.Top="0" Text="抬起鼠标左键,开始对鼠标所在点进行命中测试" />
</Canvas>
</Grid>
</Window>
后台代码:
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes; namespace WPFVisualTreeHelper
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DrawingVisual v = new DrawingVisual(); }
#region 简单命中测试
private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var ellipse = GetVisual(e.GetPosition(this));
;
MessageBox.Show(ellipse?.Fill?.ToString());
}
private Ellipse GetVisual(Point point)
{
HitTestResult hitResult = VisualTreeHelper.HitTest(this, point);
var ellipse = hitResult.VisualHit as Ellipse;
return ellipse;
}
#endregion #region 复杂命中测试
private void Grid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((UIElement)sender);
//我们定义一个10*10大小的几何
EllipseGeometry expandedHitTestArea = new EllipseGeometry(pt, 10.0, 10.0);
var ellipses = GetVisual(expandedHitTestArea);
StringBuilder stringBuilder = new StringBuilder();
foreach (var item in ellipses)
{
stringBuilder.Append(item.Fill.ToString() + ",");
}
MessageBox.Show(stringBuilder.ToString());
} private HitTestResultBehavior HitTestCallback(HitTestResult result)
{
GeometryHitTestResult geometryResult = (GeometryHitTestResult)result;
Ellipse visual = result.VisualHit as Ellipse;
if (visual != null)
{
hits.Add(visual);
}
return HitTestResultBehavior.Continue;
}
List<Ellipse> hits = new List<Ellipse>();
private List<Ellipse> GetVisual(Geometry region)
{
hits.Clear();
GeometryHitTestParameters parameters = new GeometryHitTestParameters(region);
HitTestResultCallback callback = new HitTestResultCallback(this.HitTestCallback);
//第一个参数是我们要在什么容器内查找(我们现在是在整个window),第二个参数是筛选回调值的方法,我们目前不需要,
//第三个参数是命中测试回调结果。第四个参数是需要检测的区域。
VisualTreeHelper.HitTest(this, null, callback, parameters);
return hits;
}
#endregion }
}
HitTestCallback就是我们基于搜索结果回调的关键方法。我们查找了这个几何图形下的所有ellispe。
命中测试的封装
这里搞错了,这里封装的是查找元素的封装。。这个在补完C#的知识文章后更改。
我创建了一个C#相关的交流群。用于分享学习资料和讨论问题。欢迎有兴趣的小伙伴:QQ群:542633085
WPF技巧:命中测试在视觉树中的使用的更多相关文章
- WPF命中测试示例(一)——坐标点命中测试
原文:WPF命中测试示例(一)--坐标点命中测试 命中测试也可被称为碰撞测试,在WPF中使用VisualTreeHelper.HitTest()方法实现,该方法用于获取给定的一个坐标点或几何形状内存在 ...
- WPFの命中测试
概述: WPF中的Canvas是常用的一个绘图控件,可以方便地在Canvas中添加我们需要处理的各种元素如:图片.文字等.但Canvas中元素增加到一定数量,并且有重合的时候,我们如何通过在Canv ...
- WPF命中测试示例(二)——几何区域命中测试
原文:WPF命中测试示例(二)--几何区域命中测试 接续上次的命中测试,这次来做几何区域测试示例. 示例 首先新建一个WPF项目,在主界面中拖入一个按钮控件,并修改代码中的以下高亮位置: 当前设计视图 ...
- wpf datagrid row的命中测试
1. 添加鼠标左键处理 AddHandler(DataGrid.MouseLeftButtonDownEvent, new RoutedEventHandler(grdStudyList_MouseL ...
- 重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试
原文:重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试 [源码下载] 重新想象 Windows 8 Store ...
- 背水一战 Windows 10 (69) - 控件(控件基类): UIElement - Manipulate 手势处理, 路由事件的注册, 路由事件的冒泡, 命中测试的可见性
[源码下载] 背水一战 Windows 10 (69) - 控件(控件基类): UIElement - Manipulate 手势处理, 路由事件的注册, 路由事件的冒泡, 命中测试的可见性 作者:w ...
- 分页技巧_测试并继续改进分页用的QueryHelper辅助对象
分页技巧_测试并继续改进分页用的QueryHelper辅助对象 QueryHelper.java /** * 用于辅助拼接HQL语句 */ public class QueryHelper { pri ...
- WPF技巧-Canvas转为位图
转自:http://www.cnblogs.com/tmywu/archive/2010/09/14/1825650.html 在WPF中我们可以将Canvas当成一种画布,将Canvas中的控件当成 ...
- WPF 一个数据库连接测试的实现
要实现的功能效果图如下:因为我们要测试数据是从输入框获得的,所以,我们的连接字符串不是写死在应用程序中的.下面我就详细介绍一下.
随机推荐
- Java求余%引发的一连串故事
C1 RCE对%的处理 HotSpot VM的C1有个RCE(Range Check Elimination,范围检查消除)优化,所谓范围检查消除,就是为了正确的抛出数组越界异常,虚拟机需要在数组访问 ...
- kylin的rowkey优化之调整rowkey顺序
在以hbase为存储的cuboid中,会有很多计算好的数据行,这每个行的key都是由维度值按顺序生成的rowkey 而这个顺序,在我们做cube设计的时候是可以调整的. 具体调整路径是:cube de ...
- JAVA并发(3)-ReentrantReadWriteLock的探索
1. 介绍 本文我们继续探究使用AQS的子类ReentrantReadWriteLock(读写锁).老规矩,先贴一下类图 ReentrantReadWriteLock这个类包含读锁和写锁,这两种锁都存 ...
- go输出九九乘法表和金字塔
用go语言实现输出九九乘法表和金字塔: 1.输出九九乘法表: 代码示例: package main import ( "fmt" "time" ) //实现99 ...
- stream的groupby出来的map是有顺序的map
stream分组后的map是有序map List<RedisInstanceTypeDto> typeDtoList = ModuleHelper.mapAll(redisInstance ...
- Javascript和Typescript语言类型
静态语言(强类型语言) 静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型. 例如:C++.Java.Delphi.C#等. 动态语言(弱类型语言) 动 ...
- Mybatis基础使用方法
1.首先在数据库中建立一张表 create table login( name varchar(20) not null, username varchar(20) not null, passwor ...
- ONNX MLIR方法
ONNX MLIR方法 MLIR中的开放式神经网络交换实现. Prerequisites gcc >= 6.4 libprotoc >= 3.11.0 cmake >= 3.15.4 ...
- CVPR2020:训练多视图三维点云配准
CVPR2020:训练多视图三维点云配准 Learning Multiview 3D Point Cloud Registration 源代码和预训练模型:https://github.com/zgo ...
- ContOS8 配置MariaDB
导语: 该篇文章主要记录ContOS8安装MariaDB后的一些配置内容,若想要详细了解安装过程请移步至上一篇博文! 正文: 首先对MariaDB进行相关的简单配置 使用mysql_secure_in ...