| 特性 | 靜態 (Picture Control + Bitmap) | 動態 (記憶體 DC 繪製) |
|---|---|---|
| 內容可變性 | 固定,編譯時決定 | 任意程式產生 (文字、圖形、圖片合成) |
| 效能 | 極高 (系統自動繪製) | 需手動管理 DC/位圖,略有開銷 |
| 適用場景 | Logo、背景圖、固定圖示 | 即時時鐘、儀表盤、繪圖預覽、動態水印 |
CreateCompatibleDC)。CreateCompatibleBitmap) 並選入記憶體 DC。BitBlt 將記憶體 DC 內容一次性複製到螢幕 DC 目標區域。DynBitmapClock。
class CDynBitmapClockDlg : public CDialog
{
// ...
protected:
CRect m_rcClock; // 時鐘顯示區域 (用戶區座標)
// 繪製函式
bool DrawClock(CDC* pDC, const CString& strTime);
};
BOOL CDynBitmapClockDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// 1. 啟動定時器 (100ms 更新,顯示秒數需 1000ms,100ms 較平滑)
SetTimer(1, 100, NULL);
// 2. 計算顯示區域:對話框右上角,寬 180、高 45,距右/上邊緣 25/35 像素
CRect rcClient;
GetClientRect(&rcClient);
int w = 180, h = 45;
m_rcClock.left = rcClient.right - 25 - w;
m_rcClock.top = rcClient.top + 35;
m_rcClock.right = m_rcClock.left + w;
m_rcClock.bottom = m_rcClock.top + h;
return TRUE;
}
bool CDynBitmapClockDlg::DrawClock(CDC* pDC, const CString& strTime)
{
if (!pDC || !pDC->GetSafeHdc()) return false;
// 1. 建立記憶體 DC
CDC memDC;
if (!memDC.CreateCompatibleDC(pDC)) return false;
// 2. 建立相容位圖
CBitmap bmp;
if (!bmp.CreateCompatibleBitmap(pDC, m_rcClock.Width(), m_rcClock.Height()))
{
memDC.DeleteDC();
return false;
}
// 3. 選入位圖,保存舊位圖
CBitmap* pOldBmp = memDC.SelectObject(&bmp);
// 4. 離屏繪製
// 4.1 背景填充 (黑色)
memDC.FillSolidRect(0, 0, m_rcClock.Width(), m_rcClock.Height(), RGB(0, 0, 0));
// 4.2 設定字體 (Arial, 20 點 = 200 twips)
CFont font;
font.CreatePointFont(200, _T("Arial"), &memDC);
CFont* pOldFont = memDC.SelectObject(&font);
// 4.3 文字屬性
memDC.SetBkMode(TRANSPARENT); // 透明背景
memDC.SetTextColor(RGB(255, 0, 0)); // 紅色文字
// 4.4 置中繪製時間字串
CRect rcText(0, 0, m_rcClock.Width(), m_rcClock.Height());
memDC.DrawText(strTime, &rcText, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
// 4.5 繪製 3D 邊框 (位圖邊緣外擴 2 像素)
CRect rcBorder = m_rcClock;
rcBorder.InflateRect(2, 2);
pDC->Draw3dRect(&rcBorder, RGB(0,0,0), RGB(255,255,255)); // 陰/亮 色
// 5. 一次性複製到螢幕
BOOL result = pDC->BitBlt(
m_rcClock.left, m_rcClock.top,
m_rcClock.Width(), m_rcClock.Height(),
&memDC, 0, 0, SRCCOPY
);
// 6. 清理資源 (逆序)
memDC.SelectObject(pOldFont);
memDC.SelectObject(pOldBmp);
font.DeleteObject();
bmp.DeleteObject();
memDC.DeleteDC();
return result;
}
void CDynBitmapClockDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1)
{
CTime now = CTime::GetCurrentTime();
CString strTime = now.Format(_T("%H:%M:%S")); // 24小時制
CClientDC dc(this); // 自動 ReleaseDC
if (!DrawClock(&dc, strTime))
{
AfxMessageBox(_T("時鐘繪製失敗"));
KillTimer(1);
}
}
CDialog::OnTimer(nIDEvent);
}
void CDynBitmapClockDlg::OnDestroy()
{
KillTimer(1);
CDialog::OnDestroy();
}
CreatePointFont(200, "Arial"):200 = 20 點 (1 點 = 10 twips),自動計算邏輯高度,支援 DPI 縮放。CreateFont:需手動指定邏輯高度 (負值 = 字元高度,正值 = 字元寬度),控制更精細。在矩形區域內水平垂直置中單行顯示,最適合數位時鐘。
參數:矩形、陰影色 (左/上)、高光色 (右/下)。此處位圖區域外擴 2 像素繪製,形成凸起視覺效果。
嚴格遵守 SelectObject(pOld) 後再 DeleteObject(),避免 GDI 物件洩漏導致系統資源耗盡。
memDC.DrawPicture 或 BitBlt 載入背景位圖後再繪文字。Graphics::DrawString 配合 TextRenderingHintAntiAlias。FillSolidRect 使用 ARGB(0,0,0,0),最終 AlphaBlend 複製到螢幕。GetDeviceCaps(LOGPIXELSX) 計算縮放比例,調整字體大小、區域尺寸。動態位圖顯示的核心是記憶體 DC 雙緩衝:離屏構建完整幀內容,單次 BitBlt 呈現。掌握 CreateCompatibleDC、CreateCompatibleBitmap、SelectObject、BitBlt、資源清理五步曲,即可在對話框、視圖、視窗任意位置繪製任意動態圖形。即時時鐘只是起點,儀表盤、波形圖、遊戲畫面同理可證。