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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read