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.