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

VC++ TreeCtrl 使用背景圖片並解決閃爍

     編輯:關於VC++

前言

標準 CTreeCtrl 不支援背景圖片。網上常見做法在 OnPaint 繪製背景再貼上 Tree 內容,但展開/摺疊節點時背景會劇烈閃爍。本文結合雙緩衝、WM_ERASEBKGND 抑制、TVN_ITEMEXPANDING/EXPANDED 控制重繪時機,實現無閃爍背景圖片 TreeCtrl。

效果展示

核心技術點

  1. 雙緩衝繪製:OnPaint 建立記憶體 DC,先讓 Tree 自繪到記憶體位圖,再混合背景圖,最後一次 BitBlt 到螢幕。
  2. 遮罩混合法:利用單色位圖遮罩 (SRCAND/SRCPAINT) 將 Tree 文字/圖示與背景圖無縫合成。
  3. 抑制預設擦除:OnEraseBkgnd 回傳 TRUE 防止系統擦除背景觸發閃爍。
  4. 展開/摺疊鎖定重繪:TVN_ITEMEXPANDING 呼叫 SetRedraw(FALSE) 暫停重繪,TVN_ITEMEXPANDED 呼叫 SetRedraw(TRUE) + Invalidate() 一次性更新。

完整實作代碼

1. 設定背景圖片

BOOL CMyTreeCtrl::SetBKImage(LPCTSTR lpszResource)
{
    if (m_bitmap.m_hObject) m_bitmap.DeleteObject();

    HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
        lpszResource, IMAGE_BITMAP, 0, 0,
        LR_CREATEDIBSECTION | LR_LOADFROMFILE);
    if (!hBmp) return FALSE;

    m_bitmap.Attach(hBmp);
    return TRUE;
}

2. 雙緩衝背景混合繪製 (OnPaint)

void CMyTreeCtrl::OnPaint()
{
    CPaintDC dc(this);
    CRect rcClient;
    GetClientRect(&rcClient);

    // 第一層記憶體 DC:讓 Tree 自繪內容
    CDC memDC;
    memDC.CreateCompatibleDC(&dc);
    CBitmap bmpMem;
    bmpMem.CreateCompatibleBitmap(&dc, rcClient.Width(), rcClient.Height());
    CBitmap* pOldBmpMem = memDC.SelectObject(&bmpMem);

    // 關鍵:傳入記憶體 DC 讓 DefWindowProc 繪製 Tree 內容
    CWnd::DefWindowProc(WM_PAINT, (WPARAM)memDC.m_hDC, 0);

    // 第二層記憶體 DC:建立遮罩 (單色位圖)
    CDC maskDC;
    maskDC.CreateCompatibleDC(&dc);
    CBitmap bmpMask;
    bmpMask.CreateBitmap(rcClient.Width(), rcClient.Height(), 1, 1, NULL);
    CBitmap* pOldBmpMask = maskDC.SelectObject(&bmpMask);

    // 將 Tree 內容複製到遮罩 (黑白)
    maskDC.BitBlt(0, 0, rcClient.Width(), rcClient.Height(),
                  &memDC, rcClient.left, rcClient.top, SRCCOPY);

    // 3. 在螢幕 DC 繪製背景圖 (平鋪)
    CBrush brush;
    brush.CreatePatternBrush(&m_bitmap);
    dc.FillRect(rcClient, &brush);

    // 4. 混合:Tree 文字/線條 (前景) + 背景圖
    memDC.SetBkColor(RGB(0,0,0));
    memDC.SetTextColor(RGB(255,255,255));
    memDC.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(),
                 &maskDC, rcClient.left, rcClient.top, SRCAND);

    dc.SetBkColor(RGB(255,255,255));
    dc.SetTextColor(RGB(0,0,0));
    dc.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(),
              &maskDC, rcClient.left, rcClient.top, SRCAND);

    dc.BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(),
              &memDC, rcClient.left, rcClient.top, SRCPAINT);

    // 清理
    memDC.SelectObject(pOldBmpMem);
    maskDC.SelectObject(pOldBmpMask);
    brush.DeleteObject();
}

3. 展開/摺疊無閃爍控制


void CMyTreeCtrl::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
{
    SetRedraw(FALSE); // 暫停重繪,避免展開動畫中背景閃爍
    *pResult = 0;
}

void CMyTreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult)
{
    Invalidate();     // 觸發一次完整重繪 (含背景混合)
    SetRedraw(TRUE);  // 恢復重繪
    *pResult = 0;
}

4. 抑制系統擦除背景


BOOL CMyTreeCtrl::OnEraseBkgnd(CDC* pDC)
{
    return TRUE; // 我們在 OnPaint 手繪背景,不需系統擦除
}

原理圖解:遮罩混合三步驟


1. Tree 內容 → memDC (彩色)
2. memDC → maskDC (單色遮罩:文字/線條=白,背景=黑)
3. 螢幕 DC:
   a) FillRect 背景圖
   b) memDC SRCAND maskDC → 保留文字/線條區域 (黑變透明)
   c) dc SRCAND maskDC   → 背景圖保留非文字區域 (白變透明)
   d) dc SRCPAINT memDC  → 疊加文字/線條
結果:背景圖完整顯示,Tree 文字/線條清晰疊加,無閃爍。

適用場景與注意

  • 適用於報表、資源管理器風格、皮膚化介面的 TreeCtrl。
  • 背景圖建議使用淡色/低對比度圖片,確保文字可讀性。
  • 大量節點 (>1000) 時 OnPaint 混合開銷可觀,可考慮快取合成結果或改用 Owner Draw。
  • FlashGet 1.3 同樣採用此技術實現皮膚化下載列表。

完整原始碼 (VC6.0/VC.NET 測試通過) 可下載。

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