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

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

在 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();
        }
    }
}


  • 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/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。


walter lv

博客专家

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

私信
关注

原文地址:https://www.cnblogs.com/lonelyxmas/p/12051975.html

时间: 2024-12-15 01:51:57

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

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

原文:WPF 一个空的 WPF 程序有多少个窗口 好多小伙伴说 WPF 的程序有五个窗口,但是我尝试使用了 EnumThreadWindows 去获取的时候居然拿到了 10 多个窗口 在 WPF 内部的5个窗口之 MediaContextNotificationWindow 听说有五个窗口 可以通过 user32 的 EnumThreadWindows 找到一个线程的窗口 delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

WPF中的瀑布流布局(TilePanel)控件

最近在用wpf做一个metro风格的程序,需要用到win8风格的布局容器,只能自己写一个了.效果如下 用法 : <local:TilePanel                          TileMargin="1"                         Orientation="Horizontal"                         TileCount="4" > //todo 放置内容 //loc

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

在C#中子线程怎样操作主线程中窗口上控件 在C#中,直接在子线程中对窗口上的控件操作是会出现异常,这是因为子线程和运行窗口的线程是不同的空间,因此想要在子线程来操作窗口上的控件.是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法.其作用就是让子线程告诉窗口线程来完毕对应的控件操作. 要实现该功能,基本思路例如以下: 把想对还有一线程中的控件实施的操作放到一个函数中,然后使用delegate代理那个函数.而且在那个函数中加入一个推断,用 InvokeRequir

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

原文:Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定) --------------------------------------------------------------------------------引用或转载时请保留以下信息:大可山?[MSN:a3news(AT)hotmail.com] http://www.zpxp.com?http://www.brawdraw.com萝卜鼠在线图形图像处理

WPF系列之二:解耦View层控件事件与ViewModel层事件的响应

以前的做法: 1.当项目的时间比较紧迫的时候,对UI层中控件的事件的处理,往往采取的是类似Winform中最简单的做法,直接做一个事件的Handler直接去调用VM层的方法. 2.控件只有一个Command属性,其它的事件的处理方法没有办法和ViewModel层进行解耦的时候往往也采取上面提到的方法. 如下图所示: 新的做法: 为了实现事件的处理与View层的解耦,我们可以利用WPF提供的附加属性来为需要的事件添加附加行为.附加属性扮演了一个在View层与Model层牵线的角色. 需要下面三个步

窗口使用半透明图片,控件不透明的方法

最近项目软件需要制作半透明窗口,但控件不能透明.窗口和控件都是自绘的.网上大部分例子都是直接设置窗口透明度实现,这样只能让整个窗口透明,达不到我们想要的效果.经研究发现,需要使用父子两个窗口配合制作,父窗口不能在OnPaint那里自绘,而是直接GetDC得到句柄自绘背景,子窗口负责自绘控件. 关键代码:父窗口 OnInitDialog() { m_dlg.Create(m_hWnd); m_dlg.ShowWindow(SW_SHOW); m_dlg.MoveWindow(&rct); HDC

在WPF中获取DataGridTemplateColumn模板定义的内容控件

xaml格式描述: <DataGrid Name="dataGrid" Grid.Row="1" ItemsSource="{Binding}"  >            <DataGrid.Columns>              <DataGridTemplateColumn Header="描述">                    <DataGridTemplateCo

WPF编程,通过Double Animation动态更改控件属性的一种方法。

原文:WPF编程,通过Double Animation动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/87251422 DoubleAnimation类指定起始值(From="30").终点值(To="300").时间(Duration="3"),以及动画结束应该如何(FillBehavior="Stop&qu

WPF编程,通过Double Animation动态旋转控件的一种方法。

原文:WPF编程,通过Double Animation动态旋转控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/87259162 ? ?1.控件旋转,可直接调用.需要将控件的名称,旋转的角度,时间等参数传进去. /// <summary> /// 控件绕中心旋转动画 /// </summary> /// <param name="element"