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

VC++ 可設定單元格顏色的 CListCtrl 擴充:NM_CUSTOMDRAW 實作逐格著色

     編輯:VC 教學

前言

標準 `CListCtrl` 僅支援整行選取色、交替行色,無法針對單一儲存格設定前景/背景色。利用 `NM_CUSTOMDRAW` 通知訊息,在 `CDDS_ITEMPREPAINT | CDDS_SUBITEM` 階段攔截繪製流程,即可實現任意儲存格級別的顏色自訂。本文提供 `CColorListCtrl` 類別,封裝 `SetItemColor(nRow, nCol, clrText, clrBkgnd)` 介面,內部以 `map` 儲存顏色資料(鍵 = `MAKELONG(nRow, nCol)`),繪製時查表套用。

一、類別設計


class CColorListCtrl : public CListCtrl
{
    DECLARE_DYNAMIC(CColorListCtrl)
public:
    CColorListCtrl();
    virtual ~CColorListCtrl();

    // 設定單元格顏色:clrText=-1 表不變更前景,clrBkgnd=-1 表不變更背景
    void SetItemColor(int nItem, int nSubItem, COLORREF clrText, COLORREF clrBkgnd);
    void RemoveItemColor(int nItem, int nSubItem); // 恢復預設
    void ClearAllColors(); // 清除所有自訂顏色

protected:
    struct COLORPAIR { COLORREF clrText; COLORREF clrBkgnd; };
    CMap m_mapColors; // 鍵 = MAKELONG(nItem, nSubItem)

    afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
    DECLARE_MESSAGE_MAP()
};

二、核心繪製攔截:OnCustomDraw


BEGIN_MESSAGE_MAP(CColorListCtrl, CListCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()

void CColorListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
    NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)pNMHDR;
    *pResult = CDRF_DODEFAULT;

    switch (pLVCD->nmcd.dwDrawStage)
    {
    case CDDS_PREPAINT:
        // 請求項目級別通知
        *pResult = CDRF_NOTIFYITEMDRAW;
        break;

    case CDDS_ITEMPREPAINT:
        // 請求子項目級別通知
        *pResult = CDRF_NOTIFYSUBITEMDRAW;
        break;

    case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
    {
        int nItem = (int)pLVCD->nmcd.dwItemSpec;
        int nSubItem = pLVCD->iSubItem;

        DWORD dwKey = MAKELONG(nItem, nSubItem);
        COLORPAIR cp;
        if (m_mapColors.Lookup(dwKey, cp))
        {
            // 設定文字色
            if (cp.clrText != (COLORREF)-1)
                pLVCD->clrText = cp.clrText;
            // 設定背景色
            if (cp.clrBkgnd != (COLORREF)-1)
                pLVCD->clrTextBk = cp.clrBkgnd;
        }
        *pResult = CDRF_DODEFAULT; // 繼續預設繪製流程(已套用顏色)
        break;
    }
    }
}

三、介面函數實作


void CColorListCtrl::SetItemColor(int nItem, int nSubItem, COLORREF clrText, COLORREF clrBkgnd)
{
    if (nItem < 0 || nSubItem < 0) return;
    DWORD dwKey = MAKELONG(nItem, nSubItem);
    COLORPAIR cp = {clrText, clrBkgnd};
    m_mapColors[dwKey] = cp;
    // 觸發重繪該項目
    CRect rc; GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rc);
    InvalidateRect(&rc);
}

void CColorListCtrl::RemoveItemColor(int nItem, int nSubItem)
{
    DWORD dwKey = MAKELONG(nItem, nSubItem);
    m_mapColors.RemoveKey(dwKey);
    CRect rc; GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rc);
    InvalidateRect(&rc);
}

void CColorListCtrl::ClearAllColors()
{
    m_mapColors.RemoveAll();
    Invalidate();
}

四、使用範例


void CMyDialog::OnInitList()
{
    m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
    m_list.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 60);
    m_list.InsertColumn(1, _T("姓名"), LVCFMT_LEFT, 120);
    m_list.InsertColumn(2, _T("生日"), LVCFMT_LEFT, 100);

    m_list.InsertItem(0, _T("1")); m_list.SetItemText(0, 1, _T("張三")); m_list.SetItemText(0, 2, _T("1990-01-15"));
    m_list.InsertItem(1, _T("2")); m_list.SetItemText(1, 1, _T("李四")); m_list.SetItemText(1, 2, _T("1985-07-22"));
    m_list.InsertItem(2, _T("3")); m_list.SetItemText(2, 1, _T("王五")); m_list.SetItemText(2, 2, _T("1992-11-03"));

    // 設定單元格顏色:第 0 列第 1 欄(姓名)紅字綠底
    m_list.SetItemColor(0, 1, RGB(255,0,0), RGB(0,255,0));
    // 第 1 列第 2 欄(生日)藍字黃底
    m_list.SetItemColor(1, 2, RGB(0,0,255), RGB(255,255,0));
    // 第 2 列整行淺灰背景(遍歷所有欄)
    for (int c = 0; c < 3; ++c) m_list.SetItemColor(2, c, (COLORREF)-1, RGB(230,230,230));
}

五、執行效果

ListCtrl 單元格著色示範

報表模式下,指定儲存格呈現自訂前景/背景色,整行選取時自訂色優先級高於選取高亮(可依需求在 `CDDS_ITEMPREPAINT` 階段處理選取狀態)。

六、進階:整行選取與自訂色共存

若啟用 `LVS_EX_FULLROWSELECT`,選取高亮會覆蓋子項目背景色。解決方案:在 `CDDS_ITEMPREPAINT` 階段檢查 `pLVCD->nmcd.uItemState & CDIS_SELECTED`,若為選取狀態且該列有自訂色,手動繪製背景(`CDC::FillSolidRect`)並返回 `CDRF_SKIPDEFAULT`,但需自行繪製文字、圖示、狀態圖示,複雜度較高。替代方案:不使用 FULLROWSELECT,改用 `LVS_EX_ONECLICKACTIVATE` + `LVS_EX_UNDERLINEHOT` 模擬超連結風格,或接受子項目背景色在選取時被覆蓋(僅文字色保留)。

七、結語

`NM_CUSTOMDRAW` 是 `CListCtrl` 視覺客製化的核心入口。`CColorListCtrl` 以 `CMap` 儲存稀疏顏色資料、在 `CDDS_SUBITEM` 階段套用 `clrText/clrTextBk`,實現零侵入、高效能的單元格著色。此模式同樣適用於 `CTreeCtrl`、`CHeaderCtrl` 等支援 CustomDraw 的通用控制項,是 MFC 開發者掌握進階介面定製的必修課。

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