How does WPF application get mouse position when the mouse is outside the window?

Introduction

As we all know, we have two methods to get the mouse position which is relative to other controls. One is Mouse.GetPosition(IInputElement relativeTo) , the other is MouseEventArgs.GetPosition(IInputElement relativeTo). But, what will we get when the mouse is outside the window’s client area? And how does it calculate?

This article is to tell developers about what and how we get the mouse coordinate.


Detail

The original link is WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? - walterlv. But this blog was written in Chinese, so I translate its content to English. Waterlv is an MVP(Microsoft Most Valuable Professional), and he is good at .NET Core\WPF\.NET. Here is his Blog.


Demo

Creates a default WPF application with Visual Studio 2019. And this application is developed with default .NET Core version, it contains a Button and Textblock. Now, we try to get mouse position relative to these two FrameworkElement with Mouse.GetPosition method. Here are the codes.

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

Observation

We ran this simple application, then we noticed that when the mouse is outside the window’s client area, the values of mouse position are exactly the same. See this .gif file below for more details.

For more information about Client Area, visit
WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome).
This blog was written in Chinese. I will translate it later.

Conclusion

We can get a conclusion from the messages applied above, the coordinate we get when the mouse is outside window client area is the screen’s left top position (0,0).

We moved the window to the screen’s left top position to check our conclusion. And then we got the coordinate of outside mouse position, and the value is (0,31).

It should note that 31 is the height of the window’s title bar.

It shows that the conclusion is right. So, how does GetPosition work?

Theory

After reading the source code of the GetPosition method. It is easy to find that it gets the coordinate depends on ClientToScreen API.
Here is the source codes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[SecurityCritical, SecurityTreatAsSafe]
internal Point GetScreenPositionFromSystem()
{
// Win32 has issues reliably returning where the mouse is. Until we figure
// out a better way, just return the last mouse position in screen coordinates.

Point ptScreen = new Point(0, 0);

// Security Mitigation: do not give out input state if the device is not active.
if (IsActive)
{
try
{
PresentationSource activeSource = CriticalActiveSource;
if (activeSource != null)
{
ptScreen = PointUtil.ClientToScreen(_lastPosition, activeSource);
}
}
catch (System.ComponentModel.Win32Exception)
{
// The window could be shutting down, so just return (0,0).
ptScreen = new Point(0, 0);
}
}

return ptScreen;
}

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

And here is the definition of the ClientToScreen method in user32.dll.

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

This method requires a window handle hWnd as a parameter,and its usage is listed below.

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

Essentially, we use the ClientToScreen method to get the mouse’s coordinate. And this method will return two different results:

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

So, this function fails when the mouse position is outside the window client area.And ClientToScreen returns (0,0) as result. Then, it will run the ToPoint method to translate the coordinate to relative control’s coordinate system. And that is the result we saw.

References