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

CListCtrl/CHeaderCtrl 深度客製化:行高、字體、顏色、背景完整掌控

     編輯:關於VC++

需求背景

標準 MFC CListCtrl 報表模式下,表頭高度、行高、字體、前景/背景色皆無法直接設定。本文實作 CHeaderCtrlEx 與 CListCtrlEx 兩個繼承類別,透過 Owner Draw 機制實現完整視覺客製化。

架構設計


CHeaderCtrlEx : public CHeaderCtrl     // 自繪表頭
CListCtrlEx   : public CListCtrl       // 自繪列表項目 + 子類化表頭

關鍵技術點

1. 表頭高度調整 - 處理 HDM_LAYOUT


class CHeaderCtrlEx : public CHeaderCtrl {
    double m_HeightRatio = 1.0; // 高度倍數
    // ... 字體、顏色等成員

    afx_msg LRESULT OnLayout(WPARAM, LPARAM lParam) {
        LRESULT lr = DefWindowProc(HDM_LAYOUT, 0, lParam);
        HD_LAYOUT& hdl = *(HD_LAYOUT*)lParam;
        int newHeight = (int)(hdl.pwpos->cy * m_HeightRatio);
        hdl.pwpos->cy = newHeight;
        hdl.prc->top = newHeight; // 通知列表控制項新表頭高度
        return lr;
    }
    // 訊息對應
    ON_MESSAGE(HDM_LAYOUT, OnLayout)
};

2. 列表控制項初始化 - 子類化表頭 + Owner Draw


void CListCtrlEx::PreSubclassWindow() {
    ModifyStyle(0, LVS_OWNERDRAWFIXED); // 固定高度自繪
    CListCtrl::PreSubclassWindow();
    
    CHeaderCtrl* pHeader = GetHeaderCtrl();
    m_Header.SubclassWindow(pHeader->GetSafeHwnd()); // 替換為自訂表頭
}

3. 行高控制 - MeasureItem


void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) {
    if (m_RowHeight > 0)
        lpMIS->itemHeight = m_RowHeight;
}
ON_WM_MEASUREITEM_REFLECT()

4. 完整自繪邏輯 - DrawItem


void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
    CRect rcItem(lpDIS->rcItem);
    int nItem = lpDIS->itemID;
    UINT nState = lpDIS->itemState;

    // 1. 取得列數與欄寬
    CHeaderCtrl* pHeader = GetHeaderCtrl();
    int nCols = pHeader->GetItemCount();
    
    // 2. 計算背景色 (依優先序:單元格 > 列 > 欄 > 整體)
    COLORREF bgColor = GetItemBkColor(nItem, subItem); // 查詢各層級設定
    
    // 3. 填充背景
    CBrush brush(bgColor);
    pDC->FillRect(&rcItem, &brush);
    
    // 4. 繪製每欄文字
    for (int col = 0; col < nCols; col++) {
        LV_COLUMN lvc; lvc.mask = LVCF_FMT | LVCF_WIDTH;
        pHeader->GetItem(col, &lvc);
        
        CRect rcCol = rcItem;
        // 計算欄位矩形...
        
        CString text = GetItemText(nItem, col);
        COLORREF textColor = GetItemTextColor(nItem, col);
        
        pDC->SetTextColor(textColor);
        pDC->SetBkMode(TRANSPARENT);
        
        UINT fmt = (lvc.fmt & LVCFMT_JUSTIFYMASK);
        pDC->DrawText(text, &rcCol, fmt | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
    }
    
    // 5. 焦點框
    if (nState & ODS_FOCUS)
        pDC->DrawFocusRect(&rcItem);
}

5. 表頭自繪 - 圖文混排、漸層背景


void CHeaderCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDIS) {
    // 類似邏輯,支援:
    // - 自訂字體 (SetHeaderFontHW)
    // - 文字顏色 (SetHeaderTextColor)
    // - 背景色/漸層 (SetHeaderBKColor)
    // - 排序箭頭繪製
    // - 圖示支援 (m_HChar 陣列儲存標題文字)
}

公開介面 API 設計


class CListCtrlEx : public CListCtrl {
public:
    // 整體設定
    void SetBkColor(COLORREF clr);
    void SetTextColor(COLORREF clr);
    void SetFontHW(int height, int width = 0);
    void SetRowHeight(int h);
    void SetHeaderHeight(double ratio); // 1.0=預設, 1.5=1.5倍
    
    // 表頭專屬
    void SetHeaderFontHW(int h, int w);
    void SetHeaderTextColor(COLORREF clr);
    void SetHeaderBkColor(COLORREF clr); // 或漸層參數
    
    // 欄級別
    void SetColColor(int col, COLORREF bkClr);
    void SetColTextColor(int col, COLORREF txtClr);
    
    // 列級別
    void SetRowColor(int row, COLORREF bkClr);
    void SetRowTextColor(int row, COLORREF txtClr);
    
    // 單元格級別 (最高優先權)
    void SetItemColor(int row, int col, COLORREF bkClr);
    void SetItemTextColor(int row, int col, COLORREF txtClr);
};

使用範例


// OnInitDialog 中
m_List.SetColColor(0, RGB(10,150,20));     // 第 0 欄背景綠系
m_List.SetColColor(2, RGB(30,100,90));     // 第 2 欄背景青系
m_List.SetBkColor(RGB(50,10,10));          // 整體深紅背景
m_List.SetItemColor(1, 1, RGB(100,100,10)); // 第 1 列第 1 欄特殊色
m_List.SetRowHeight(25);                    // 行高 25 像素
m_List.SetHeaderHeight(1.5);                // 表頭 1.5 倍高
m_List.SetHeaderFontHW(16, 0);              // 表頭字體 16pt
m_List.SetHeaderTextColor(RGB(255,200,100));
m_List.SetHeaderBkColor(RGB(100,255,100));  // 表頭背景
m_List.SetFontHW(15, 0);                    // 內容字體 15pt
m_List.SetTextColor(RGB(0,255,255));        // 內容青色文字
m_List.SetColTextColor(2, RGB(255,255,100)); // 第 2 欄黃色文字
m_List.SetItemTextColor(3, 1, RGB(255,0,0)); // 第 3 列第 1 欄紅字

效能考量

  • Owner Draw 模式下每次重繪皆呼叫 DrawItem,大量資料時建議:
    • 使用 LVS_OWNERDATA (虛擬清單) 僅繪製可見項目
    • 快取筆刷、字體、格式化字串避免重複建立
    • 啟用雙緩衝 (WS_EX_COMPOSITED 或記憶體 DC) 減少閃爍
  • 表頭與列表分離設計,便於獨立維護與擴充

總結

透過繼承 CHeaderCtrl/CListCtrl 並攔截 HDM_LAYOUT/WM_MEASUREITEM/WM_DRAWITEM,配合多層級色彩/字體優先權設計,可實現企業級報表的完整視覺客製化。附帶原始碼提供完整可編譯專案。

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