Custom Draw items inside a ListView Control
--------------------------------------------------------------------------------
This article was contributed by Navi Singh.
If you do not want to go through the hassle implementing OwnerDraw list controls, where you got to code a bunch of stuff inside the DrawItem override, then you can use the CustomDraw handler. With version 4.70 of the Comctrl.dll, you can handle row data, but with the 4.72+ version of the Dll, you can handle cell data. Which opens a lot of possibilities.
Here I present some examples on how to use the CustomDraw message.
Note: Similar handling is also possible for the other common controls.
Add the following to your CListCtrl derived class header file:
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
// add the following to your message map in the cpp file.
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
// add the following function to the cpp file.
// for specialized row handling.
// modify to suit. (in this sample function, I color every alternate row).
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
//for this notification, the structure is actually a
// NMLVCUSTOMDRAW that tells you what 's going on with the custom
// draw action. So, we 'll need to cast the generic pNMHDR pointer.
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;
switch(lplvcd-> nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW; // ask for item notifications.
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_DODEFAULT;
int iRow = lplvcd-> nmcd.dwItemSpec;
if(iRow & 1)
{
lplvcd-> clrTextBk = RGB(255, 0, 0);
lplvcd-> clrText = RGB(255, 255, 0);
*pResult = CDRF_NEWFONT;
}
break;
default:
*pResult = CDRF_DODEFAULT;
}
}
// add the following function to the cpp file.
// for specialized cell handling.
// modify to suit. (in this sample function, I color every alternate cell).
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
//for this notification, the structure is actually a
// NMLVCUSTOMDRAW that tells you what 's going on with the custom
// draw action. So, we 'll need to cast the generic pNMHDR pointer.
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;
switch(lplvcd-> nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications.
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications.
break;
case CDDS_ITEMPREPAINT|CDDS_SUBITEM: // recd when CDRF_NOTIFYSUBITEMDRAW is returned in
{ // response to CDDS_ITEMPREPAINT.
*pResult = CDRF_DODEFAULT;
int iCol = lplvcd-> iSubItem;
int iRow = lplvcd-> nmcd.dwItemSpec;
if((iRow & 1) && (iCol & 1))
{
lplvcd-> clrTextBk = RGB(255, 0, 0);
lplvcd-> clrText = RGB(255, 255, 0);
*pResult = CDRF_NEWFONT;
}
break;
}
default:// it wasn 't a notification that was interesting to us.
*pResult = CDRF_DODEFAULT;
}
}
// add the following function to the cpp file.
// for specialized cell handling.
// modify to suit.
// (in this sample function, I color every alternate cell,
// if the checkbox style is not present, otherwise, I color all checked rows).
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
//for this notification, the structure is actually a
// NMLVCUSTOMDRAW that tells you what 's going on with the custom
// draw action. So, we 'll need to cast the generic pNMHDR pointer.
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;
switch(lplvcd-> nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications.
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications.
if(GetExtendedStyle() & LVS_EX_CHECKBOXES) // if we have a checkbox style,
{ // forget about subitem notifications.
*pResult = CDRF_DODEFAULT;
int iRow = lplvcd-> nmcd.dwItemSpec;
if(GetCheck(iRow)) // highlight checked rows
{
lplvcd-> clrTextBk = RGB(255, 0, 0);
lplvcd-> clrText = RGB(255, 255, 0);
*pResult = CDRF_NEWFONT;
}
}
break;
case CDDS_ITEMPREPAINT|CDDS_SUBITEM: // recd when CDRF_NOTIFYSUBITEMDRAW is returned in
{ // response to CDDS_ITEMPREPAINT.
*pResult = CDRF_DODEFAULT;
int iCol = lplvcd-> iSubItem;
int iRow = lplvcd-> nmcd.dwItemSpec;
if((iRow & 1) && (iCol & 1))
{
lplvcd-> clrTextBk = RGB(255, 0, 0);
lplvcd-> clrText = RGB(255, 255, 0);
*pResult = CDRF_NEWFONT;
}
break;
}
default:// it wasn 't a notification that was interesting to us.
*pResult = CDRF_DODEFAULT;
}
}
// add the following function to the cpp file.
// for specialized cell handling.
// modify to suit.
// (in this sample function, I display the text centered).
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
//for this notification, the structure is actually a
// NMLVCUSTOMDRAW that tells you what 's going on with the custom
// draw action. So, we 'll need to cast the generic pNMHDR pointer.
LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR;
switch(lplvcd-> nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW; // ask for subitem notifications.
break;
case CDDS_ITEMPREPAINT:
*pResult = CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_ITEMPREPAINT|CDDS_SUBITEM:
{
int iCol = lplvcd-> iSubItem;
int iRow = lplvcd-> nmcd.dwItemSpec;
CString sItem = GetItemText(iRow, iCol);
CRect rc;
GetCellRect(iRow, iCol, LVIR_BOUNDS, rc);
// get the device context.
CDC *pDC= CDC::FromHandle(lplvcd-> nmcd.hdc);
// paint the text centered.
pDC-> DrawText(sItem , rc, DT_CENTER);
*pResult= CDRF_SKIPDEFAULT;
break;
}
default:// it wasn 't a notification that was interesting to us.
*pResult = CDRF_DODEFAULT;
}
}
Where GetCellRect is defined as follows:
BOOL CListCtrlEx::GetCellRect(int iRow, int iCol, int nArea, CRect &rect)
{
if(iCol)
return GetSubItemRect(iRow, iCol, nArea, rect);
if(GetColumnCount()== 1)
return GetItemRect(iRow, rect, nArea);
iCol = 1;
CRect rCol1;
if(!GetSubItemRect(iRow, iCol, nArea, rCol1))
return FALSE;
if(!GetItemRect(iRow, rect, nArea))
return FALSE;
rect.right = rCol1.left;
return TRUE;
}