在 MFC 開發中,若要在視窗標題欄顯示動態圖示,傳統做法是使用 SetTimer 配合 OnTimer 週期性更換圖示。但這種方式不僅程式碼繁瑣,還會佔用系統定時器資源。本文介紹一種將動畫邏輯封裝於獨立執行緒的 CAnimateIcon 類,只需兩行程式碼即可啟動動畫,大幅簡化開發流程。
原作者唐星彬的文章採用 SetTimer 觸發 OnTimer 切換圖示。本方案改為:
CAnimateIcon 類別內部SetImageList(載入圖示資源)與 Draw(啟動動畫)兩個介面class CAnimateIcon {
protected:
CImageList m_imgList; // 圖示列表
int m_iImageCounter; // 目前圖示索引
int m_nMaxNumOfImages; // 圖示總數
bool m_bExit; // 執行緒結束旗標
CWnd* m_pWnd; // 擁有圖示的視窗指標
DWORD m_dwMilliseconds; // 刷新間隔(毫秒)
public:
CAnimateIcon();
~CAnimateIcon();
bool SetImageList(int nResourceID, int nCount, COLORREF crTransparent);
BOOL Draw(CWnd* pWnd, DWORD dwInterval = 1000);
void ChangeInterval(DWORD dwTimes);
protected:
static UINT WINAPI _ThreadAnimation(LPVOID pParam);
};
Draw()BOOL CAnimateIcon::Draw(CWnd* pWnd, DWORD dwTimes) {
m_pWnd = pWnd;
ASSERT(m_pWnd);
m_dwMilliseconds = dwTimes;
unsigned int nThreadID;
HANDLE hThread = (HANDLE)_beginthreadex(
NULL, 0, _ThreadAnimation, this,
CREATE_SUSPENDED, &nThreadID
);
if (!hThread) {
TRACE(_T("Draw: Couldn't start ICON animation thread\n"));
return FALSE;
}
ResumeThread(hThread);
CloseHandle(hThread); // 不再需要句柄,立即關閉
return TRUE;
}
_ThreadAnimation()UINT WINAPI CAnimateIcon::_ThreadAnimation(LPVOID pParam) {
CAnimateIcon& self = *(CAnimateIcon*)pParam;
while (!self.m_bExit) {
if (self.m_imgList.m_hImageList == NULL) return 0;
self.m_iImageCounter++;
if (self.m_iImageCounter >= self.m_nMaxNumOfImages)
self.m_iImageCounter = 0;
HICON hIcon = self.m_imgList.ExtractIcon(self.m_iImageCounter);
HICON hPrevIcon = (HICON)self.m_pWnd->SetIcon(hIcon, FALSE);
DestroyIcon(hPrevIcon); // 釋放舊圖示資源
Sleep(self.m_dwMilliseconds);
}
return 1;
}
在視窗類別中宣告成員變數:
CAnimateIcon m_animIcon;
於 OnInitDialog 或 OnCreate 中初始化並啟動:
m_animIcon.SetImageList(IDB_BITMAP_ANIM, 6, RGB(0,0,0));
m_animIcon.Draw(this); // 預設 1000ms 間隔
若需調整速度,可呼叫 m_animIcon.ChangeInterval(500); 設為 500ms。
SetTimer 資源WM_TIMER 訊息m_bExit 可於解構子中設定,確保乾淨退出CImageList 要求)m_pWnd完整原始碼請參考本文配套下載。此方案同樣適用於工具列按鈕、系統托盤圖示等需動態切換圖示的場景。