在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标当前相对于某个 WPF 控件的位置,也可以通过在 MouseMove 事件中通过 e.GetPosition(IInputElement relativeTo) 方法拿到同样的信息。不过,在任意时刻去获取鼠标位置的时候,如果鼠标在窗口之外,将获取到什么点呢?

本文将介绍鼠标在窗口之外时获取到的鼠标位置。


本文内容

  • 可用于演示的 DEMO
  • 观察现象
  • 推断结论
  • 原理

可用于演示的 DEMO

直接使用 Visual Studio 2019 创建一个空的 WPF 应用程序。默认 .NET Core 版本的 WPF 会带一个文本框和一个按钮。我们现在就用这两个按钮来显示 Mouse.GetPosition 获取到的值。

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;namespace Walterlv.Demo
{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();CompositionTarget.Rendering += OnRendering; }private void OnRendering(object sender, EventArgs e){DebugTextBlock.Text = Mouse.GetPosition(DebugTextBlock).ToString();DebugButton.Content = Mouse.GetPosition(DebugButton).ToString();}}
}

观察现象

我们运行这个最简单的 Demo,然后不断移动鼠标,可以观察到一旦鼠标脱离窗口客户区,获取到的坐标点将完全固定。

如果不知道客户区是什么,可以阅读下面我的另一篇博客:

  • WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)

在以上图中,我拖动改变了窗口的位置,这时将鼠标移动至离开客户区后,获取到的坐标点又被固定为另一个数值。

推断结论

从上面的动图中以及我实际的测量发现,当鼠标移出窗口的客户区之后,获取鼠标的坐标的时候始终拿到的是屏幕的 (0, 0) 点。如果有多个屏幕,是所有屏幕组合起来的虚拟屏幕的 (0, 0) 点。

验证这一点,我们把窗口移动到屏幕的左上角后,将鼠标移出客户区,左上角的控件其获取到的鼠标位置已经变成了 (0, 31),而这个是窗口标题栏非客户区的高度。

原理

Mouse.GetPosition 获取鼠标相对于控件的坐标点的方法在内部的最终实现是 user32.dll 中的 ClientToScreen

[DllImport("user32.dll")]
static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);

此方法需要使用到一个窗口句柄参数,此参数的含义:

A handle to the window whose client area is used for the conversion.

用于转换坐标点的窗口句柄,坐标会被转换到窗口的客户区部分。

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.

如果此方法成功,将返回非零的坐标值;如果失败,将返回 0。

而鼠标在窗口客户区之外的时候,此方法将返回 0,并且经过后面的 ToPoint() 方法转换到控件的坐标下。于是这才得到了我们刚刚观察到的坐标值。

[SecurityCritical, SecurityTreatAsSafe]
public static Point ClientToScreen(Point pointClient, PresentationSource presentationSource)
{// For now we only know how to use HwndSource.HwndSource inputSource = presentationSource as HwndSource;if(inputSource == null){return pointClient;}HandleRef handleRef = new HandleRef(inputSource, inputSource.CriticalHandle);NativeMethods.POINT ptClient            = FromPoint(pointClient);NativeMethods.POINT ptClientRTLAdjusted = AdjustForRightToLeft(ptClient, handleRef);UnsafeNativeMethods.ClientToScreen(handleRef, ptClientRTLAdjusted);return ToPoint(ptClientRTLAdjusted);
}

参考资料

  • How do I get the current mouse screen coordinates in WPF? - Stack Overflow
  • pinvoke.net: clienttoscreen (user32)
  • c# - ClientToScreen unexpected return values? - Stack Overflow
  • ClientToScreen function (winuser.h) - Microsoft Docs

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?相关推荐

  1. 半透明窗口中显示标准控件(控件与文字不透明)的实现方案(附源码)

    和大家分享一下在半透明窗口中显示标准控件的实现方案.通过层叠窗口可以简单实现半透明与不规则形状窗口的效果,但在其上显示标准控件(控件与文字不透明)却是件比较有挑战的事情,这里会给出一个可行的解决方案. ...

  2. Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定)

    原文:Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定) ------------------------------ ...

  3. C# chart控件中游标随着鼠标移动

    chart控件中游标如何随着移动? 思路很简单,只需要在鼠标进入chart控件时,获取鼠标的位置,然后将数值赋给游标的position属性. 具体实现方法如下: 选中chart控件.F4进入属性窗口, ...

  4. VS2010/MFC对话框程序调用Windows Media Player播放器控件

    MFC对话框程序调用Windows Media Player播放器控件播放打开的avi格式的文件,具体步骤如下: 1.根据MFC向导提示,创建一个默认的对话框项目TestMediaPlayer. 2. ...

  5. 窗口之间值、控件的传递

    一.值的传递,很简单,因为在同一名称空间下,所以只需在要提供值的窗口里将值声明为public后,就可以在要引用值的窗口里通过"类名.变量名"使用了.如: MainWindow.cs ...

  6. winform 日期控件放在工具条(先放一个label 占好位置)上。工具条和其他控件都要求有鼠标的精准。...

    注意两点: 1. 要先放一个label 占好位置 2.鼠标动作要到位,才能放置好. 怎么检查是否放置好了. 最大化,最小化一下就知道了. 工具条的使用.最好是从左向右排列,全部都是左对齐的,日期控件放 ...

  7. WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展

    原文:WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展 一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐 ...

  8. SNF快速开发平台MVC-各种级联绑定方式,演示样例程序(包含表单和表格控件)...

    做了这么多项目,经常会使用到级联.联动的情况. 如:省.市.县.区.一级分类.二级分类.三级分类.仓库.货位. 方式:有表单需要做级联的,还是表格行上需要做级联操作的. 实现:实现方法也有很多种方式. ...

  9. matlab GUI窗口最大化,以及控件大小和字体自适应

    1.GUI 窗口最大化 双击除控件外的空白处(视图)>属性检查器>resize>on即可. 设置完这个,当放大的时候,会发现我们控件的位置没有变化.此时我们需要设置一个. 工具> ...

最新文章

  1. vmware虚拟机异常关闭处理
  2. MyBatis传入参数与parameterType
  3. 【Java Web后台实验与开发】ServletHTTPRequest笔记
  4. java实例域静态域_有关java 实例域 静态域 静态方法
  5. C++模版类的简单使用
  6. linux命令取ip,linux下命令取IP地址的多种方法
  7. 余承东透露华为Mate X发布时间:今年六月上市
  8. 常用的sql语句用法
  9. 亚马逊EC2构建代理服务器心血历程
  10. kali如何取得超级用户权限_微商如何取得好的口碑?好的口碑等于信任微商如何提高用户信任?...
  11. java学习——线程
  12. Redis集群方案介绍
  13. 关于“父虚拟磁盘在子虚拟磁盘创建之后被修改过。……打不开磁盘啥啥啥”的解决方法
  14. vue功能-数字键盘
  15. ONFI ZQ Calibration
  16. 关于PhpStorm设置点击编辑文件自动定位源文件
  17. Word中下划线自动换行版式不…
  18. 基于STM32F103的步进电机S型曲线加减速算法与实现
  19. 串之Ukkonen、Rabin_karp算法
  20. phpwind mysql 密码_PhpWind教程:MySQL数据库密码修改方法

热门文章

  1. JavaScript实现的,轮播图左右切换网页动画源码
  2. 一文让你深刻理解什么是suid提权
  3. 2023-03-04 java 主函数main和测试函数test,还有调用函数(在主函数里面调用其他的函数)
  4. 美国陆军正在研制一种战术胸罩
  5. 计算平均值,保留小数点后一位
  6. U盘本来是29G大小,突然可用空间变为2MB大小解决办法
  7. 动态瑜伽 静态瑜伽 初学者_静态网站生成器:初学者指南
  8. javascript获取wx.config内部字段解决微信分享
  9. mac外接硬盘在哪里打开 mac外接硬盘用什么格式
  10. 【程序员觉醒】提高效率,增加输出