概述: 

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. 【干货】Chrome插件(扩展)开发全攻略

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  2. 『Asp.Net 组件』Asp.Net 服务器组件 的开发优势和劣势

    在写<Asp.Net 服务器组件系列文档>之前,笔者不才,揣测微软战略用意: 微软利益诉求莫过于 微软产品和技术的市场份额: 因此,微软战略之一莫过于将 所有开发人员 团聚在 微软周围,以 ...

  3. C语言用regcomp、regexec、regfree和regerror函数实现正则表达式校验

    前言 首先,祝大家国庆假期玩的嗨皮!可能有的人已经在回家的路上了,是不是都看不到我的真挚祝福了? C语言对于一些东西的封装比较少,比如正则表达式,但速度快一直使它立于不败之地,今天就要介绍如何用C封装 ...

  4. Numpy 基础学习

    numpy.array() 功能:创建一个数据 vector = numpy.array([1,2,3,4]) matrix = numpy.array([1,2,3,4],[11,12,13,14] ...

  5. JDBC 异常简介 jDBC简介(六)

    SQL 异常简介 对于数据库的操作访问,必然也很可能抛出异常. JDBC中定义了SQLException,用于描述数据库相关操作中可能出现的异常情况. java.sql.SQLException    ...

  6. springmvc 项目完整示例05 日志 --log4j整合 配置 log4j属性设置 log4j 配置文件 log4j应用

    log4j 就是log for java嘛,老外都喜欢这样子,比如那个I18n  ---internationalization  不就是i和n之间有18个字母... http://logging.a ...

  7. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  8. python 操作RabbitMq详解

    一.简介: RabbitMq 是实现了高级消息队列协议(AMQP)的开源消息代理中间件.消息队列是一种应用程序对应用程序的通行方式,应用程序通过写消息,将消息传递于队列,由另一应用程序读取 完成通信. ...

  9. Spring Boot(十一)Redis集成从Docker安装到分布式Session共享

    一.简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API,Redis也是技术领域使用最为广泛的存储中间件,它是 ...

  10. SpringMVC集成rabbitmq:优化秒杀下单环节

    前言 上一篇在springboot中基于自动配置集成了rabbitmq.那么回到最初的话题中就是想在秒杀下单环节增加排队机制,从而达到限流的目的. 优化秒杀下单流程 之前是在控制器里拿到客户端请求后直 ...