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