在 Win32 API 中,Caret(光標) 是一個閃爍的視覺指示器,標識鍵盤輸入焦點位置。每個線程同一時刻只能擁有一個 Caret。窗口獲得鍵盤焦點(WM_SETFOCUS)時創建 Caret,失去焦點(WM_KILLFOCUS)時必須銷毀。
| 函數 | 用途 | 關鍵參數/返回值 |
|---|---|---|
CreateCaret | 創建光標 | hWnd 擁有窗口,hBitmap 位圖句柄(NULL 為矩形),寬高(邏輯單位) |
ShowCaret | 顯示並開始閃爍 | 返回 BOOL,成功非零 |
HideCaret | 暫時隱藏(不銷毀) | 配合 ShowCaret 使用 |
DestroyCaret | 徹底銷毀光標 | 釋放系統資源 |
SetCaretPos | 設置光標位置 | 客戶區坐標,隱藏狀態下也生效 |
GetCaretPos | 獲取當前位置 | 輸出 POINT 結構 |
GetCaretBlinkTime | 獲取閃爍半週期 | 毫秒數,完整週期 = 2×返回值 |
SetCaretBlinkTime | 設置閃爍半週期 | 參數為半週期毫秒數 |
void CMyWnd::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
// 創建實心矩形光標(寬 2px,高 16px)
// 尺寸建議使用 GetSystemMetrics(SM_CXBORDER/SM_CYBORDER) 適配 DPI
CreateCaret(NULL, 2, 16);
ShowCaret();
}
void CMyWnd::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
// 從資源加載位圖(需預先導入 .bmp 資源,IDB_CARET_BITMAP)
HBITMAP hBmp = LoadBitmap(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_CARET_BITMAP));
if (hBmp)
{
// 位圖模式下寬高參數被忽略
CreateCaret(hBmp, 0, 0);
ShowCaret();
}
}
void CMyWnd::OnKillFocus(CWnd* pNewWnd)
{
HideCaret(); // 先隱藏
DestroyCaret(); // 再銷毀
CWnd::OnKillFocus(pNewWnd);
}
某些特殊場景(如繪圖程序、無障礙輔助)需讓 Caret 跟隨滑鼠:
void CMyWnd::OnMouseMove(UINT nFlags, CPoint point)
{
// 客戶區坐標直接使用
SetCaretPos(point.x, point.y);
// 注意:高頻移動時閃爍可能不明顯,靜止片刻後恢復正常閃爍
CWnd::OnMouseMove(nFlags, point);
}
為獲得系統標準光標尺寸,推薦使用系統度量指標:
int cx = GetSystemMetrics(SM_CXBORDER); // 邊框寬度,通常 1-2 像素
int cy = GetSystemMetrics(SM_CYBORDER); // 邊框高度
// 文本編輯器常用:寬 = cx,高 = 字體高度
CreateCaret(NULL, cx, fontHeight);
WM_SETFOCUS 都創建,WM_KILLFOCUS 未銷毀 → GDI 對象泄漏,系統 Caret 表現異常。CreateCaret 對位圖像素取反顯示,設計位圖時需考慮反色效果。MFC 未直接封裝 Caret 類,但 CWnd 提供了成員函數別名:
// 等價於 ::CreateCaret(m_hWnd, ...)
CreateCaret(hBitmap, nWidth, nHeight);
ShowCaret();
HideCaret();
DestroyCaret();
SetCaretPos(point);
GetCaretPos(&point);
使用 MFC 封裝可自動綁定 this 窗口句柄,減少參數傳遞錯誤。
Win32 Caret 機制雖簡單,但涉及焦點生命周期、GDI 資源管理、DPI 適配等關鍵細節。掌握標準創建/銷毀流程、位圖光標製作、系統度量指標應用,能構建專業級文本編輯與交互體驗。現代應用中,複雜輸入法編輯器(IME)組合框、RichEdit 等控件已內建高級 Caret 邏輯,自定義需求通常僅限於特殊繪圖、無障礙或教學演示場景。