覆寫 OnSetCursor() 動態切換游標時,游標會在預設箭頭與自訂游標間閃爍,體驗極差。
MFC 預設視窗類別註冊時設定了預設游標 (hCursor)。當滑鼠移動進入視窗,系統先顯示類別預設游標,再呼叫 OnSetCursor 讓你切換,造成「顯示預設 → 切換自訂」兩次繪製 = 閃爍。
BOOL CMyView::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL) {
// CS_DBLCLKS 支援雙擊,第三參數 hbrBackground = NULL 代表無背景刷 (需自行 OnEraseBkgnd)
cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS, NULL, NULL, NULL);
}
return CScrollView::PreCreateWindow(cs);
}
關鍵:AfxRegisterWndClass 第四參數 hCursor 傳 NULL,系統不會為此視窗類別設定任何預設游標,徹底消除閃爍來源。
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
// 根據當前操作模式決定游標
switch (m_nOperation) {
case OP_DRAW_LINE:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));
break;
case OP_SELECT:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
break;
case OP_ZOOM:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE));
break;
default:
::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
}
CScrollView::OnMouseMove(nFlags, point);
}
因視窗類別無背景刷,必須處理 OnEraseBkgnd 避免殘影:
BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
CRect rect; GetClientRect(&rect);
pDC->FillSolidRect(&rect, ::GetSysColor(COLOR_WINDOW));
return TRUE; // 已處理,不再呼叫預設
}
| 方案 | 閃爍 | 複雜度 | 適用場景 |
|---|---|---|---|
| OnSetCursor 切換 | 有 | 低 | 單一固定游標 |
| PreCreateWindow NULL + OnMouseMove | 無 | 中 | 多模式動態游標 (繪圖/編輯/縮放) |
| SetClassLong GCL_HCURSOR 運行時改類別游標 | 可能 | 中 | 全域切換,影響同類別所有視窗 |
MFC SDI 專案示範:繪圖模式 (十字)、選取模式 (箭頭)、縮放模式 (雙向箭頭) 無縫切換,零閃爍。