我们有时候又需求从当前视觉树中找一些东西,比如鼠标按下的时候,看看鼠标下的元素都有什么。又比如某块区域下有哪些元素?某个坐标点下有哪些元素?

  这些需求在使用 命中测试的时候,可以非常方便和快速的去找到我们需要的内容。

简单命中测试

  我们写一个最简单的命中测试的示例,来了解命中测试。我在一个画板上在不同的位置放了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技巧:命中测试在视觉树中的使用的更多相关文章

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

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

  2. WPFの命中测试

    概述:  WPF中的Canvas是常用的一个绘图控件,可以方便地在Canvas中添加我们需要处理的各种元素如:图片.文字等.但Canvas中元素增加到一定数量,并且有重合的时候,我们如何通过在Canv ...

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

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

  4. wpf datagrid row的命中测试

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

  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. 分页技巧_测试并继续改进分页用的QueryHelper辅助对象

    分页技巧_测试并继续改进分页用的QueryHelper辅助对象 QueryHelper.java /** * 用于辅助拼接HQL语句 */ public class QueryHelper { pri ...

  8. WPF技巧-Canvas转为位图

    转自:http://www.cnblogs.com/tmywu/archive/2010/09/14/1825650.html 在WPF中我们可以将Canvas当成一种画布,将Canvas中的控件当成 ...

  9. WPF 一个数据库连接测试的实现

    要实现的功能效果图如下:因为我们要测试数据是从输入框获得的,所以,我们的连接字符串不是写死在应用程序中的.下面我就详细介绍一下.

随机推荐

  1. Qt 设置窗体透明

    一.前言 在音频开发中,窗体多半为半透明.圆角窗体,如下为Qt 5.5 VS2013实现半透明方法总结. 二.半透明方法设置 1.窗体及子控件都设置为半透明 1)setWindowOpacity(0. ...

  2. 给MySQL数据表加入uuid

    alter table table_name add column uuid VARCHAR(255) default "0" update table_name set uuid ...

  3. MVC、MVP和MVVM的区别

    前言 在web1.0时代时,那个时候程序猿还没有前后端之分,更程序员开发的时候,都是要前后端一起写的,前后端的代码都是杂揉在一起,如图下 这种开发模式的话,开发的时候因为不需要和其他人员沟通协作,前后 ...

  4. Linux(CentOS7)下rpm安装MySQL8.0.16

    记录一下自己在 CentOS7 下 rpm 安装 MySQL8.0.16 的过程. 一.准备工作 1. 下载MySQL所需要的安装包 从 MySQL官网 下载,上传至 CentOS 系统 /usr/l ...

  5. 使用CUDA Warp-Level级原语

    使用CUDA Warp-Level级原语 NVIDIA GPU以SIMT(单指令,多线程)的方式执行称为warps 的线程组.许多CUDA程序通过利用warp执行来实现高性能.本文将展示如何使用cud ...

  6. YOLOV4各个创新功能模块技术分析(三)

    YOLOV4各个创新功能模块技术分析(三)  八.数据增强相关-Stylized-ImageNet 论文名称:ImageNet-trained cnns are biased towards text ...

  7. Python神经网络集成技术Guide指南

    Python神经网络集成技术Guide指南 本指南将介绍如何加载一个神经网络集成系统并从Python运行推断. 提示 所有框架的神经网络集成系统运行时接口都是相同的,因此本指南适用于所有受支持框架(包 ...

  8. P1123 取数游戏

    题目描述 一个N \times MN×M的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻88个格子中的一个即认为这两个数字相邻),求取 ...

  9. 升级 macOS Big Sur 差点丢了我多年的珍藏文件(夹)!!!

    原来升级 macOS Big Sur 过程中会转移根目录下的个人文件(夹). 春节期间升级了一下 macOS ,升级之前就比较担心丢数据,然而怕什么来什么.升级之后发现根目录下的个人文件(夹)都不见了 ...

  10. .NET Worker Service 作为 Windows 服务运行及优雅退出改进

    上一篇文章我们了解了如何为 Worker Service 添加 Serilog 日志记录,今天我接着介绍一下如何将 Worker Service 作为 Windows 服务运行. 我曾经在前面一篇文章 ...