I have a ListView where I wish to tweak the drawing of items (for example highlighting certain strings in list view itmes), however I don't want to radically alter the way that items are displayed.
I have set the OwnerDraw to true and can get my head around how to draw my highlighting effect, however whenever I try to defer to the default implementation to draw the rest of the list view item things go wrong and I'm left with a whole load of graphical problems indicating that actually I've completely gone wrong.
Is there somewhere that I can see what the "Default" handlers for the DrawItem
and DrawSubItem
events do so that I can better my understanding and more easily tweak my code?
For reference here is a snippet showing what I'm currently doing:
public MyListView()
{
this.OwnerDraw = true;
this.DoubleBuffered = true;
this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader);
this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem);
this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem);
}
private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Not interested in changing the way columns are drawn - this works fine
e.DrawDefault = true;
}
private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
}
private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0)
{
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
}
e.DrawText();
}
I haven't got the time now to write up a complete answer so instead I'll put down some quick notes and come back to it later.
As LarsTech said, owner drawing a ListView
control is a pain - the .Net ListView
class is a wrapper around the underlying Win32 List View Control and the ability to "Owner draw" is provided by the NM_CUSTOMDRAW
notification code. As such there is no "default .Net implementation" - the default is to use the underlying Win32 control.
To make life even more difficult there are a number of extra considerations to make:
DrawItem
and DrawSubItem
you may well be drawing the contents of the first cell twice.DrawItem
event will occur without corresponding DrawSubItem
events, meaning that if you draw a background in the DrawItem
event and then draw the text in the DrawSubItem
event your item text will disappear when you mouse over.ItemState
property is not always correct, for example just after resizing a column. Consequently I've found its best not to rely on it.DrawItem
event occurs first, anything you draw in the DrawItem
handler (e.g. the selection effect) may well be overlayed by things you do in the DrawSubItem
handler (e.g. having certain cells with a different background color).All in all handling owner drawing is a fairly involved affair - I found it best to handle all drawing inside the DrawSubItem
event, its also best to perform your own double-buffering by using the BufferedGraphics
class.
I also found looking at the source code for ObjectListView very handy.
Finally, all of this is just to handle the details mode of the list view (the only mode I am using), if you want the other modes to work too then I believe that there are extra things to take account of.
When I get a chance I'll try and post my working example code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With