问题描述:建立了一个win32窗口,上面存在一个自绘按钮控件,现在要对鼠标对于按钮的悬停事件进行监控。
有以下步骤:
1、在winproc中对msg进行选择,获取mouse move事件。从对应的lparam中获取当前鼠标的坐标点对。
2、用获取的坐标点对与控件区域信息进行比对,判断鼠标是否悬停在控件上。
第一步可以用另外一种方法实现:GetCursorPos(),用这个函数来获得当前光标的位置。这个函数的MSDN定义见最后备注。
当前面这些完成后来测试这个功能,发现鼠标定位会有偏差,如下图中黑框表示控件,红区表示在此区域内,光标才会被判定在控件上。
无论全屏还是窗口模式下都呈现这个问题。
解决过程:
1、对窗口进行全屏处理,用GetCursorPos(原本用的消息参数来获取的)输出对应鼠标位置信息。发现当不使用ClientToScreen函数时,鼠标定位信息与预期相应。而使用后会发现在左上角鼠标位置信息非零,为(-m,-n)此处m、n都为某个正整数,笔者机器上是(-8,-30)。
2、在上一步进行的同时,发现在边界上,即靠近屏幕四周的一个很小的环形区域内,消息方法无法获得鼠标位置信息。
3、综上,判定鼠标在2中区域时是否还在客户区。笔者在此时犯了个错误。获取客户区的矩形后并没有第一时间输出工作区矩形的信息,并且用的消息中的鼠标位置信息,而且只对左上角区域进行了判定,结果发现鼠标一直在客户区。事实上,获取的客户区区确实是左上角为(0,0),但是右下点并非在预设窗口大小应在位置。
4、输出客户区矩形信息后,发现右下角为(i,j),而且i+m与j+n正好和我们的窗口大小吻合。也就是说事实上,我们的客户区并没有在全屏时占据整个屏幕。
5、检查窗口样式,最终发现在CreateWindow时用WS_POPUP时,得到鼠标信息与预期一致。也即2中所知的区域为窗体border。
6、全屏模式下通过无边框的窗口样式即可解决定位问题,但是在窗口下该怎么处理这问题呢?
首先来看下,window style的参数枚举值:
Constant/value | Description |
---|---|
|
The window has a thin-line border. |
|
The window has a title bar (includes the WS_BORDER style). |
|
The window is a child window. A window with this style cannot have a menu bar. This style cannot be used with the WS_POPUP style. |
|
Same as the WS_CHILD style. |
|
Excludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window. |
|
Clips child windows relative to each other; that is, when a particular child window receives a WM_PAINT message, the WS_CLIPSIBLINGS style clips all other overlapping child windows out of the region of the child window to be updated. If WS_CLIPSIBLINGS is not specified and child windows overlap, it is possible, when drawing within the client area of a child window, to draw within the client area of a neighboring child window. |
|
The window is initially disabled. A disabled window cannot receive input from the user. To change this after a window has been created, use theEnableWindow function. |
|
The window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar. |
|
The window is the first control of a group of controls. The group consists of this first control and all controls defined after it, up to the next control with the WS_GROUP style. The first control in each group usually has theWS_TABSTOP style so that the user can move from group to group. The user can subsequently change the keyboard focus from one control in the group to the next control in the group by using the direction keys. You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function. |
|
The window has a horizontal scroll bar. |
|
The window is initially minimized. Same as the WS_MINIMIZE style. |
|
The window is initially maximized. |
|
The window has a maximize button. Cannot be combined with theWS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified. |
|
The window is initially minimized. Same as the WS_ICONIC style. |
|
The window has a minimize button. Cannot be combined with theWS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified. |
|
The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style. |
|
The window is an overlapped window. Same as the WS_TILEDWINDOWstyle. |
|
The windows is a pop-up window. This style cannot be used with theWS_CHILD style. |
|
The window is a pop-up window. The WS_CAPTION andWS_POPUPWINDOW styles must be combined to make the window menu visible. |
|
The window has a sizing border. Same as the WS_THICKFRAME style. |
|
The window has a window menu on its title bar. The WS_CAPTION style must also be specified. |
|
The window is a control that can receive the keyboard focus when the user presses the TAB key. Pressing the TAB key changes the keyboard focus to the next control with the WS_TABSTOP style. You can turn this style on and off to change dialog box navigation. To change this style after a window has been created, use the SetWindowLong function. For user-created windows and modeless dialogs to work with tab stops, alter the message loop to call the IsDialogMessage function. |
|
The window has a sizing border. Same as the WS_SIZEBOX style. |
|
The window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_OVERLAPPED style. |
|
The window is an overlapped window. Same as theWS_OVERLAPPEDWINDOW style. |
|
The window is initially visible. This style can be turned on and off by using the ShowWindow orSetWindowPos function. |
|
The windThe window has a vertical scroll bar. |
似乎并没有可以设置的样式,但是PopUp无边框的窗口可以在一定程度上解决出现的问题,但是没有了标题栏等。
7、那么我们来看下DXUT范例中所给的处理方法吧。
int nDefaultWidth = 640; int nDefaultHeight = 480; if( GetDXUTState().GetOverrideWidth() != 0 ) nDefaultWidth = GetDXUTState().GetOverrideWidth(); if( GetDXUTState().GetOverrideHeight() != 0 ) nDefaultHeight = GetDXUTState().GetOverrideHeight(); RECT rc; SetRect( &rc, 0, 0, nDefaultWidth, nDefaultHeight ); AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, ( hMenu != NULL ) ? true : false ); WCHAR* strCachedWindowTitle = GetDXUTState().GetWindowTitle(); wcscpy_s( strCachedWindowTitle, 256, strWindowTitle ); // Create the render window HWND hWnd = CreateWindow( L"Direct3DWindowClass", strWindowTitle, WS_OVERLAPPEDWINDOW, x, y, ( rc.right - rc.left ), ( rc.bottom - rc.top ), 0, hMenu, hInstance, 0 );
DXUT里面的思想很简单就是直接根据所需客户区的大小(一般和D3DDevice缓冲区大小相同)来调整整个窗口的大小,从而使的鼠标获取的信息与预期相同。其中最重要的函数是:
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, ( hMenu != NULL ) ? true : false );
详细介绍见备注2,使用这个函数后通过GetCursorPos函数获得点,由ScreenToCient函数(见备注3)转换后就是我们所需要的点的信息了。
--------------------------------------------------分割线-----------------------------------------------------------------------
备注1:
GetCursorPos function
Retrieves the position of the mouse cursor, in screen coordinates.
Syntax
BOOL WINAPI GetCursorPos(
_Out_ LPPOINT lpPoint
);
Parameters
lpPoint [out]Type: LPPOINT
A pointer to a POINT structure that receives the screen coordinates of the cursor.
Return value
Type: BOOL
Returns nonzero if successful or zero otherwise. To get extended error information, call GetLastError.
Remarks
The cursor position is always specified in screen coordinates and is not affected by the mapping mode of the window that contains the cursor.
The calling process must have WINSTA_READATTRIBUTES access to the window station.
The input desktop must be the current desktop when you call GetCursorPos. Call OpenInputDesktop to determine whether the current desktop is the input desktop. If it is not, call SetThreadDesktop with theHDESK returned by OpenInputDesktop to switch to that desktop.
备注2:
AdjustWindowRect function
Calculates the required size of the window rectangle, based on the desired client-rectangle size. The window rectangle can then be passed to the CreateWindow function to create a window whose client area is the desired size.
To specify an extended window style, use the AdjustWindowRectEx function.
Syntax
BOOL WINAPI AdjustWindowRect( _Inout_ LPRECT lpRect, _In_ DWORD dwStyle, _In_ BOOL bMenu );
Parameters
- lpRect [in, out]
-
Type: LPRECT
A pointer to a RECT structure that contains the coordinates of the top-left and bottom-right corners of the desired client area. When the function returns, the structure contains the coordinates of the top-left and bottom-right corners of the window to accommodate the desired client area.
- dwStyle [in]
-
Type: DWORD
The window style of the window whose required size is to be calculated. Note that you cannot specify theWS_OVERLAPPED style.
- bMenu [in]
-
Type: BOOL
Indicates whether the window has a menu.
Return value
Type:
Type: BOOL
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
Remarks
A client rectangle is the smallest rectangle that completely encloses a client area. A window rectangle is the smallest rectangle that completely encloses the window, which includes the client area and the nonclient area.
The AdjustWindowRect function does not add extra space when a menu bar wraps to two or more rows.
The AdjustWindowRect function does not take the WS_VSCROLL or WS_HSCROLL styles into account. To account for the scroll bars, call the GetSystemMetrics function with SM_CXVSCROLL or SM_CYHSCROLL.
备注3:
ClientToScreen function
The ClientToScreen function converts the client-area coordinates of a specified point to screen coordinates.
Syntax
BOOL ClientToScreen( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint );
Parameters
- hWnd [in]
-
A handle to the window whose client area is used for the conversion.
- lpPoint [in, out]
-
A pointer to a POINT structure that contains the client coordinates to be converted. The new screen coordinates are copied into this structure if the function succeeds.
Return value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero.
Remarks
The ClientToScreen function replaces the client-area coordinates in the POINT structure with the screen coordinates. The screen coordinates are relative to the upper-left corner of the screen. Note, a screen-coordinate point that is above the window's client area has a negative y-coordinate. Similarly, a screen coordinate to the left of a client area has a negative x-coordinate.
All coordinates are device coordinates.
Examples
For an example, see "Drawing Lines with the Mouse" in Using Mouse Input.