100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C++

Custom Drawing and Owner-Draw

How MFC lets you take over rendering of standard controls — buttons, list boxes, combo boxes, list controls — through the owner-draw mechanism and custom-draw notifications.

GraphicsIntermediate10 min readJul 10, 2026
Analogies

Why Standard Controls Need Owner-Draw

Standard Windows controls (buttons, list boxes, combo boxes, list controls) render themselves using default system visuals, which is fine until you need per-item icons, colored status indicators, variable-height rows, or a custom-styled button. Owner-draw is the mechanism by which the control delegates its own painting back to the parent window (the 'owner'): you set the control's style to include owner-draw (e.g., BS_OWNERDRAW for a button, LBS_OWNERDRAWVARIABLE for a list box) and MFC then routes drawing requests to virtual functions like CButton::DrawItem or CListBox::DrawItem that you override.

🏏

Cricket analogy: Like a franchise handing full creative control of the pre-match light show to an external production company instead of using the stadium's default announcement system, owner-draw hands rendering control from the default control to your own DrawItem override.

Implementing DrawItem for a List Box

When a list box is styled LBS_OWNERDRAWFIXED or LBS_OWNERDRAWVARIABLE, Windows sends WM_DRAWITEM for each visible item, which MFC forwards to CListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct). The structure carries the item's index, its rectangle (rcItem), and its state flags (ODS_SELECTED, ODS_FOCUS) so you can, for instance, fill a different background color for a selected row before drawing the item's text or icon on top — and for LBS_OWNERDRAWVARIABLE you must also override MeasureItem to report each item's height since rows are no longer uniform.

🏏

Cricket analogy: Like the broadcast graphics team checking whether a batter is 'on strike' (a state flag) before highlighting their name differently on the scorecard, DrawItem checks ODS_SELECTED to decide whether to highlight that row's background.

NM_CUSTOMDRAW for Common Controls

Modern common controls like CListCtrl and CTreeCtrl support a finer-grained mechanism than classic owner-draw: custom draw, delivered via WM_NOTIFY with an NM_CUSTOMDRAW structure across multiple drawing stages (CDDS_PREPAINT, CDDS_ITEMPREPAINT, CDDS_ITEMPOSTPAINT). You return a combination of CDRF_NOTIFYITEMDRAW and similar flags to tell the control which stages you want to intercept, letting you, for example, change only a subitem's text color while leaving the control to draw the rest of its chrome (gridlines, selection rectangle, focus rectangle) itself — a much lighter-weight approach than fully replacing DrawItem.

🏏

Cricket analogy: Like a third umpire only reviewing the specific moment of a run-out at the crease rather than re-officiating the entire over, custom draw lets you intercept just the CDDS_ITEMPREPAINT stage rather than repainting the whole control.

cpp
void CMyListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
    CRect rc(lpDIS->rcItem);

    BOOL bSelected = (lpDIS->itemState & ODS_SELECTED) != 0;
    pDC->FillSolidRect(&rc, bSelected ? RGB(0, 120, 215) : RGB(255, 255, 255));

    CString strText;
    GetText(lpDIS->itemID, strText);

    pDC->SetTextColor(bSelected ? RGB(255, 255, 255) : RGB(0, 0, 0));
    pDC->SetBkMode(TRANSPARENT);
    pDC->DrawText(strText, &rc, DT_SINGLELINE | DT_VCENTER | DT_LEFT);

    if (lpDIS->itemState & ODS_FOCUS)
        pDC->DrawFocusRect(&rc);
}

For simple cases like custom text color or a custom background per row in a CListCtrl, prefer NM_CUSTOMDRAW over full owner-draw: it lets the control keep handling gridlines, headers, and selection rectangles for you, which means less code and fewer visual inconsistencies compared to a full DrawItem override where you're responsible for every pixel.

  • Owner-draw hands rendering responsibility for a control from its default painting to a DrawItem override you write.
  • Buttons use BS_OWNERDRAW; list boxes use LBS_OWNERDRAWFIXED or LBS_OWNERDRAWVARIABLE.
  • LPDRAWITEMSTRUCT provides the item's rectangle, index, and state flags like ODS_SELECTED and ODS_FOCUS.
  • LBS_OWNERDRAWVARIABLE requires also overriding MeasureItem to report per-item heights.
  • Modern common controls support NM_CUSTOMDRAW via WM_NOTIFY, offering staged, lighter-weight paint interception.
  • NM_CUSTOMDRAW return flags like CDRF_NOTIFYITEMDRAW control which paint stages get delivered next.
  • Prefer custom draw over full owner-draw when you only need to tweak specific visual details, not replace all rendering.

Practice what you learned

Was this page helpful?

Topics covered

#MFCMicrosoftFoundationClassesStudyNotes#MicrosoftTechnologies#CustomDrawingAndOwnerDraw#Custom#Drawing#Owner#Draw#StudyNotes#SkillVeris#ExamPrep