您的位置:首页 > 其它

win32窗口:关于鼠标定位位置偏移问题的原因及解决方法

2014-03-01 20:28 951 查看
问题描述:建立了一个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/valueDescription
WS_BORDER0x00800000LThe window has a thin-line border.

WS_CAPTION0x00C00000LThe window has a title bar (includes the WS_BORDER style).

WS_CHILD0x40000000LThe 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.

WS_CHILDWINDOW0x40000000LSame as the WS_CHILD style.

WS_CLIPCHILDREN0x02000000LExcludes the area occupied by child windows when drawing occurs within the parent window. This style is used when creating the parent window.

WS_CLIPSIBLINGS0x04000000LClips 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.

WS_DISABLED0x08000000LThe 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.

WS_DLGFRAME0x00400000LThe window has a border of a style typically used with dialog boxes. A window with this style cannot have a title bar.

WS_GROUP0x00020000LThe 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.

WS_HSCROLL0x00100000LThe window has a horizontal scroll bar.

WS_ICONIC0x20000000LThe window is initially minimized. Same as the WS_MINIMIZE style.

WS_MAXIMIZE0x01000000LThe window is initially maximized.

WS_MAXIMIZEBOX0x00010000LThe window has a maximize button. Cannot be combined with theWS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.

WS_MINIMIZE0x20000000LThe window is initially minimized. Same as the WS_ICONIC style.

WS_MINIMIZEBOX0x00020000LThe window has a minimize button. Cannot be combined with theWS_EX_CONTEXTHELP style. The WS_SYSMENU style must also be specified.

WS_OVERLAPPED0x00000000LThe window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_TILED style.

WS_OVERLAPPEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)The window is an overlapped window. Same as the WS_TILEDWINDOWstyle.

WS_POPUP0x80000000LThe windows is a pop-up window. This style cannot be used with theWS_CHILD style.

WS_POPUPWINDOW(WS_POPUP | WS_BORDER | WS_SYSMENU)The window is a pop-up window. The WS_CAPTION andWS_POPUPWINDOW styles must be combined to make the window menu visible.

WS_SIZEBOX0x00040000LThe window has a sizing border. Same as the WS_THICKFRAME style.

WS_SYSMENU0x00080000LThe window has a window menu on its title bar. The WS_CAPTION style must also be specified.

WS_TABSTOP0x00010000LThe 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.

WS_THICKFRAME0x00040000LThe window has a sizing border. Same as the WS_SIZEBOX style.

WS_TILED0x00000000LThe window is an overlapped window. An overlapped window has a title bar and a border. Same as the WS_OVERLAPPED style.

WS_TILEDWINDOW(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)The window is an overlapped window. Same as theWS_OVERLAPPEDWINDOW style.

WS_VISIBLE0x10000000LThe window is initially visible.

This style can be turned on and off by using the ShowWindow orSetWindowPos function.

WS_VSCROLL0x00200000LThe 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

C++

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

C++

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: [b]BOOL[/b]

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

C++

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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: