光标闪烁问题的解决办法
在調用Windows API函數SetCursor設置光標時,可能會碰到閃爍的問題:移動鼠標,光標在Class Cursor(即注冊窗口類時指定的Cursor)與預設Cursor之間閃爍。
在MSDN上有關SetCursor函數的備注中強調,如果Class Cursor非空,那么每當鼠標移動,系統都會把光標恢復為Class Cursor。為了避免光標閃爍這一問題,必須處理WM_SETCURSOR消息。(MSDN說明)
下面是一個例子:程序在主窗口視圖的中間位置繪制RGB條帶,當鼠標移動在條帶范圍就將光標設置成為Cross,此外根據光標的位置,在RGB條帶上方30px處顯示所處條帶的顏色。程序運行起來像這樣:
如果在WM_MOUSEMOVE的消息處理中判斷光標的位置并設置光標的話,就會碰到所說的光標閃爍問題。WM_MOUSEMOVE的消息處理如下代碼所示:
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的消息處理中,當然獲得光標客戶坐標的方式不同,代碼如下所示:
LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/){POINT point;::GetCursorPos(&point);ScreenToClient(&point);set_cursor(point);return 0;}而代碼中的set_cursor私有方法其實就是上面的判斷邏輯,即:
// ptCursor: in client coordinatevoid 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));}這樣就解決了光標閃爍的問題。本例的工程文件可在這里下載,它是基于WTL的SDI程序。
P.S. 另外一個問題雖然簡單但仍值得一提。上面程序的截圖顯示的結果實際上并不準確,Cross光標的中心已經處于Green條帶,但條帶上方顯示的卻是"Cursor on Red part"。造成此問題的原因是自定義光標的熱點Hot Spot默認值為(0, 0),而不是以中心+來標識的。解決辦法是在VS資源編輯器中使用Hot Spot Tool,如下圖所示:
點擊Cross光標的中心點,熱點坐標將變成你所設置的值,如下圖所示:
總結
以上是生活随笔為你收集整理的光标闪烁问题的解决办法的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 使用gdbserver远程调试
- 下一篇: Java如何判断今天本月第几周的周几?
