Owner-drawing a ListView control is more complicated than a ListBox control: many more details to take care of.
This is an example that considers four View settings of a ListView:
View.Details
, View.List
, View.Tile
and View.SmallIcon
.
Only the Text is drawn here (that's why View.LargeIcon
is not included), to contain the code to a decent limit.
An example of drawing the Bitmaps included in a ImageList linked to the ListView is here.
Set up the ListView:
Enable your ListView OwnerDraw
mode, then subscribe to its DrawItem, DrawSubItem and DrawColumnHeader events as shown in the sample code (mandatory, if you want the ListView to show anything).
The Headers are painted using the default rendering (setting e.DrawDefault = true
).
Description of common operations:
The Item Text is drawn using TextRenderer.DrawText: this is the original method used by the ListView to draw its items. It allows to match exactly the default rendering, so we won't notice some mis-alignment of the text.
The DrawItem
event is used to draw a custom background in all View
modes and will draw the Items' text in all mode except View.Details, where the DrawSubItems
event comes into play: we would draw the first Item's text twice, should the DrawItem
event perform the same task.
The DrawSubItems
event is not called when the View
is set to Tile
or List
.
Details on the code presented here:
A helper method, GetTextAlignment
, takes care of setting the Items' alignment, since each Column can have a specific text alignment.
A field, Color listViewSelectionColor
, is used to set/change the Color of the select Items. To modify the selection color, set this filed to any value and Invalidate()
the ListView: the new Color will be applied immediately.
Sample of the results:
bool lvEditMode = false;
Color listViewSelectionColor = Color.Orange;
protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
var lView = sender as ListView;
if (lvEditMode || lView.View == View.Details) return;
TextFormatFlags flags = GetTextAlignment(lView, 0);
Color itemColor = e.Item.ForeColor;
if (e.Item.Selected) {
using (var bkBrush = new SolidBrush(listViewSelectionColor)) {
e.Graphics.FillRectangle(bkBrush, e.Bounds);
}
itemColor = e.Item.BackColor;
}
else {
e.DrawBackground();
}
TextRenderer.DrawText(e.Graphics, e.Item.Text, e.Item.Font, e.Bounds, itemColor, flags);
if (lView.View == View.Tile && e.Item.SubItems.Count > 1) {
var subItem = e.Item.SubItems[1];
flags = GetTextAlignment(lView, 1);
TextRenderer.DrawText(e.Graphics, subItem.Text, subItem.Font, e.Bounds, SystemColors.GrayText, flags);
}
}
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
var lView = sender as ListView;
TextFormatFlags flags = GetTextAlignment(lView, e.ColumnIndex);
Color itemColor = e.Item.ForeColor;
if (e.Item.Selected && !lvEditMode) {
if (e.ColumnIndex == 0 || lView.FullRowSelect) {
using (var bkgrBrush = new SolidBrush(listViewSelectionColor)) {
e.Graphics.FillRectangle(bkgrBrush, e.Bounds);
}
itemColor = e.Item.BackColor;
}
}
else {
e.DrawBackground();
}
TextRenderer.DrawText(e.Graphics, e.SubItem.Text, e.SubItem.Font, e.Bounds, itemColor, flags);
}
protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
=> e.DrawDefault = true;
private TextFormatFlags GetTextAlignment(ListView lstView, int colIndex)
{
TextFormatFlags flags = (lstView.View == View.Tile)
? (colIndex == 0) ? TextFormatFlags.Default : TextFormatFlags.Bottom
: TextFormatFlags.VerticalCenter;
if (lstView.View == View.Details) flags |= TextFormatFlags.LeftAndRightPadding;
if (lstView.Columns[colIndex].TextAlign != HorizontalAlignment.Left) {
flags |= (TextFormatFlags)((int)lstView.Columns[colIndex].TextAlign ^ 3);
}
return flags;
}
private void listView1_BeforeLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = true;
private void listView1_AfterLabelEdit(object sender, LabelEditEventArgs e) => lvEditMode = false;