• VC++ 解决在鼠标移动时,光标闪烁的问题。其实本质是 ON_SETCURSOR的用法


    在调用Windows API函数SetCursor设置光标时,可能会碰到闪烁的问题:移动鼠标,光标在Class Cursor(即注册窗口类时指定的Cursor)与预设Cursor之间闪烁。

    在MSDN上有关SetCursor函数的备注中强调,如果Class Cursor非空,那么每当鼠标移动,系统都会把光标恢复为Class Cursor。为了避免光标闪烁这一问题,必须处理WM_SETCURSOR消息。(MSDN说明)

    下面是一个例子:程序在主窗口视图的中间位置绘制RGB条带,当鼠标移动在条带范围就将光标设置成为Cross,此外根据光标的位置,在RGB条带上方30px处显示所处条带的颜色。程序运行起来像这样:

    如果在WM_MOUSEMOVE的消息处理中判断光标的位置并设置光标的话,就会碰到所说的光标闪烁问题。WM_MOUSEMOVE的消息处理如下代码所示:

    01.LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)  
    02.{  
    03.    POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };  
    04.    RECT rect, rectText;  
    05.    get_rects(&rect, &rectText);  
    06.    InvalidateRect(&rectText);  
    07.    UpdateWindow();  
    08.    if (::PtInRect(&rect, ptCursor)) {  
    09.        ::SetCursor(m_cursor);  
    10.        int dx = (rect.right - rect.left) / 3;  
    11.        LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };  
    12.        int index;  
    13.        if (ptCursor.x - rect.left < dx)  
    14.            index = 0;  
    15.        else if (ptCursor.x - rect.left < 2 * dx)  
    16.            index = 1;  
    17.        else index = 2;  
    18.        WTL::CString str;  
    19.        str.Format(_T("Cursor on %s part"), ppsz[index]);  
    20.        CClientDC dc(m_hWnd);  
    21.        dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);  
    22.    }  
    23.    else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));  
    24.    return 0;  
    25.}  

    闪烁产生的原因在于每次进入OnMouseMove之前,系统都会先将光标恢复成Arrow,进入OnMouseMove之后,如果光标处在RGB条带范围内则立即被设置成Cross。

    解决办法就是将上面的判断逻辑放在WM_SETCURSOR的消息处理中,当然获得光标客户坐标的方式不同,代码如下所示:

    [cpp] view plaincopyprint?
    1.LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)  
    2.{  
    3.    POINT point;  
    4.    ::GetCursorPos(&point);  
    5.    ScreenToClient(&point);  
    6.    set_cursor(point);  
    7.    return 0;  
    8.}  
    	LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
    	{
    		POINT point;
    		::GetCursorPos(&point);
    		ScreenToClient(&point);
    		set_cursor(point);
    		return 0;
    	}

    而代码中的set_cursor私有方法其实就是上面的判断逻辑,即:

    [cpp] view plaincopyprint?
    1.// ptCursor: in client coordinate  
    2.void set_cursor(POINT& ptCursor) throw()  
    3.{  
    4.    RECT rect, rectText;  
    5.    get_rects(&rect, &rectText);  
    6.    InvalidateRect(&rectText);  
    7.    UpdateWindow();  
    8.    if (::PtInRect(&rect, ptCursor)) {  
    9.        ::SetCursor(m_cursor);  
    10.        int dx = (rect.right - rect.left) / 3;  
    11.        LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };  
    12.        int index;  
    13.        if (ptCursor.x - rect.left < dx)  
    14.            index = 0;  
    15.        else if (ptCursor.x - rect.left < 2 * dx)  
    16.            index = 1;  
    17.        else index = 2;  
    18.        WTL::CString str;  
    19.        str.Format(_T("Cursor on %s part"), ppsz[index]);  
    20.        CClientDC dc(m_hWnd);  
    21.        dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);  
    22.    }  
    23.    else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));  
    24.}  
    	// ptCursor: in client coordinate
    	void set_cursor(POINT& ptCursor) throw()
    	{
    		RECT rect, rectText;
    		get_rects(&rect, &rectText);
    		InvalidateRect(&rectText);
    		UpdateWindow();
    		if (::PtInRect(&rect, ptCursor)) {
    			::SetCursor(m_cursor);
    			int dx = (rect.right - rect.left) / 3;
    			LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
    			int index;
    			if (ptCursor.x - rect.left < dx)
    				index = 0;
    			else if (ptCursor.x - rect.left < 2 * dx)
    				index = 1;
    			else index = 2;
    			WTL::CString str;
    			str.Format(_T("Cursor on %s part"), ppsz[index]);
    			CClientDC dc(m_hWnd);
    			dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
    		}
    		else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
    	}

    这样就解决了光标闪烁的问题。

    在调用Windows API函数SetCursor设置光标时,可能会碰到闪烁的问题:移动鼠标,光标在Class Cursor(即注册窗口类时指定的Cursor)与预设Cursor之间闪烁。

    在MSDN上有关SetCursor函数的备注中强调,如果Class Cursor非空,那么每当鼠标移动,系统都会把光标恢复为Class Cursor。为了避免光标闪烁这一问题,必须处理WM_SETCURSOR消息。(MSDN说明)

    下面是一个例子:程序在主窗口视图的中间位置绘制RGB条带,当鼠标移动在条带范围就将光标设置成为Cross,此外根据光标的位置,在RGB条带上方30px处显示所处条带的颜色。程序运行起来像这样:

    如果在WM_MOUSEMOVE的消息处理中判断光标的位置并设置光标的话,就会碰到所说的光标闪烁问题。WM_MOUSEMOVE的消息处理如下代码所示:

    1. LRESULT OnMouseMove(UINT /*uMsg*/WPARAM /*wParam*/LPARAM lParam, BOOL/*bHandled*/)  
    2. {  
    3.     POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };  
    4.     RECT rect, rectText;  
    5.     get_rects(&rect, &rectText);  
    6.     InvalidateRect(&rectText);  
    7.     UpdateWindow();  
    8.     if (::PtInRect(&rect, ptCursor)) {  
    9.         ::SetCursor(m_cursor);  
    10.         int dx = (rect.right - rect.left) / 3;  
    11.         LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };  
    12.         int index;  
    13.         if (ptCursor.x - rect.left < dx)  
    14.             index = 0;  
    15.         else if (ptCursor.x - rect.left < 2 * dx)  
    16.             index = 1;  
    17.         else index = 2;  
    18.         WTL::CString str;  
    19.         str.Format(_T("Cursor on %s part"), ppsz[index]);  
    20.         CClientDC dc(m_hWnd);  
    21.         dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);  
    22.     }  
    23.     else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));  
    24.     return 0;  
    25. }  
    	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
    	{
    		POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
    		RECT rect, rectText;
    		get_rects(&rect, &rectText);
    		InvalidateRect(&rectText);
    		UpdateWindow();
    		if (::PtInRect(&rect, ptCursor)) {
    			::SetCursor(m_cursor);
    			int dx = (rect.right - rect.left) / 3;
    			LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
    			int index;
    			if (ptCursor.x - rect.left < dx)
    				index = 0;
    			else if (ptCursor.x - rect.left < 2 * dx)
    				index = 1;
    			else index = 2;
    			WTL::CString str;
    			str.Format(_T("Cursor on %s part"), ppsz[index]);
    			CClientDC dc(m_hWnd);
    			dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
    		}
    		else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
    		return 0;
    	}

    闪烁产生的原因在于每次进入OnMouseMove之前,系统都会先将光标恢复成Arrow,进入OnMouseMove之后,如果光标处在RGB条带范围内则立即被设置成Cross。

    解决办法就是将上面的判断逻辑放在WM_SETCURSOR的消息处理中,当然获得光标客户坐标的方式不同,代码如下所示:

    1. LRESULT OnSetCursor(UINT /*uMsg*/WPARAM /*wParam*/LPARAM lParam, BOOL/*bHandled*/)  
    2. {  
    3.     POINT point;  
    4.     ::GetCursorPos(&point);  
    5.     ScreenToClient(&point);  
    6.     set_cursor(point);  
    7.     return 0;  
    8. }  
    	LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
    	{
    		POINT point;
    		::GetCursorPos(&point);
    		ScreenToClient(&point);
    		set_cursor(point);
    		return 0;
    	}

    而代码中的set_cursor私有方法其实就是上面的判断逻辑,即:

    1. // ptCursor: in client coordinate  
    2. void set_cursor(POINT& ptCursor) throw()  
    3. {  
    4.     RECT rect, rectText;  
    5.     get_rects(&rect, &rectText);  
    6.     InvalidateRect(&rectText);  
    7.     UpdateWindow();  
    8.     if (::PtInRect(&rect, ptCursor)) {  
    9.         ::SetCursor(m_cursor);  
    10.         int dx = (rect.right - rect.left) / 3;  
    11.         LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };  
    12.         int index;  
    13.         if (ptCursor.x - rect.left < dx)  
    14.             index = 0;  
    15.         else if (ptCursor.x - rect.left < 2 * dx)  
    16.             index = 1;  
    17.         else index = 2;  
    18.         WTL::CString str;  
    19.         str.Format(_T("Cursor on %s part"), ppsz[index]);  
    20.         CClientDC dc(m_hWnd);  
    21.         dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);  
    22.     }  
    23.     else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));  
    24. }  
    	// ptCursor: in client coordinate
    	void set_cursor(POINT& ptCursor) throw()
    	{
    		RECT rect, rectText;
    		get_rects(&rect, &rectText);
    		InvalidateRect(&rectText);
    		UpdateWindow();
    		if (::PtInRect(&rect, ptCursor)) {
    			::SetCursor(m_cursor);
    			int dx = (rect.right - rect.left) / 3;
    			LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
    			int index;
    			if (ptCursor.x - rect.left < dx)
    				index = 0;
    			else if (ptCursor.x - rect.left < 2 * dx)
    				index = 1;
    			else index = 2;
    			WTL::CString str;
    			str.Format(_T("Cursor on %s part"), ppsz[index]);
    			CClientDC dc(m_hWnd);
    			dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
    		}
    		else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
    	}

    这样就解决了光标闪烁的问题。

  • 相关阅读:
    python 自定义模块路径问题
    好书一下
    批量修改shell文件
    查看内存占用,排名最高开始
    prosql写法示例
    curl base64 python 请求api地址进行测试服务是否正常
    linxu家目录$ 或者是家目录丢失
    docker 添加普通用户权限
    关系型数据库和非关系型数据库的内在区别
    MapperScan的工作,Spring-Mybatis怎么自动getMapper
  • 原文地址:https://www.cnblogs.com/lujin49/p/4932065.html
Copyright © 2020-2023  润新知