MFC 中調用 SetTimer 並通過 ClassWizard 添加 OnTimer 響應函數,但定時器觸發時繪圖代碼不執行,通常是消息映射未關聯、繪圖上下文錯誤或定時器 ID 衝突導致。
WM_TIMER 消息OnTimer(UINT_PTR nIDEvent)
BEGIN_MESSAGE_MAP(CMyDlg, CDialogEx)
ON_WM_TIMER()
END_MESSAGE_MAP()
// 啟動定時器:返回值為實際分配的 ID
UINT_PTR nTimerId = SetTimer(1, 1000, NULL); // ID=1, 間隔1秒
if (nTimerId == 0) {
AfxMessageBox(_T("定時器創建失敗"));
return;
}
// 成員變量保存 ID,便於 KillTimer
m_nTimerId = nTimerId;
OnTimer 中判斷 nIDEvent == m_nTimerIdSetTimer 導致 ID 衝突錯誤做法:在 OnTimer 中直接 CDC* pDC = GetDC(); 繪圖 → 閃爍、丟幀、坐標錯誤
正確做法:
void CMyDlg::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent == m_nTimerId) {
// 1. 標記需要重繪
InvalidateRect(NULL, FALSE); // 或指定區域 InvalidateRect(&rect, FALSE)
// 2. 可選:UpdateWindow() 強制立即重繪
// UpdateWindow();
}
CDialogEx::OnTimer(nIDEvent);
}
// 繪圖邏輯集中在 OnPaint
void CMyDlg::OnPaint() {
CPaintDC dc(this); // 自動 BeginPaint/EndPaint
// 這裡寫所有繪圖代碼
DrawMyGraphics(dc);
}
SetTimer 必須在 UI 線程調用,否則消息泵無法分發 WM_TIMERPostMessage 通知主線程創建定時器DoModal() 運行期間才有消息泵Create() 成功且消息泵正常OnTimer 首行設置斷點,確認是否進入TRACE(_T("OnTimer: %d\n"), nIDEvent); 輸出調試信息WM_TIMER 消息是否送達窗口
class CMyDlg : public CDialogEx {
UINT_PTR m_nTimerId = 0;
CRect m_rectDrawArea; // 繪圖區域
afx_msg void OnTimer(UINT_PTR nIDEvent);
void DrawMyGraphics(CDC& dc);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CMyDlg, CDialogEx)
ON_WM_TIMER()
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyDlg::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent == m_nTimerId) {
InvalidateRect(&m_rectDrawArea, FALSE); // 僅刷新繪圖區
}
CDialogEx::OnTimer(nIDEvent);
}
void CMyDlg::OnPaint() {
CPaintDC dc(this);
DrawMyGraphics(dc);
}
遵循「定時器只負責觸發重繪,OnPaint 統一繪圖」原則,配合 ClassWizard 正確映射消息,定時繪圖即可穩定運行。