概述: 

WPF中的Canvas是常用的一个绘图控件,可以方便地在Canvas中添加我们需要处理的各种元素如:图片、文字等。但Canvas中元素增加到一定数量,并且有重合的时候,我们如何通过在Canvas中点击鼠标,获得我们想要的元素,然后再对该元素做出相应的控制?

命中测试,可以很好地解决这个问题

本文目的: 

使用命中测试,选取Canvas中相应Element。

正文: 

可视化树如何影响命中测试

可视化树中的起始点确定在对象的命中测试枚举过程中返回哪些对象。如果有多个要执行命中测试的对象,则可视化树中用作起始点的可视化对象必须是所有相关对象的公共上级。例如,如果您希望对以下关系图中的按钮元素和绘图可视化对象执行命中测试,则必须将可视化树中的起始点设置为两者的公共上级。在本例中,画布元素是按钮元素和绘图可视化对象的公共上级。

可视化树层次结构的关系图

即要在命中测试的结果中,包含Button及Drawing Visual,就必须在他们的公共上级Canvas中做命中测试。

示例

Canvas中放置了底层放置了一张图片,顶层放置了一个文本框,我们想实现点击文本框区域时,命中测试的返回结果为下层的Image,先贴上代码,然后再解释。

private void canvasMain_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
GetCurrentElement(e.GetPosition(canvasMain));
} private void GetCurrentElement(Point point)
{
PointHitTestParameters parameters = new PointHitTestParameters(point);
VisualTreeHelper.HitTest(canvasMain, HitTestFilter, HitTestCallback, parameters);
} private HitTestResultBehavior HitTestCallback(HitTestResult result)
{
Image image = result.VisualHit as Image;
if (image != null)
   {
_currentElement = image;
return HitTestResultBehavior.Stop;
} else
return HitTestResultBehavior.Continue;
} private HitTestFilterBehavior HitTestFilter(DependencyObject o)
{
Type type = o.GetType();
switch (type.Name)
{
case "Canvas":
return HitTestFilterBehavior.ContinueSkipSelf;
default:
return HitTestFilterBehavior.Continue;
}
}

其中VisualTreeHelper.HitTest方法是最主要的方法,其有三种重载形式,我们使用的是第二种重载形式,参数及其含义如下:

参数名称

类型

说明

reference

System.Windows.Media.Visual

要进行命中测试的 Visual

filterCallback

System.Windows.Media.HitTestFilterCallback

表示命中测试筛选回调值的方法

resultCallback

System.Windows.Media.HitTestResultCallback

表示命中测试结果回调值的方法

hitTestParameters

System.Windows.Media.HitTestParameters

要进行命中测试的参数值

1. reference

这个参数不用多说,就是我们要进行命中测试元素的公共上级,即他们在WPF树结构的上级,在这里为“canvasMain”;

2. filterCallback

使用命中测试筛选回调函数可以枚举呈现内容包含指定坐标的所有可视化对象。但是,您可能要忽略不希望在命中测试结果回调函数中处理的可视化树的某些分支。命中测试筛选回调函数的返回值确定可视化对象的枚举应执行的操作类型。例如,如果返回值 ContinueSkipSelfAndChildren,则可从命中测试结果枚举中移除当前可视化对象及其子对象。 这意味着命中测试结果回调函数在其枚举中将看不到这些对象。修剪可视化对象树会减少命中测试结果枚举过程中的处理量。其效果如下所示:

示例代码中我们并不想对Canvas进行命中测试,所以我们就判断如果枚举的命中测试结果为Canvas,则返回HitTestFilterBehavior.ContinueSkipSelf,这样将忽略它,而对其子对象进行命中测试。

下面是HitTestFilterBehavior的成员列表,我们可以根据需要,返回不用的值,以筛选命中测试结果。

成员名称

说明

ContinueSkipChildren

针对当前的 Visual(但不包括其子代)进行命中测试。

ContinueSkipSelfAndChildren

不要针对当前的 Visual 或其子代进行命中测试。

ContinueSkipSelf

不要针对当前的 Visual 进行命中测试,但要针对其子代进行命中测试。

Continue

针对当前的 Visual 及其子代进行命中测试。

Stop

在当前 Visual 处停止命中测试。

3. resultCallback

表示命中测试结果回调值的方法。result.VisualHit即为本次命中测试的结果,可以根据其类型判断其是否符合我们的测试要求,如果不符合则返回HitTestResultBehavior.Continue,继续枚举命中测试的下一个结果,如果符合要求则可以使用变量来接受这个结果,然后直接调用HitTestResultBehavior.Stop来结束整个命中测试。

4. hitTestParameters

定义命中测试的参数。从此公共基类派生出来的可用于实际命中测试的类包括 PointHitTestParameters 和 GeometryHitTestParameters。根据名称亦可以看出PointHitTestParameters主要用于点测试,而GeometryHitTestParameters则可以用于区域内的命中测试,该类型的详细信息请查询MSDN。

PointHitTestParameters的初始化也很简单,只需传入我们需要进行命中测试的点(Point)即可。

OK,主要的方法及其含义都解释完毕,想必大家都已能看明白示例代码,对命中测试也有了一个初步的了解。实际情况中可以根据情况,灵活运用filterCallback和resultCallback这两个回调函数,做到复杂的命中测试。

注意事项:

1. WPF大多控件都有IsHitTestVisible 属性,其可获取或设置一个值,该值声明某个 UIElement 派生对象是否可以作为其呈现内容某部分的命中测试结果返回。 这样,您便可以选择性地更改可视化树,以确定命中测试中涉及哪些可视化对象。

2. 在命中测试结果枚举过程中,不应执行修改可视化树的任何操作。在遍历可视化树的过程中,在可视化树中添加或移除对象会导致不可预知的行为。您可以在 HitTest 方法返回之后安全地修改可视化树。 您可能想要提供一个数据结构(例如 ArrayList),以便在命中测试结果枚举期间存储值。

3. 命中的可视化对象按 Z 顺序进行枚举。Z 顺序中位于最顶层的可视化对象最先进行枚举。所有其他可视化对象按递减的 Z 顺序级别进行枚举。此枚举顺序对应于可视化对象的呈现顺序。

(非原创)

WPFの命中测试的更多相关文章

  1. WPF命中测试示例(二)——几何区域命中测试

    原文:WPF命中测试示例(二)--几何区域命中测试 接续上次的命中测试,这次来做几何区域测试示例. 示例 首先新建一个WPF项目,在主界面中拖入一个按钮控件,并修改代码中的以下高亮位置: 当前设计视图 ...

  2. WPF命中测试示例(一)——坐标点命中测试

    原文:WPF命中测试示例(一)--坐标点命中测试 命中测试也可被称为碰撞测试,在WPF中使用VisualTreeHelper.HitTest()方法实现,该方法用于获取给定的一个坐标点或几何形状内存在 ...

  3. wpf datagrid row的命中测试

    1. 添加鼠标左键处理 AddHandler(DataGrid.MouseLeftButtonDownEvent, new RoutedEventHandler(grdStudyList_MouseL ...

  4. WPF技巧:命中测试在视觉树中的使用

    我们有时候又需求从当前视觉树中找一些东西,比如鼠标按下的时候,看看鼠标下的元素都有什么.又比如某块区域下有哪些元素?某个坐标点下有哪些元素? 这些需求在使用 命中测试的时候,可以非常方便和快速的去找到 ...

  5. 重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试

    原文:重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试 [源码下载] 重新想象 Windows 8 Store ...

  6. 背水一战 Windows 10 (69) - 控件(控件基类): UIElement - Manipulate 手势处理, 路由事件的注册, 路由事件的冒泡, 命中测试的可见性

    [源码下载] 背水一战 Windows 10 (69) - 控件(控件基类): UIElement - Manipulate 手势处理, 路由事件的注册, 路由事件的冒泡, 命中测试的可见性 作者:w ...

  7. WPF 触摸到事件

    原文:WPF 触摸到事件 本文从代码底层告诉大家,在触摸屏幕之后是如何拿到触摸点并且转换为事件 在 WPF 界面框架核心就是交互和渲染,触摸是交互的一部分.在 WPF 是需要使用多个线程来做触摸和渲染 ...

  8. WPF 使用WindowChrome自定义窗体 保留原生窗体特性

    本文大幅度借鉴dino.c大佬的文章 https://www.cnblogs.com/dino623/p/uielements_of_window.html https://www.cnblogs.c ...

  9. 2019-6-15-WPF-触摸到事件

    原文:2019-6-15-WPF-触摸到事件 title author date CreateTime categories WPF 触摸到事件 lindexi 2019-06-15 08:58:54 ...

随机推荐

  1. Android 开发工具类 18_NetWorkUtil

    检测网络的一个工具包: 1.网络是否可用: 2.判断是否有网络连接: 3.判断 WIFI 网络是否可用: 4.判断 MOBILE 网络是否可用; 5.获取当前网络连接的类型信息: 6.获取当前的网络状 ...

  2. SpringBoot入门教程(二)CentOS部署SpringBoot项目从0到1

    在之前的博文<详解intellij idea搭建SpringBoot>介绍了idea搭建SpringBoot的详细过程, 并在<CentOS安装Tomcat>中介绍了Tomca ...

  3. OKR与Scrum如何强强联手

    我们收到很多问题询问如何把OKR和其他框架结合起来使用,以便管理组织的人员.流程和活动. 软件开发公司最喜欢用的框架之一就是Scrum,Scrum是一个诞生于20世纪90年代的软件开发框架,我们公司内 ...

  4. Jenkins结合.net平台之ftp客户端

    上一节我们讲解了如何配置ftp服务端,本节我们讲解如何使用winscp搭建ftp客户端,为什么使用winscp而不是filezilla客户端版,前面我们简单说过,这里不再赘述. 下载winscp以后我 ...

  5. Golang垃圾回收机制(二)

    原文:https://blog.csdn.net/qq_15427331/article/details/54613635 Go语言正在构建的垃圾收集器(GC),似乎并不像宣传中那样的,技术上迎来了巨 ...

  6. ZXing 生成、读取二维码(带logo)

    前言 ZXing,一个支持在图像中解码和生成条形码(如二维码.PDF 417.EAN.UPC.Aztec.Data Matrix.Codabar)的库.ZXing(“zebra crossing”)是 ...

  7. eclipse中的.yml和.properties文件没有绿色叶子图标

    0.首先确认正确安装了STS插件 要在eclipse使用spring boot创建项目,必须先安装STS(Spring Tool Suite (STS) for Eclipse),如果网速给力的话可以 ...

  8. XML记一次带命名空间的xml读取

    public static void ReadXML(string xmlUrl) { //判断文件是否存在 if (!File.Exists(xmlUrl)) { Console.WriteLine ...

  9. 学JAVA第五天,今天困得要死

    好不容易坚持到第五天了,继续继续!!! 今天老师没有讲JAVA的for循环,倒是讲了HTML的相关内容: 讲了JAVA代码怎么在HTML中运行. 只要在HTML加入这个 background-colo ...

  10. Java基础篇——线程、并发编程知识点全面介绍(面试、学习的必备索引)

    原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10739579.html,希望大家多多支持!!! 一.线程基础 1.线程与进程 线程是指进程 ...