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.