Get the coordinates of a WM_NCHITTEST message?
Asked Answered
Z

3

7

How do I get the coordinates of a WM_NCHITTEST message in C# code?
I'd love to get the fastest way, because performance is a requirement.

Zwick answered 17/12, 2010 at 14:12 Comment(0)
F
12

From MSDN:

wParam
This parameter is not used.

lParam
The low-order word specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the screen.
The high-order word specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the screen.

So you just need to extract the low-order and high-order words from the message's lParam:

int x = lParam.ToInt32() & 0x0000FFFF;
int y = (int)((lParam.ToInt32() & 0xFFFF0000) >> 16)
Point pos = new Point(x, y);

I wouldn't worry too much about performance, since these operations are just bit level arithmetic...

Note that these coordinates are relative to the screen. If you want coordinates relative to a control (or form), you can use the PointToClient method:

Point relativePos = theControl.PointToClient(pos);
Fils answered 17/12, 2010 at 14:16 Comment(1)
Consider looking at the next answer. TL;DR, cast x and y to (short) before assigning them to support multi-monitor setups (which have negative coordinates).Lentic
U
15

Up until this morning, I'd have agreed 100% with Thomas Levesques answer, I pulled the same information from msdn and the code (seemingly) worked perfectly. However, there's one case where this will bite you, it took me three hours to find the reason this afternoon.

The symptom I had was that, on one of my development machines, within the VS2010 IDE, my control was only selectable by clicking when I clicked on it at a certain y-position. Very small controls at the top of the form weren't selectable by click at all. The size of the region that wasn't clickable looked identical to the size of the IDE surrounding the Windows Forms designer, so at first I thought I had some weird little-known DesignMode problem. The most confusing bit was that exactly the same project (checked out of TFS on a different machine) wouldn't show this behavior.

Here's what happens:

Consider you have a double monitor setup as shown here (sorry for the german screenshot, I don't have an english OS at hand):

Double monitor setup

As you can see, the upper left corner of monitor 2 is at coordinates (1280, -256). If you use the solution shown above, you'll get an y of something like 65505 if the mouse really is at -30. This is because the positions are stored as high and low order WORD of LParam. So, doing (lParam.ToInt32() & 0xFFFF0000) >> 16 will give you the correct bits for the y-position. Casting this to int, however, yields 65505 since you're casting to the wrong data type.

Solution:

int x = (short)(lParam.ToInt32() & 0x0000FFFF);
int y = (short)((lParam.ToInt32() & 0xFFFF0000) >> 16);
Point pos = new Point(x, y);

Casting to short gives you the correct position values. I did cast the x as well, since you can arrange your monitors in a way that the second monitor is left of the main one, and hence the x-position would have the same problem.

I recently found that one of the constructors of Point will to the work for you. So, the short version is:

Point pos = new Point(lParam.ToInt32());
Unbeaten answered 17/6, 2011 at 16:16 Comment(4)
I do have 2 monitors (multiple monitors FTW!) and I am going to look into this. This would be a huge problem to users.Zwick
@Zwick Please do. I'm curious if my setup is just so unusual, since I never saw this mentioned anywhere else, and I really can't imagine I'm the only one seeing this. Anyhow, two monitors can be arranged in a way that this doesn't occur, if they're the same size and orientation and arranged from left to right you'll just get one large plane of (in my example) 2560x1024px. Since there's no negative coordinates in any of the monitor positions this will not be an issue. Funnily enough, I only rotated my second monitor this way yesterday :)Unbeaten
Yes, I'm pretty sure this is the exact problem that the GET_X_LPARAM and GET_Y_LPARAM macros were added to address when Windows gained multiple monitor support (circa Win 98). You aren't supposed to just keep using the LOWORD and HIWORD macros. (Obviously this macro silliness has to be translated to .NET, but I thought an explanation was useful.)Ticknor
@Cody Nice, thanks. I didn't know about those ones, since my plain C programming skills were last used on Windows 3.11. Some of the really old MS apps also show this problem, for example, the context menus of the Data Environment designer in VB6. (Yes, I know, it's a very old legacy app ...).Unbeaten
F
12

From MSDN:

wParam
This parameter is not used.

lParam
The low-order word specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the screen.
The high-order word specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the screen.

So you just need to extract the low-order and high-order words from the message's lParam:

int x = lParam.ToInt32() & 0x0000FFFF;
int y = (int)((lParam.ToInt32() & 0xFFFF0000) >> 16)
Point pos = new Point(x, y);

I wouldn't worry too much about performance, since these operations are just bit level arithmetic...

Note that these coordinates are relative to the screen. If you want coordinates relative to a control (or form), you can use the PointToClient method:

Point relativePos = theControl.PointToClient(pos);
Fils answered 17/12, 2010 at 14:16 Comment(1)
Consider looking at the next answer. TL;DR, cast x and y to (short) before assigning them to support multi-monitor setups (which have negative coordinates).Lentic
B
9

I know this question was already answered and all but...

Point p = new Point(m.LParam.ToInt32());

System.Drawing.Point now has a constructor specifically designed to accept this exact value. Strictly speaking, I think this is probably the easiest way.

To be perfectly honest, I have no idea if this constructor even existed when the other answers were posted.

That all being said, this probably isn't any faster because takrl's answer is almost certainly what the aforementioned constructor does internally anyway.

Biogeochemistry answered 29/11, 2014 at 21:22 Comment(1)
This is the best answer as it automatically handles the multiple monitor issues described by takri.Late

© 2022 - 2024 — McMap. All rights reserved.