In Depth Banner
Skip Navigation Links

Full Custom Listview Drawing.

In the last article you saw how ListView enabled detail view customization through the LVS_OWNERDRAWFIXED style and the DRAWITEMSTRUCT. While it's possible to create a cool and distinctive look using this method it has certain disadvantages. First, it only works in detail view so if you want a custom look for your large-icon view or the list view you're out of luck. Secondly, the control only tells you about the item to be drawn. When, how or if you draw the sub-items is entirely up to you.

In addition to the method you saw previously, ListView has the ability to give you total control over the draw process so that you can control the customization of all the views, items and subitems in the list. This method takes advantage of the NM_CUSTOMDRAW notification message and works in the following manner.

Before the control is drawn a message is sent that asks if you want to perform custom drawing. If you do then as each item is required, a message will be sent to you asking you to draw the item. If the control is in Large-Icon, Small-Icon or List mode, the process will end there and the cycle will be repeated at each paint message. In Detail view an added level of nesting is provided that allows you to decide whether you want to draw all the sub-items individually. If you opt to do so then the control send a longer stream of messages which you trap to paint the sub-items.

At any stage you can get the control to draw the default look for any particular item or sub-item so you can even do partial customization of the view.

The system works through the reflected WM_NOTIFY message. When the combination of WM_REFLECT and WM_NOTIFY is received, you can check to see what the notification was about by checking the NMHDR structure passed in the message's LParam member. If the message is NM_CUSTOMDRAW you should return one of several codes that tell the control whether to continue or stop the process. Incidentally, when WM_NOTIFY is received on its own with an NM_CUSTOMDRAW you can do custom drawing on the ListView headers.

The goal of this article and code is to create a ListView control that untangles this low-level messages and provides a set of .NET delegates and events to enable simple and complete customization of the ListView control.

Trap the messages.

As in the previous example this is accomplished in the WndProc override. There is no need however to set a special style and the messages are sent out all the time for all view modes. This listing shows the simple WndProc.

Once a custom draw notification arrives the control needs to go through the following steps according to the current drawing stage.

DrawStage

Action

CDDS_PREPAINT

Decide whether custom drawing is to be done. Return CDRF_NOTIFYITEMDRAW or CDRF_NOTIFYSUBITEMDRAW if it is. Otherwise return CDRF_DODEFAULT.

If you return one of the former codes, you will receive notifications with the DrawStage set to one of the other values in this table.

CDDS_ITEMPREPAINT

When in Detail mode, you can choose to draw the whole item completely in which case you return CDRF_SKIPDEFAULT. Alternatively, you can return CDRF_NOTIFYSUBITEMREDRAW and your code will be sent a new notification for each sub-item including sub-item 0 which is the main item.

If the view setting is LargeIcon, SmallIcon or List you simply draw the item according to the custom style you chose and return CDRF_SKIPDEFAULT.

The combination of CDDS_SUBITEM and CDDS_ITEMPREPAINT

This is received whenever a sub-item is drawn in detail mode. You will draw the item or subitem in the rectangle provided.

Return CDRF_SKIPDEFAULT.

Note that at any stage of custom drawing you can return CDRF_DODEFAULT to have the ListView control paint with the standard appearance. This is useful if you only want to custom draw a few items or subitems and have the rest appear as they normally would.

This is managed in the example code by the ProcessNotify method

Making the event.

ProcessNotify turns the cryptic system of notification messages into .NET style events and provides two specialized events with their own event arguments that enable you to control the custom draw process directly from standard Windows Forms code.  

The first event, QueryCustomDraw, is fired once in each draw cycle and enables you to specify whether you want to continue to receive custom draw events. The event arguments, QueryCustomDrawEventArgs, contains the current view style setting and has a Boolean member but you can set to true if you wish to continue custom drawing. 

The second event, PaintItem, Is fired once for each item or sub-item drawn.  This event is accompanied by the ListViewItemPaintEventArgs that contains information about the item being drawn and enables you to obtain all the information you need in order to do your drawing.

This listing shows the two event arguments provided for the demo.

Using the control.

Because CustomListViewEx generates the events it's up to an application that services those events to do the drawing. As you read earlier, the application can choose to draw in any draw mode so you have to be aware of the different constraints placed upon the graphics by the layout of the ListView. Custom drawing is, by definition not something that can be specified exactly so, rather than implement a complex application the demonstration application provides a few simple examples of drawing in various view modes.

 

   

In small-icon mode the custom drawing simply alters the color and weight of the text of the selected items. In large icon mode the selected icon is given an orange border and a similar text treatment. In detail mode the colors of the individual items and text are modified. As you can see, in-place label editing continues to work. This last point was the reason for the ItemOffset in the ListViewItemPaintEventArgs because the ListView base simply plonks the edit window where it thinks the text should appear so if the item text isn't actually in that place the editing looks bad.

The event handlers from the test application are shown in this listing.

Summary

This ends the custom-drawing fest. By now you will understand how to extend the capabilities of the ListView common control in a Windows Forms context.

 

You can find the Source Code files here.

Return to the main menu.

Copyright © Bob Powell 2003-2009. All rights reserved