MFC listctrl修改背景颜色

在 MFC 中修改 ListCtrl 控件的行背景颜色,需要通过自绘(Owner-Draw)机制实现。以下是详细的实现方法:

方法一:通过自绘(Owner-Draw)实现

步骤 1:启用自绘属性

在对话框设计器中选择 ListCtrl 控件,右键点击属性 → 样式 → 勾选 "自绘"(Owner Draw Fixed)。

步骤 2:处理 NM_CUSTOMDRAW 消息

在 ListCtrl 所在的对话框类(如 CMyDialog)中添加消息处理函数:

cpp 复制代码
// 在对话框类头文件(.h)中声明
afx_msg void OnNMCustomdrawList1(NMHDR *pNMHDR, LRESULT *pResult);

// 在对话框类实现文件(.cpp)中添加消息映射
BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    // ... 其他消息映射 ...
    ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST1, &CMyDialog::OnNMCustomdrawList1)
END_MESSAGE_MAP()

// 实现消息处理函数
void CMyDialog::OnNMCustomdrawList1(NMHDR *pNMHDR, LRESULT *pResult) {
    LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    *pResult = CDRF_DODEFAULT;

    // 阶段 1:预处理
    if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) {
        *pResult = CDRF_NOTIFYITEMDRAW;
    }
    // 阶段 2:绘制项目
    else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) {
        *pResult = CDRF_NOTIFYSUBITEMDRAW;
    }
    // 阶段 3:绘制子项
    else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) {
        int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);  // 当前行
        int nSubItem = pLVCD->iSubItem;  // 当前列

        // 根据条件设置背景色和文本色
        if (nItem == 0) {  // 例如:第一行设置为红色背景
            pLVCD->clrTextBk = RGB(255, 200, 200);  // 浅红色背景
            pLVCD->clrText = RGB(0, 0, 0);  // 黑色文本
        }
        else if (nItem == 1) {  // 第二行设置为蓝色背景
            pLVCD->clrTextBk = RGB(200, 200, 255);  // 浅蓝色背景
            pLVCD->clrText = RGB(0, 0, 0);  // 黑色文本
        }
        else {
            // 默认颜色(使用系统默认)
            pLVCD->clrTextBk = ::GetSysColor(COLOR_WINDOW);
            pLVCD->clrText = ::GetSysColor(COLOR_WINDOWTEXT);
        }

        *pResult = CDRF_NEWFONT;  // 使用新设置的颜色
    }
}

方法二:自定义 ListCtrl 类

更优雅的方式是创建一个继承自 CListCtrl 的自定义类,封装自绘逻辑。

步骤 1:创建自定义类
cpp 复制代码
// MyListCtrl.h
#pragma once

class CMyListCtrl : public CListCtrl {
    DECLARE_DYNAMIC(CMyListCtrl)

public:
    CMyListCtrl();
    virtual ~CMyListCtrl();

protected:
    DECLARE_MESSAGE_MAP()
    afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
};

// MyListCtrl.cpp
#include "stdafx.h"
#include "MyListCtrl.h"

IMPLEMENT_DYNAMIC(CMyListCtrl, CListCtrl)

CMyListCtrl::CMyListCtrl() {}
CMyListCtrl::~CMyListCtrl() {}

BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyListCtrl::OnNMCustomdraw)
END_MESSAGE_MAP()

void CMyListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {
    LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
    *pResult = CDRF_DODEFAULT;

    if (CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage) {
        *pResult = CDRF_NOTIFYITEMDRAW;
    }
    else if (CDDS_ITEMPREPAINT == pLVCD->nmcd.dwDrawStage) {
        *pResult = CDRF_NOTIFYSUBITEMDRAW;
    }
    else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) {
        int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);
        
        // 根据条件设置颜色(示例:交替行颜色)
        if (nItem % 2 == 0) {
            pLVCD->clrTextBk = RGB(240, 240, 240);  // 浅灰色背景
        }

        *pResult = CDRF_NEWFONT;
    }
}
步骤 2:在对话框中使用自定义类
  1. 在对话框头文件中添加成员变量:

    cpp 复制代码
    class CMyDialog : public CDialogEx {
        // ...
    private:
        CMyListCtrl m_listCtrl;  // 自定义 ListCtrl
    };
  2. 在对话框的 DoDataExchange 函数中关联控件:

    cpp 复制代码
    void CMyDialog::DoDataExchange(CDataExchange* pDX) {
        CDialogEx::DoDataExchange(pDX);
        DDX_Control(pDX, IDC_LIST1, m_listCtrl);
    }
  3. 在对话框初始化时设置 ListCtrl 属性:

    cpp 复制代码
    BOOL CMyDialog::OnInitDialog() {
        CDialogEx::OnInitDialog();
        
        // 设置 ListCtrl 样式
        m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | 
                                   LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
        
        // 添加列和数据...
        
        return TRUE;
    }

进阶技巧:动态更新行颜色

如果需要在运行时动态更改某行的颜色,可以在自定义类中添加公共方法:

cpp 复制代码
// 在 CMyListCtrl 类中添加
public:
    void SetItemBackgroundColor(int nItem, COLORREF color) {
        // 使用 CMap 存储每行的颜色
        m_rowColors.SetAt(nItem, color);
        InvalidateRect(NULL);  // 重绘列表
    }

private:
    CMap<int, int, COLORREF, COLORREF> m_rowColors;  // 存储行颜色的映射表

// 修改 OnNMCustomdraw 函数
void CMyListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult) {
    // ... 前面的代码不变 ...
    
    else if ((CDDS_ITEMPREPAINT | CDDS_SUBITEM) == pLVCD->nmcd.dwDrawStage) {
        int nItem = static_cast<int>(pLVCD->nmcd.dwItemSpec);
        
        // 检查是否有自定义颜色
        COLORREF bkColor;
        if (m_rowColors.Lookup(nItem, bkColor)) {
            pLVCD->clrTextBk = bkColor;
        }
        
        *pResult = CDRF_NEWFONT;
    }
}

使用时调用:

cpp 复制代码
m_listCtrl.SetItemBackgroundColor(2, RGB(255, 255, 200));  // 第三行设置为黄色背景

注意事项

  1. 性能考虑:自绘会增加渲染开销,对于大数据量列表,建议使用虚拟列表(LVS_OWNERDATA 样式)。
  2. 选中状态 :默认选中状态的颜色可能覆盖自定义颜色,可通过判断 pLVCD->nmcd.uItemState & CDIS_SELECTED 来特殊处理。
  3. 字体设置 :若需要自定义字体,需在代码中设置 pLVCD->nmcd.lpFont

通过以上方法,你可以灵活控制 ListCtrl 中任意行的背景颜色,实现个性化的界面效果。

相关推荐
汉克老师3 分钟前
第十四届蓝桥杯青少组C++选拔赛[2023.2.12]第二部分编程题(1、求和)
c++·蓝桥杯·蓝桥杯c++·c++蓝桥杯
XXYBMOOO23 分钟前
Qt UDP 通信类详解与实现
开发语言·网络·c++·qt·网络协议·ui·udp
君鼎28 分钟前
More Effective C++ 条款29:引用计数
c++
小欣加油32 分钟前
leetcode 6 Z字形变化
c++·算法·leetcode·职场和发展
曙曙学编程37 分钟前
stm32——寄存器操作,蜂鸣器原理
c语言·c++·stm32·单片机·嵌入式硬件
田里的水稻1 小时前
C++_数据类型和数据结构
java·数据结构·c++
notfindjob2 小时前
Opencv C++ 教程-人脸识别
c++·opencv·计算机视觉
极客智造2 小时前
OpenCV C++ 核心:Mat 与像素操作全解析
c++·人工智能·opencv
极客智造2 小时前
OpenCV C++ 色彩空间详解:转换、应用与 LUT 技术
c++·人工智能·opencv
小江村儿的文杰3 小时前
理解UE4中C++17的...符号及enable_if_t的用法及SFINAE思想
数据结构·c++·ue4