Click here to Skip to main content
15,880,469 members
Articles / Desktop Programming / WTL
Article

Owner Drawn ListViews In WTL

Rate me:
Please Sign up or sign in to vote.
4.65/5 (13 votes)
26 Jun 20043 min read 71.5K   4.4K   34   5
An overview of handling owner drawn listview controls in WTL.

Image 1

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
MCGRADYV57-Apr-13 2:54
MCGRADYV57-Apr-13 2:54 
QuestionVertical scrolling? Pin
Artz2-Jul-09 0:22
Artz2-Jul-09 0:22 
GeneralTwo more steps to this being a 5 out of 5. Pin
aeomer10-Feb-07 0:22
aeomer10-Feb-07 0:22 
QuestionCapturing events of list Pin
CodeFundu17-Mar-06 21:44
CodeFundu17-Mar-06 21:44 
I have implemented a owner drwn list in my dialog i want to enable/disable a button based on the item selection in the list, but the problem is i am not able to capture the events of the list box. i am using the

NOTIFY_HANDLER(IDC_LIST, LVN_ITEMCHANGED, OnListSelectionChange)

message handler but the handle is not called. Can you help me out with this.
Thanks in Advance..
AnswerRe: Capturing events of list Pin
mark novak18-Mar-06 1:59
mark novak18-Mar-06 1:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.