MDI 應用程式主框架客戶區 (CMDIFrameWnd 內部 CMDIClientWnd) 預設為單色背景。透過衍生 CWnd 類別 CMdiMainClient 接管客戶區繪製,實現位圖輪播動畫背景。
class CMdiMainClient : public CWnd {
CBitmap m_bmpBackground; // 靜態背景 (可選)
CBitmap m_bmpBanner[8]; // 動畫幀位圖陣列 (資源 IDB_BITMAP0~7)
int m_nCurrentFrame; // 當前幀索引
BOOL m_bStopRotation; // 暫停旗標
CSize m_sizeClient; // 客戶區尺寸
// ...
};
CMdiMainClient::CMdiMainClient() : m_nCurrentFrame(0), m_bStopRotation(FALSE) {
VERIFY(m_bmpBackground.LoadBitmap(IDB_BCKGRD)); // 基底背景
for (int i = 0; i < 8; ++i) {
VERIFY(m_bmpBanner[i].LoadBitmap(IDB_BITMAP0 + i)); // 動畫幀
}
}
BEGIN_MESSAGE_MAP(CMdiMainClient, CWnd)
ON_WM_TIMER()
ON_WM_ERASEBKGND()
ON_WM_RBUTTONDOWN() // 右鍵啟動/停止
ON_WM_LBUTTONDOWN() // 左鍵建立子視窗測試
ON_WM_SIZE()
END_MESSAGE_MAP()
OnEraseBkgnd 繪製當前幀
BOOL CMdiMainClient::OnEraseBkgnd(CDC* pDC) {
CRect rect; GetClientRect(&rect);
// 1. 繪製基底背景 (平鋪或拉伸)
if (m_bmpBackground.GetSafeHandle()) {
CDC dcMem; dcMem.CreateCompatibleDC(pDC);
CBitmap* pOld = dcMem.SelectObject(&m_bmpBackground);
BITMAP bm; m_bmpBackground.GetBitmap(&bm);
pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &dcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
dcMem.SelectObject(pOld);
}
// 2. 繪製動畫幀 (置中或指定位置)
if (!m_bStopRotation && m_bmpBanner[m_nCurrentFrame].GetSafeHandle()) {
CDC dcMem; dcMem.CreateCompatibleDC(pDC);
CBitmap* pOld = dcMem.SelectObject(&m_bmpBanner[m_nCurrentFrame]);
BITMAP bm; m_bmpBanner[m_nCurrentFrame].GetBitmap(&bm);
int x = (rect.Width() - bm.bmWidth) / 2;
int y = (rect.Height() - bm.bmHeight) / 2;
// 透明繪製需用 TransparentBlt 或遮罩法 (參考前文)
pDC->BitBlt(x, y, bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(pOld);
}
return TRUE; // 已處理背景擦除,系統不再填充實心筆刷
}
OnTimer 輪播幀
void CMdiMainClient::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent == 1 && !m_bStopRotation) {
m_nCurrentFrame = (m_nCurrentFrame + 1) % 8;
Invalidate(FALSE); // 觸發 WM_ERASEBKGND 重繪 (不擦除背景,由我們全繪)
}
CWnd::OnTimer(nIDEvent);
}
OnSize 同步尺寸與啟動定時器
void CMdiMainClient::OnSize(UINT nType, int cx, int cy) {
CWnd::OnSize(nType, cx, cy);
m_sizeClient = CSize(cx, cy);
if (!m_bStopRotation) {
SetTimer(1, 300, NULL); // 300ms 換幀 (~3.3 FPS)
}
}
void CMdiMainClient::OnRButtonDown(UINT nFlags, CPoint point) {
m_bStopRotation = !m_bStopRotation;
if (!m_bStopRotation) SetTimer(1, 300, NULL);
else KillTimer(1);
CWnd::OnRButtonDown(nFlags, point);
}
void CMdiMainClient::OnLButtonDown(UINT nFlags, CPoint point) {
// 建立 MDI 子視窗測試動畫持續
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)GetParent();
pFrame->MDICreateWindow(...);
CWnd::OnLButtonDown(nFlags, point);
}
// CMainFrame::OnCreate
if (!m_wndClient.Create(NULL, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
CRect(0,0,0,0), this, AFX_IDW_PANE_FIRST, NULL)) {
return -1;
}
// m_wndClient 為 CMdiMainClient 成員
// 需在 CMainFrame 建構前設定 m_pClientWnd = &m_wndClient (MFC 內部機制)
或修改 CMainFrame 類別宣告:CMdiMainClient m_wndClient; 並覆寫 GetClientWnd() 返回 &m_wndClient。
OnEraseBkgnd 中建立記憶體 DC 繪製完整幀,再一次 BitBlt 到螢幕,消除閃爍。TransparentBlt 或遮罩法疊加在基底背景上。StretchBlt 縮放 (品質下降)。CreateCompatibleDC。