程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> VC++ 解決在鼠標移動時,光標閃爍的問題。其實本質是 ON_SETCURSOR的用法,vcon_setcursor

VC++ 解決在鼠標移動時,光標閃爍的問題。其實本質是 ON_SETCURSOR的用法,vcon_setcursor

編輯:C++入門知識

VC++ 解決在鼠標移動時,光標閃爍的問題。其實本質是 ON_SETCURSOR的用法,vcon_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的消息處理如下代碼所示:

[cpp] view plaincopyprint?  
  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的消息處理中,當然獲得光標客戶坐標的方式不同,代碼如下所示:

[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));
	}

這樣就解決了光標閃爍的問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved