原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

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

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


可用于演示的 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();
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

观察现象

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

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

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

推断结论

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

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

原理

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

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

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

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

参考资料


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

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

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

发布了382 篇原创文章 · 获赞 232 · 访问量 47万+

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

  1. WPF 程序如何跨窗口/跨进程设置控件焦点

    原文:WPF 程序如何跨窗口/跨进程设置控件焦点 WPF 程序提供了 Focus 方法和 TraversalRequest 来在 WPF 焦点范围内转移焦点.但如果 WPF 窗口中嵌入了其他框架的 U ...

  2. C语言Windows程序开发—Windows窗口样式与常用控件样式【第04天】

    (一)Windows窗口(MDICLIENT)样式介绍 /* Windows窗口样式 */ WS_BORDER //带有边框的窗口 WS_CAPTION //带有标题栏的窗口 WS_CHILD //子 ...

  3. WPF 一个空的 WPF 程序有多少个窗口

    原文:WPF 一个空的 WPF 程序有多少个窗口 好多小伙伴说 WPF 的程序有五个窗口,但是我尝试使用了 EnumThreadWindows 去获取的时候居然拿到了 10 多个窗口 在 WPF 内部 ...

  4. WPF 从程序集中检索图片资源stream给Image控件使用

    原文:WPF 从程序集中检索图片资源stream给Image控件使用 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/nihang1234/artic ...

  5. WPF教程十一:简单了解并使用控件模板

    WPF教程十一:简单了解并使用控件模板 这一章梳理控件模板,每个WPF控件都设计成无外观的,但是行为设计上是不允许改变的,比如使用Button的控件时,按钮提供了能被点击的内容,那么自由的改变控件外观 ...

  6. 在C#中子线程如何操作主窗口线程上的控件

    在C#中子线程怎样操作主线程中窗口上控件 在C#中,直接在子线程中对窗口上的控件操作是会出现异常,这是因为子线程和运行窗口的线程是不同的空间,因此想要在子线程来操作窗口上的控件.是不可能简单的通过控件 ...

  7. WPF 10天修炼 第五天- 内容控件

    WPF内容控件 在WPF中,所有呈现在用户界面上的对象都称为用户界面元素.但是只有派生自System.Windows.Controls.Control类的对象才称为控件.内容控件通常是指具有Conte ...

  8. WPF 自定义ComboBox样式,自定义多选控件

    原文:WPF 自定义ComboBox样式,自定义多选控件 一.ComboBox基本样式 ComboBox有两种状态,可编辑和不可编辑状态.通过设置IsEditable属性可以切换控件状态. 先看基本样 ...

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

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

随机推荐

  1. GIT-本地仓库

    用户配置 git config --global user.name "name" git config --global user.email "123@qq.com& ...

  2. yarn一些最佳配置

    合理设置队列名 mapreduce.job.queuename设置队列名map读取时进行小文件整合 mapreduce.input.fileinputformat.split.minsize mapr ...

  3. VIM 命令速查表

    今天整理一份 VIM 常用命令速查表,当做给自己备忘. 进入VIM 相关 命令 描述 vim filename 打开或者新建文件 vim +n filename 打开文件并将光标置于第n行行首 vim ...

  4. 《微信小程序商城开发实战》笔者的新书,欢迎各位粉丝上京东购买

    作者图书京东链接,请点击------>>>    **微信小程序商城开发实战** 附京东真实评价截图: 编辑推荐 在当今移动互联网大潮中,微信应用凭借其庞大的用户基数和极强的用户黏性 ...

  5. js - true和false

    JavaScript的true和false什么时候会出现,如何优雅的写判断条件? 以下条件为false,也可称为“falsy” - > 虚值 if (false) if (null) if (u ...

  6. Spring Cloud-Eureka 服务注册中心

    Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件 它主要包括两个组件:Eureka Server 和 Eureka Client Eureka Client: ...

  7. File checksum

    File checksum https://golang.org/pkg/io/#Copy https://blog.iphpo.com/blog/2017/03/golang-產生檔案的md5-ha ...

  8. 理解Spark RDD中的aggregate函数(转)

    针对Spark的RDD,API中有一个aggregate函数,本人理解起来费了很大劲,明白之后,mark一下,供以后参考. 首先,Spark文档中aggregate函数定义如下 def aggrega ...

  9. SpringBoot项目中的全局异常处理器 Failed to invoke @ExceptionHandler method

    文件下载代码 @RequestMapping(value = { "/data/docking/picture/{id}/{empi}" }) public JsonApi pic ...

  10. Linux(CentOS)启动时自动执行脚本(rc.local)

    一.Linux开机启动有多种方法,比如我设置mysql开机启动为:chkconfig --level 35 mysqld on 二.下面说说通过rc.local文件进行开机启动: 1.首先创建一个启动 ...