`CTreeCtrl` 不支援自繪,直接設定背景圖片需繞過繪製流程。核心技巧:將樹狀控制項繪製到記憶體 DC,再透明混合到背景位圖上。本文提供 `CTreeCtrlX` 衍生類完整實作,含位圖載入、調色板建立、WM_ERASEBKGND/WM_PAINT 訊息處理與平鋪/置中顯示選項。
protected:
CPalette m_pal; // 邏輯調色板
CBitmap m_bmp; // 背景位圖
int m_cxBmp, m_cyBmp; // 位圖尺寸
BOOL CTreeCtrlX::SetBkImage(UINT nIDResource) {
return SetBkImage(MAKEINTRESOURCE(nIDResource));
}
BOOL CTreeCtrlX::SetBkImage(LPCTSTR lpszResourceName) {
// 1. 刪除舊 GDI 物件
if (m_bmp.m_hObject) m_bmp.DeleteObject();
if (m_pal.m_hObject) m_pal.DeleteObject();
// 2. 載入 DIBSECTION 以取得色彩表
HBITMAP hBmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
lpszResourceName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
if (!hBmp) return FALSE;
m_bmp.Attach(hBmp);
BITMAP bm; m_bmp.GetBitmap(&bm);
m_cxBmp = bm.bmWidth; m_cyBmp = bm.bmHeight;
// 3. 建立邏輯調色板(256 色以下)
if (bm.bmBitsPixel <= 8) {
int nColors = 1 << bm.bmBitsPixel;
RGBQUAD* pRGB = new RGBQUAD[nColors];
::GetDIBColorTable(m_bmp.GetSafeHandle(), 0, nColors, pRGB);
LOGPALETTE* pLP = (LOGPALETTE*)new BYTE[sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY)];
pLP->palVersion = 0x300;
pLP->palNumEntries = nColors;
for (int i = 0; i < nColors; i++) {
pLP->palPalEntry[i].peRed = pRGB[i].rgbRed;
pLP->palPalEntry[i].peGreen = pRGB[i].rgbGreen;
pLP->palPalEntry[i].peBlue = pRGB[i].rgbBlue;
pLP->palPalEntry[i].peFlags = 0;
}
m_pal.CreatePalette(pLP);
delete[] pLP; delete[] pRGB;
}
Invalidate(); // 觸發重繪
return TRUE;
}
// 擦除背景:不做任何事,防止閃爍
BOOL CTreeCtrlX::OnEraseBkgnd(CDC* pDC) { return TRUE; }
// 繪製:先畫背景,再呼叫預設繪製
void CTreeCtrlX::OnPaint() {
CPaintDC dc(this);
CDC memDC; memDC.CreateCompatibleDC(&dc);
CBitmap* pOldBmp = memDC.SelectObject(&m_bmp);
// 取得用戶區大小
CRect rect; GetClientRect(&rect);
// 平鋪繪製背景
for (int y = 0; y < rect.Height(); y += m_cyBmp)
for (int x = 0; x < rect.Width(); x += m_cxBmp)
dc.BitBlt(x, y, m_cxBmp, m_cyBmp, &memDC, 0, 0, SRCCOPY);
memDC.SelectObject(pOldBmp);
// 關鍵:呼叫預設繪製讓節點/文字覆蓋在背景上
Default(); // 或 CTreeCtrl::OnPaint()
}
// 在對話框 OnInitDialog 或 View OnInitialUpdate 中
m_tree.SetBkImage(IDB_MY_BACKGROUND); // IDB_ 為位圖資源 ID