Click here to Skip to main content
Click here to Skip to main content

Custom Drawn Controls using WTL

By , 30 Nov 2000
 

Sample Image - customdrawlist_wtl.jpg

Introduction

Having recently spent sometime working with WTL, I relise their is a distinct lack of documentation. In my my opinion WTL is the best solution so far to wrapping up Win32, and the more documentation we get on the web, the better it'll be for all of us.

This article tackles the approach used to create custom drawn controls using WTL.

I've choosen to use a listView to link up with the article Neat Stuff to do with List Controls using CustomDraw, which demonstrates the same concept using MFC. I choose to keep this simple and would refer you to that document for more examples on what you can do to improve the look and feel of the list view.

MyListControl

The WTL makes creating custom drawn controls very easy. The trick is to know which bits of code you need in your project to make it happen. We begin by defining our own class for a CListViewCtrl, so we can keep all the custom drawing in one place.

class MyListView : public CWindowImpl<MyListView, CListViewCtrl>,
                   public CCustomDraw<MyListView>                   
{
public:

  BEGIN_MSG_MAP(MyListView)    
    CHAIN_MSG_MAP(CCustomDraw<MyListView>)
  END_MSG_MAP()      
}

We have to inherit from CWindowImpl, otherwise we're not going to get any window messages. The most important thing to note here is we also inherit from CCustomDraw. This along with CHAIN_MSG_MAP(CCustomDraw<MyListView>) macro gives us the ability to neatly over ride the process of drawing the CListViewContol.

The CHAIN_MSG_MAP() macro routes the message to the default message map in the base class.

If you take a look inside at the code inside AtlCtrls.h at the class CCustomCtrl<T> you'll see it defines the marco NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw) which handles the custom draw notification. It splits the message into it component part and calls a function for each. These are the functions you can overload to deal with the drawing of your control.

// Overrideables
DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnItemPreErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);
DWORD OnItemPostErase(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/);

We'll override a couple of these functions and change the background fill color every second entry to give a stripped look to the listView, making it easier to read accross the rows.

So in our MyListViewCtrl we define the two overloads

class MyListView : public CWindowImpl<MyListView, CListViewCtrl>,
                   public CCustomDraw<MyListView>                
{
public:

    BEGIN_MSG_MAP(MyListView)    
    CHAIN_MSG_MAP(CCustomDraw<MyListView>)
    END_MSG_MAP()              

    DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
    {        
        return  CDRF_NOTIFYITEMDRAW;
    }

    DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
    {
        NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( lpNMCustomDraw );

        // This is the prepaint stage for an item. Here's where we set the
        // item's text color. Our return value will tell Windows to draw the
        // item itself, but it will use the new color we set here for the background

        COLORREF crText;

        if ( (pLVCD->nmcd.dwItemSpec % 2) == 0 )
            crText = RGB(200,200,255);
        else 
            crText = RGB(255,255,255);        

        // Store the color back in the NMLVCUSTOMDRAW struct.
        pLVCD->clrTextBk = crText;

        // Tell Windows to paint the control itself.
        return CDRF_DODEFAULT;
    }
};

Creating the Control

Most examples you see in MSDN and in other articles assume the control is created as a resource and linked into you code. To be different I'm going to create this list box directly onto the main window.

Below is all the code we're going to add the wizard generated CMainFrame to create our custom drawn control. Look out for the CHAIN_MSG_MAP_MEMBER(m_listView) macro. Miss it out and your class won't get the NM_CUSTOMDRAW message. This macro routes the message to the default message map in the data member, which is what we want to do.

As a side note, we could have handled the NM_CUSTOMDRAW message at this level. Checked to see what the control id was and process the painting as appropriate. This maybe useful in some cases, but its much neater to do what we have done. Keeping our code clean and clear.

class CMainFrame : public CFrameWindowImpl<CMainFrame>, 
        public CUpdateUI<CMainFrame>,
        public CMessageFilter, public CIdleHandler
{
public:

    // ...
    // code left out for readability
    // ...

    BEGIN_MSG_MAP(CMainFrame)
        // ...
        // code left out for readability
        // ...
        CHAIN_MSG_MAP_MEMBER(m_listView)
    END_MSG_MAP()
    
    LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/,
                     LPARAM /*lParam*/, BOOL& /*bHandled*/)
    {
        // code left out for readability

        // create a list box    
        RECT r = {0,0,182,80};    
        m_listView.Create(m_hWnd,r,CListViewCtrl::GetWndClassName(),
                          WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | 
                          WS_CLIPCHILDREN | LVS_REPORT, WS_EX_CLIENTEDGE);
  
        // fill in the headers
        m_listView.AddColumn(_T("Symbol"), 0);
        m_listView.AddColumn(_T("Company     "), 1);
        m_listView.AddColumn(_T("Price     "), 2);            

        m_listView.AddItem(0,0,"VOD");
        m_listView.AddItem(0,1,"Vodaphone Airtouch");
        m_listView.AddItem(0,2,"252.25");

        m_listView.AddItem(1,0,"LAT");
        m_listView.AddItem(1,1,"Lattice Group");
        m_listView.AddItem(1,2,"149.9");

        m_listView.AddItem(2,0,"CLA");
        m_listView.AddItem(2,1,"Claims Direct");
        m_listView.AddItem(2,2,"132.50");   

        return 0;
    }

    
    // ...
    // code left out for readability
    // ...    
        
public :

  MyListView m_listView;

};

In the OnCreate() we just call create on the listView data member and fill in some demo data. Mission complete ...

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

About the Author

Billy Leverington
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionPlease Help , I can not Run Program in 2005 c++.Netmemberjack_ttn11 Sep '07 - 17:21 
GeneralOwner draw toolbar buttonsmemberjoebarthib15 May '06 - 21:49 
GeneralForgot overridable OnSubItemPrePaintmemberMartijn26 Aug '03 - 9:33 
Generalone questionmemberxzhoumin13 Mar '03 - 3:09 
GeneralWon`t work properly when compiled by VS7 EA.memberIlushka5 Feb '03 - 4:09 
GeneralRe: Won`t work properly when compiled by VS7 EA.memberOlan Patrick Barnes6 Feb '03 - 8:57 
GeneralRe: Won`t work properly when compiled by VS7 EA.sussMatt Spaulding11 Feb '03 - 18:33 
GeneralRe: Won`t work properly when compiled by VS7 EA.memberm3rlinez22 Sep '09 - 21:22 
GeneralRe: Won`t work properly when compiled by VS7 EA.memberTcherepanov Serguei28 Apr '06 - 22:43 
GeneralAnother alternative to CHAIN_MSG_MAP_MEMBERmemberBjornar Henden22 Jan '03 - 0:49 
GeneralRe: Another alternative to CHAIN_MSG_MAP_MEMBERsussMatt Spaulding11 Feb '03 - 18:10 
GeneralAlternative to CHAIN_MSG_MAP_MEMBER()sussMatt Spaulding21 Nov '02 - 15:12 
GeneralRe: Alternative to CHAIN_MSG_MAP_MEMBER()sussMatt Spaulding21 Nov '02 - 15:22 
GeneralRe: Alternative to CHAIN_MSG_MAP_MEMBER()memberKonstantin Izm11 Aug '09 - 22:12 
GeneralThanksmemberJon Taylor6 Sep '02 - 20:13 
GeneralCustom Drawing Sub ItemsmemberChris Stiefeling8 Jun '01 - 11:41 
General[Message Deleted]memberMagnusH22 Dec '00 - 1:07 
GeneralRe: Unwanted customdrawmemberAnonymous27 Feb '01 - 14:35 
You should check if you get notification for proper control.
 
DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
{
// check, if notification for this control
if (*this != lpNMCustomDraw->hdr.hwndFrom)
return CDRF_DODEFAULT;
 
NMLVCUSTOMDRAW* pLVCD = reinterpret_cast( lpNMCustomDraw );
...
 

Leonid Tochinski
tochinski@home.com
GeneralRe: Unwanted customdraw---correctionmemberAnonymous16 Mar '01 - 4:39 

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 1 Dec 2000
Article Copyright 2000 by Billy Leverington
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid