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



Comments

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Thanks to wide spread cloud hosting and innovations small businesses can meet and exceed the legacy systems of goliath corporations. Explore the freedom to work how you want, with a phone system that will adapt to your evolving needs and actually save you lots of expense—read Get an Enterprise Phone System without High Cost and Complexity. The article clearly illustrates: The only hardware you'll need is phone equipment for advanced voice and fax. How to join all your employees, mobile devices, …

  • Cloud computing isn't new, but it's still a hot topic in technology circles. Moving to the cloud has done great things for many businesses. When it comes to handling a basic business necessity, cloud computing has brought many improvements to overall business continuity. Using the cloud for DR makes a lot of sense for many, but it's not exactly the most simple concept. Read this eBook to find answers to all the key questions IT professionals have about cloud-based disaster recovery, and helpful info around the …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date