Microsoft & .NET Visual C# Custom Draw items inside a ListView Control

Custom Draw items inside a ListView Control


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;
}

History

Date Posted: June 19, 1999

Latest Posts

Related Stories