ListView is a standard Windows control that displays a set of values per line, in particular in this article we will see how to alternate the colors in a report view.
In a report, you can color the lines simply by listening for a notification of CustomDraw, in the event you can have both entire rows, and the individual cells. To create the example given, we will need a single document MFC project with a ListView. Once built the project should set the valuesof simple styles and extended to our control, so we’re going to populate the rows and columns. Let the class code of sight, only to be changed and adapted to our needs.
// File ColorListView.h
#pragma once
class CColorListView : public CListView
{
protected:
CColorListView();
DECLARE_DYNCREATE(CColorListView)
public:
CColorListDoc* GetDocument() const;
public:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual void OnInitialUpdate();
public:
virtual ~CColorListView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);
};
#ifndef _DEBUG // debug version in ColorListView.cpp
inline CColorListDoc* CColorListView::GetDocument() const
{ return reinterpret_cast(m_pDocument); }
#endif
// ColorListView.cpp
#include "stdafx.h"
#include "ColorList.h"
#include "ColorListDoc.h"
#include "ColorListView.h"
// CColorListView
IMPLEMENT_DYNCREATE(CColorListView, CListView)
BEGIN_MESSAGE_MAP(CColorListView, CListView)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)
END_MESSAGE_MAP()
CColorListView::CColorListView()
{
}
CColorListView::~CColorListView()
{
}
BOOL CColorListView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= LVS_REPORT;
return CListView::PreCreateWindow(cs);
}
void CColorListView::OnInitialUpdate()
{
CListView::OnInitialUpdate();
CListCtrl& rList = GetListCtrl();
rList.SetExtendedStyle(LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT|
LVS_EX_LABELTIP|LVS_EX_BORDERSELECT);
// Insert columns
rList.InsertColumn(0, _T("First Column"), LVCFMT_LEFT, 200);
rList.InsertColumn(1, _T("Second Column"), LVCFMT_LEFT, 200);
rList.InsertColumn(2, _T("Third Column"), LVCFMT_LEFT, 200);
rList.InsertColumn(3, _T("Fourth Column"), LVCFMT_LEFT, 200);
rList.InsertColumn(4, _T("Fifth Column"), LVCFMT_LEFT, 200);
// Insert values
CString buffer;
for (int i = 0; i < 50; ++i)
{
buffer.Format(_T("Row %d Column 1"), i + 1);
rList.InsertItem(i, buffer);
buffer.Format(_T("Row %d Column 2"), i + 1);
rList.SetItemText(i, 1, buffer);
buffer.Format(_T("Row %d Column 3"), i + 1);
rList.SetItemText(i, 2, buffer);
buffer.Format(_T("Row %d Column 4"), i + 1);
rList.SetItemText(i, 3, buffer);
buffer.Format(_T("Row %d Column 5"), i + 1);
rList.SetItemText(i, 4, buffer);
}
rList.SetItemState(0, LVIS_SELECTED|LVIS_FOCUSED,
LVIS_SELECTED|LVIS_FOCUSED);
}
#ifdef _DEBUG
void CColorListView::AssertValid() const
{
CListView::AssertValid();
}
void CColorListView::Dump(CDumpContext& dc) const
{
CListView::Dump(dc);
}
CColorListDoc* CColorListView::GetDocument() const
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CColorListDoc)));
return (CColorListDoc*)m_pDocument;
}
#endif //_DEBUG
void CColorListView::OnNMCustomdraw(NMHDR *pNMHDR,
LRESULT *pResult)
{
LPNMLVCUSTOMDRAW pLVCD = reinterpret_cast(pNMHDR);
*pResult = CDRF_DODEFAULT;
switch (pLVCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_PREPAINT|CDDS_ITEM:
{
COLORREF crEven = RGB(96, 132, 132);
COLORREF crOdd = RGB(61, 84, 84);
COLORREF crText = RGB(250, 250, 250);
if ((pLVCD->nmcd.dwItemSpec % 2) == 0)
pLVCD->clrTextBk = crEven;
else
pLVCD->clrTextBk = crOdd;
pLVCD->clrText = crText;
}
break;
}
}
As you can see from the code, we need to intercept the notification when it shows the stage of drawing and returning a value to make sure we have a new notification for drawing the line. If we were to color every cell instead we should return a value that allows for a notification before the same color.
...
switch (pLVCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_PREPAINT|CDDS_ITEM:
*pResult = CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_PREPAINT|CDDS_ITEM|CDDS_SUBITEM:
...
Study well the code and put it inside the compiler, you will see that there are even lines of one color and odd ones of another, a very efficient method to improve the readability of the rows in a list.
From this example you can take inspiration for many other applications, such as darker color the column ordered.