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


    问题描述:建立了一个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_BORDER
    0x00800000L

    The window has a thin-line border.

    WS_CAPTION
    0x00C00000L

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

    WS_CHILD
    0x40000000L

    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.

    WS_CHILDWINDOW
    0x40000000L

    Same as the WS_CHILD style.

    WS_CLIPCHILDREN
    0x02000000L

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

    WS_CLIPSIBLINGS
    0x04000000L

    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.

    WS_DISABLED
    0x08000000L

    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.

    WS_DLGFRAME
    0x00400000L

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

    WS_GROUP
    0x00020000L

    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.

    WS_HSCROLL
    0x00100000L

    The window has a horizontal scroll bar.

    WS_ICONIC
    0x20000000L

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

    WS_MAXIMIZE
    0x01000000L

    The window is initially maximized.

    WS_MAXIMIZEBOX
    0x00010000L

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

    WS_MINIMIZE
    0x20000000L

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

    WS_MINIMIZEBOX
    0x00020000L

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

    WS_OVERLAPPED
    0x00000000L

    The 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_POPUP
    0x80000000L

    The 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_SIZEBOX
    0x00040000L

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

    WS_SYSMENU
    0x00080000L

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

    WS_TABSTOP
    0x00010000L

    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.

    WS_THICKFRAME
    0x00040000L

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

    WS_TILED
    0x00000000L

    The 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_VISIBLE
    0x10000000L

    The window is initially visible.

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

    WS_VSCROLL
    0x00200000L

    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.

  • 相关阅读:
    Layabox记录坑
    爬虫 Beautifulsoup 常用笔记
    opencv +python 提取roi目标区域全部像素的值 得出上下限 均匀值
    opencv + python 读取像素点 BGRtoRGB 以及注意事项
    pyinstaller打包python+opencv 无法在别人电脑上正常运行 问题所在:opencv_ffmpeg341_64.dll
    python 绝对路径相对路径
    面向对象编程-OOP-一张图看懂类和实例的基本用法
    python3 _笨方法学Python_日记_DAY7
    儋州“炰米”:美味的特制粮食
    PHP学习步骤
  • 原文地址:https://www.cnblogs.com/moniza/p/3575574.html
Copyright © 2020-2023  润新知