在 MFC 中修改 ListCtrl 控件的行背景颜色,需要通过自绘(Owner-Draw)机制实现。以下是详细的实现方法:
方法一:通过自绘(Owner-Draw)实现
步骤 1:启用自绘属性
在对话框设计器中选择 ListCtrl 控件,右键点击属性 → 样式 → 勾选 “自绘”(Owner Draw Fixed)。
步骤 2:处理 NM_CUSTOMDRAW 消息
在 ListCtrl 所在的对话框类(如 CMyDialog
)中添加消息处理函数:
// 在对话框类头文件(.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:创建自定义类
// 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:在对话框中使用自定义类
在对话框头文件中添加成员变量:
class CMyDialog : public CDialogEx { // ... private: CMyListCtrl m_listCtrl; // 自定义 ListCtrl };
在对话框的
DoDataExchange
函数中关联控件:void CMyDialog::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST1, m_listCtrl); }
在对话框初始化时设置 ListCtrl 属性:
BOOL CMyDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置 ListCtrl 样式 m_listCtrl.SetExtendedStyle(m_listCtrl.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); // 添加列和数据... return TRUE; }
进阶技巧:动态更新行颜色
如果需要在运行时动态更改某行的颜色,可以在自定义类中添加公共方法:
// 在 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;
}
}
使用时调用:
m_listCtrl.SetItemBackgroundColor(2, RGB(255, 255, 200)); // 第三行设置为黄色背景
注意事项
- 性能考虑:自绘会增加渲染开销,对于大数据量列表,建议使用虚拟列表(LVS_OWNERDATA 样式)。
- 选中状态:默认选中状态的颜色可能覆盖自定义颜色,可通过判断
pLVCD->nmcd.uItemState & CDIS_SELECTED
来特殊处理。 - 字体设置:若需要自定义字体,需在代码中设置
pLVCD->nmcd.lpFont
。
通过以上方法,你可以灵活控制 ListCtrl 中任意行的背景颜色,实现个性化的界面效果。