MFC 允許同一窗口創建多個定時器,關鍵在於 SetTimer 的 nIDEvent 參數與 OnTimer 中的 switch-case 分發。
UINT_PTR SetTimer(
UINT_PTR nIDEvent, // 定時器唯一標識,自定義非 0 值
UINT uElapse, // 間隔,毫秒
TIMERPROC lpTimerFunc // 回調函數,NULL 表示使用 WM_TIMER 消息
);
// 在 OnInitDialog 或按鈕處理中啟動
void CMyDialog::StartTimers()
{
// 定時器 1:秒針,每 1000ms
SetTimer(1, 1000, NULL);
// 定時器 2:分鐘針,每 60000ms
SetTimer(2, 60000, NULL);
// 定時器 3:動畫幀,每 30ms
SetTimer(3, 30, NULL);
}
void CMyDialog::OnTimer(UINT_PTR nIDEvent)
{
switch (nIDEvent)
{
case 1: // 秒針
UpdateSecondHand();
break;
case 2: // 分鐘針
UpdateMinuteHand();
break;
case 3: // 動畫
AnimateGauge();
break;
default:
CDialogEx::OnTimer(nIDEvent); // 基類處理
}
}
void CMyDialog::StopTimers()
{
KillTimer(1);
KillTimer(2);
KillTimer(3);
}
// 或在 OnDestroy 統一清理
void CMyDialog::OnDestroy()
{
KillTimer(1); KillTimer(2); KillTimer(3);
CDialogEx::OnDestroy();
}
若定時器數量動態,可用成員變量記錄:
class CMyDialog : public CDialogEx
{
UINT_PTR m_timerIds[3]; // 存儲返回的真實 ID
void StartDynamicTimer(int interval, std::function<void()> callback)
{
// 尋找空槽
for (int i = 0; i < 3; ++i) {
if (m_timerIds[i] == 0) {
m_timerIds[i] = SetTimer(i + 100, interval, NULL); // 基礎 ID 100
m_callbacks[i] = callback;
break;
}
}
}
void OnTimer(UINT_PTR nIDEvent) override
{
for (int i = 0; i < 3; ++i) {
if (m_timerIds[i] == nIDEvent && m_callbacks[i]) {
m_callbacks[i]();
return;
}
}
CDialogEx::OnTimer(nIDEvent);
}
std::function<void()> m_callbacks[3];
};
| 錯誤 | 後果 | 修正 |
|---|---|---|
多次調用 SetTimer(1,...) 未 KillTimer | 定時器疊加,回調頻率倍增 | 啟動前先 KillTimer(1) |
在 OnTimer 執行耗時操作 | 阻塞 UI 線程,導致定時器積壓、界面卡頓 | 耗時任務拋給工作線程 |
| 銷毀對話框前未 KillTimer | 定時器觸發時訪問已銷毀對象,崩潰 | OnDestroy 中統一清理 |
nIDEvent 是多定時器的核心:創建時指定唯一 ID,OnTimer 中 switch(nIDEvent) 分發邏輯,銷毀時 KillTimer(ID) 清理。掌握此模式,即可輕鬆實現複雜的多時序 UI 控制。