Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Owner Drawn ListViews In WTL

0.00/5 (No votes)
26 Jun 2004 2  
An overview of handling owner drawn listview controls in WTL.

Introduction

Owner drawing controls is a nice skill to have. Easy to master with immediate impressive results. Tuck it away carefully because if used correctly, it can make your applications look more professional without crossing over to look like you are just trying too hard and getting it all wrong. By learning to use it, you'll wield the broad sword that Microsoft Word has, but Notepad doesn't. Let's continue now.

This article is to get you to understand the basics quickly.

Background

An owner drawn listview is a normal report view list view that also generates 4 owner draw messages. It expects you to do the drawing.

  • WM_DRAWITEM (Call for each item when it needs to be drawn)
  • WM_MEASUREITEM (Requests the height of the row, called when resized)
  • WM_COMPAREITEM (Used for sorting)
  • WM_DELETEITEM (Called for each item when it gets deleted)

Using the code

The breakdown:

Using the WTL wizard, I created a dialog based project, nothing special. I added a new class derived from COwnerDrawnListViewCtrl.

class CDemoOwnerDrawnListViewCtrl : 
    public COwnerDrawnListViewCtrl<CDemoOwnerDrawnListViewCtrl>
{
public:
    BEGIN_MSG_MAP(CDemoOwnerDrawnListViewCtrl)
        CHAIN_MSG_MAP(COwnerDrawnListViewCtrl<CDemoOwnerDrawnListViewCtrl>)
    END_MSG_MAP()

    void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
};

For DrawItem, the dc you do your drawing onto is in lpDrawItemStruct->hDC, and the rect for the current item is in lpDrawItemStruct->rcItem.

For MeasureItem, all you need to do is set lpMeasureItemStruct->itemHeight to the height you need.

Your control is done.

All the lines that I added to the Dialog class have "// ADDED" appended to them so you can do a Find in Files. The basics for using your control are:

  • Create your COwnerDrawnListViewCtrl derived control by subclassing a ListView control with the "Owner Drawn Fixed" property to true. Call ForceMeasureItemMessage() if you are going to be using WM_MEASUREITEM (see below).
    m_lvwDemoListViewCtrl.SubclassWindow(GetDlgItem(IDC_LIST1));
    m_lvwDemoListViewCtrl.ForceMeasureItemMessage();
  • Make sure you add some columns (AddColumn()).
  • The dialog needs to reflect notify messages back down to the control. You handle this by adding REFLECT_NOTIFICATIONS() inside the message map of the parent dialog.

You should be good now.

Details

COwnerDrawnTaskListView is a very simple class with only 3 functions, which itself derives from CWindowImpl and COwnerDraw.

void ForceMeasureItemMessage();
void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/) {};
void GetCellRect(int header_column, const CRect& item_rect, CRect& cell_rect);

Inside COwnerDrawnTaskListView, we CHAIN_MSG_MAP_ALT(COwnerDraw<TBase>, 1) because we are handling the reflected messages (OCM_XXXX). If we were the parent then we would just CHAIN_MSG_MAP(COwnerDraw<TBase>).

ForceMeasureItemMessage() is required to receive a WM_MEASUREITEM message. The problem is that you can't subclass the window quick enough to receive it initially because it's sent before it's created. The only way you can catch it is if you .Create() or .CreateEx() it. In that case, you don't need ForceMeasureItemMessage(). ForceMeasureItemMessage()'s magic is moving the control up a little which generates a WM_MEASUREITEM message. You get another one when ForceMeasureItemMessage() moves it back.

The empty DeleteItem() function is required to fix an ambiguous access issue that happens with WTL. What happens, if you don't define it, is that OnDeleteItem() will want to call DeleteItem() which is ambiguous. You got WTL::CListViewCtrlT<ATL::CWindow>::DeleteItem() and WTL::COwnerDraw<T>::DeleteItem(). Since they are in different MI branches, they are out of each other's overloading scope. Our empty DeleteItem() function (which you can hide with your function down the hierarchy) hides both functions resolving the ambiguity.

GetCellRect() is a function that returns a CRect of the "cell" or the subitem's coordinates within a row which simplifies the drawing.

History

Sunday, June 27, 2004 - Created.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here