首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

MFC 對話框動態顯示位圖:記憶體 DC 雙緩衝繪製即時時鐘範例

     編輯:VC 教學

靜態 vs 動態位圖顯示

特性靜態 (Picture Control + Bitmap)動態 (記憶體 DC 繪製)
內容可變性固定,編譯時決定任意程式產生 (文字、圖形、圖片合成)
效能極高 (系統自動繪製)需手動管理 DC/位圖,略有開銷
適用場景Logo、背景圖、固定圖示即時時鐘、儀表盤、繪圖預覽、動態水印

核心技術:記憶體 DC 雙緩衝繪製流程

  1. 建立與螢幕 DC 相容的記憶體 DC (CreateCompatibleDC)。
  2. 建立相容位圖 (CreateCompatibleBitmap) 並選入記憶體 DC。
  3. 在記憶體 DC 上繪製背景、文字、圖形 (離屏操作,無閃爍)。
  4. BitBlt 將記憶體 DC 內容一次性複製到螢幕 DC 目標區域。
  5. 清理:取消選入位圖、刪除位圖/DC。

完整範例:對話框右上角即時數位時鐘

1. 專案建立與資源準備

  • 新建 MFC Dialog-based 專案 DynBitmapClock。
  • 刪除預設 TODO 靜態文字,調整對話框大小,將「確定/取消」移至底部。

2. 成員變數宣告 (DynBitmapClockDlg.h)


class CDynBitmapClockDlg : public CDialog
{
    // ...
protected:
    CRect m_rcClock; // 時鐘顯示區域 (用戶區座標)

    // 繪製函式
    bool DrawClock(CDC* pDC, const CString& strTime);
};

3. OnInitDialog:定時器啟動、計算顯示區域


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;
}

4. 核心繪製函式:DrawClock


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;
}

5. OnTimer:取得時間、呼叫繪製


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);
}

6. OnDestroy:清理定時器


void CDynBitmapClockDlg::OnDestroy()
{
    KillTimer(1);
    CDialog::OnDestroy();
}

關鍵技術細節解析

字體建立:CreatePointFont vs CreateFont

  • CreatePointFont(200, "Arial"):200 = 20 點 (1 點 = 10 twips),自動計算邏輯高度,支援 DPI 縮放。
  • CreateFont:需手動指定邏輯高度 (負值 = 字元高度,正值 = 字元寬度),控制更精細。

文字對齊:DT_CENTER | DT_VCENTER | DT_SINGLELINE

在矩形區域內水平垂直置中單行顯示,最適合數位時鐘。

3D 邊框:Draw3dRect

參數:矩形、陰影色 (左/上)、高光色 (右/下)。此處位圖區域外擴 2 像素繪製,形成凸起視覺效果。

資源管理鐵律:選入 → 繪製 → 取消選入 → 刪除

嚴格遵守 SelectObject(pOld) 後再 DeleteObject(),避免 GDI 物件洩漏導致系統資源耗盡。

進階擴充建議

  • 背景圖片:memDC.DrawPicture 或 BitBlt 載入背景位圖後再繪文字。
  • 抗鋸齒文字:使用 GDI+ Graphics::DrawString 配合 TextRenderingHintAntiAlias。
  • 透明背景:位圖建立 32-bit (含 Alpha 通道),FillSolidRect 使用 ARGB(0,0,0,0),最終 AlphaBlend 複製到螢幕。
  • 高 DPI 支援:GetDeviceCaps(LOGPIXELSX) 計算縮放比例,調整字體大小、區域尺寸。

結語

動態位圖顯示的核心是記憶體 DC 雙緩衝:離屏構建完整幀內容,單次 BitBlt 呈現。掌握 CreateCompatibleDC、CreateCompatibleBitmap、SelectObject、BitBlt、資源清理五步曲,即可在對話框、視圖、視窗任意位置繪製任意動態圖形。即時時鐘只是起點,儀表盤、波形圖、遊戲畫面同理可證。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved