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.