In Depth Banner
Skip Navigation Links

ListView customization Part 1.

Customizing the detail view

When I originally embarked upon this article I imagined that the process of creating owner-drawn ListView derivatives in Windows Forms would be quite cut and dried. As the great Rabbie Burns once said "The best laid plans of mice and men often go awry"

In their effort to promote the cause of re-usable componentry, Microsoft have created the ListView Windows Forms control as a fairly loose wrapper around the ListView common control. I say fairly loose because any comprehensive implementation would have taken the custom draw abilities of the control into account and provided events that could be responded to in order to draw any item, sub-item or header. Instead we have a partial implementation of an otherwise perfectly competent control.

While researching this article I revisited many of the MFC and even C based code samples but found very little pertaining to a direct .NET implementation except an article that dealt more with the business of sorting columns than custom drawing. I did rediscover however that there are two distinct approaches to ListView customization and that they are both complicated enough to warrant an article each so I decided to make this issue a bumper ListView Customization-fest. This, the first of two articles, deals with the simplest form of ListView customization, Detail View customization using the WM_DRAWITEM message.

What's it good for?

Although this method of customization is limited to only one style of display, the detail view, the advantage over the default drawing system is that you can draw the items in the list using whatever scheme you like. This is ideal for adding graphics or highlighting and selection styles to the view. The method is easy to use because most of what you need to know is passed to you in a DRAWITEMSTRUCT structure.

The solution requires considerable interop with Win32 structures, methods and messages so there is an extensive collection of enumerations and marshalling structures that accompany the examples.

Enabling custom drawing.

The first thing to do in this customization method is to enable the drawing of detail items by adding a style to the window's creation parameters. This is accomplished by overriding the CreateParams property and inserting some information into the Style property.

This listing illustrates this process.

Trapping messages

Once the CreateParams override is in place the ListView class will begin sending reflected WM_DRAWITEM messages to the WndProc of the derived class. In order to trap them you have to override WndProc. Reflected messages are sent using a combination of the WM_REFLECT message and some other message, in this case WM_DRAWITEM. To detect them you have to be aware that the message being received is in fact two flags that have been added together using a bitwise OR.

To make the drawing simpler we can force the last column header in the list of columns to auto-size to fill the control. This is accomplished by catching one of header notification messages and then setting the width of the final column to -2. This ensures that the selection row always fills the control from left to right.

This listing shows the override of the WndProc method.

Decoding the message.

The ListView notifies us that an item needs to be drawn by passing parameters in the message. In this case, we're handed an unmanaged pointer to a DRAWITEMSTRUCT in the LParam of the message. To decode and use this we need a .NET style structure that mimics the size and placement of the one passed to us and a method of copying the data out of the unmanaged data. This is accomplished in this listing.

With the structures in place, we can go ahead and decode the data so that the drawing can be completed. The DRAWITEMSTRUCT informs the code of which item is being drawn in the itemID member and provides an hDC on which to draw in the hDC member. It also provides a rectangle for the item to be drawn into in the rcItem member, this is converted in to a System.DrawingRectangle and the rest of the custom drawing progresses from there.

Drawing the items.

There are a few things to consider when drawing an item. First, the control may have a small image list that provides icons for each item. Then, the control might have check-boxes enabled and there may be more than one header with the header dividers in arbitrary positions. It's also important to ensure that the item name is drawn in the correct place because in-place label editing pops up a window on top of the item name. The ListView control assumes that the name will be drawn in a specific place so it's important to ensure that it is regardless of the combination of image-list or checkbox presence. Lastly, the user may have scrolled the control so we need to know and take account of the horizontal scroll position.

The customized style of drawing makes the control look like a repeater with alternating coloured rows. You can select the fore and back colours for odd and even rows and also for the selection. The repeater function can be turned off also. Figure 1 shows the control in action.

Figure 1. The custom list view.

The drawing of the list view items is handled by this method.

As usual, the full code for the control is in the code page. Read on in the next article now to discover how to totally control the drawing process down to the sub-item level using full custom drawing.

You can find the Source Code files here.

Return to the main index.

Copyright © Bob Powell 2003-2009. All rights reserved