Owner Drawing

Owner drawing allow to customize appearance of any element, element part and the control itself:

Drawing Over Control Parts

The simple way to draw over control parts is by using drawing events:

Each of these events contains System.Drawing.Graphics object in event data with which custom drawing is possible. Areas of drawing are also provided. For example, to draw inside item inner area, there is a BetterListViewDrawItemEventArgs.ItemBounds.BoundsInner property containing the rectangle.



If you want to paint outside element areas, set OptimizedInvalidation property to false. This will ensure your custom drawing code will be called in every redraw cycle.



Replacing Default Drawing by Custom Drawing

Owner drawing events are always called after the default drawing, so it is possible only to draw over exisiting drawing. When you need turn off some painting and do your own drawing instead of the default one (e.g. draw rotated text instead the straight one), you have to create your custom control inheriting from BetterListView:

C#

class OwnerDrawBetterListView : BetterListView
{
    // ...
}

Visual Basic

Class OwnerDrawBetterListView Inherits BetterListView
    ' ...
End Class

Then you can override one of the drawing methods:

This gives you more control over the painting, because your drawing code can be called before or after the default drawing, depending on where and if you call base implementation.

Every part of the default drawing has a switch so you can turn the default drawing off. For example, if you want not to draw default text on some item, set BetterListViewDrawItemEventArgs.DrawText property to false.

It is also possible to do custom drawing as the very last drawing of the whole control. To do this, override DrawingRedrawCore method and do your drawing after calling the base implementation:

C#

protected override void DrawingRedrawCore(Graphics grfx)
{
    base.DrawingRedrawCore(grfx);

    // do your custom drawing
}

Visual Basic

Protected Overrides Sub DrawingRedrawCore(grfx As Graphics)

    MyBase.DrawingRedrawCore(grfx)

    ' do your custom drawing
    
End Sub

Overriding Item and Control States

The appearance of element depends not only on its state, but also on the control state.

If you override one of the drawing methods (e.g. OnDrawItem), you can modify event data before calling base class implementation (e.g. base.OnDrawItem).

For example, BetterListViewDrawItemEventArgs contains ItemStateInfo property. By modifying this property, you can force drawing item in any state you wish.

BetterListViewDrawItemEventArgs also contains two properties regarding control state:

By default, these properties correspond to actual control's state, but they can be modified. For example, one may want to set DrawFocused to true on every item that is selected, so the item will be highlighted even if the control loses focus.

The control state properties are available only in the BetterListViewItemEventArgs, but element states can be modified in all painting event handlers (also column headers and groups).

Sample Source Code

The following sample shows owner drawing of item background:

C#

this.listView.BeginUpdate();

this.listView.Items.Add("Item with owner-drawn image and background.");

this.listView.View = BetterListViewView.Tile;
// turn off automatic image sizing to make space for image even when items do not have any images set
this.listView.LayoutOptions = (BetterListViewLayoutOptions.Auto & ~BetterListViewLayoutOptions.AutoSizeItemImage);
// set 4-pixel boundary around image
this.listView.LayoutItemsCurrent.ImagePadding = new Padding(4);
// set image size to be 50 by 50 pixels (it is possible to set image sizes for sub-items as well by adding more Size instances in the collection)
this.listView.LayoutItemsCurrent.ImageSizes = new ReadOnlyCollection<Size>(new[] { new Size(50, 50) });

this.listView.EndUpdate();

// we would like to draw over item's foreground (custom image)
this.listView.DrawItem += ListViewDrawItem;
// we would like to draw over item's background
this.listView.DrawItemBackground += ListViewDrawItemBackground;

Visual Basic

ListView.BeginUpdate()

ListView.Items.Add("Item with owner-drawn image and background.")

ListView.View = BetterListViewView.Tile
' turn off automatic image sizing to make space for image even when items do not have any images set
ListView.LayoutOptions = (BetterListViewLayoutOptions.Auto And Not BetterListViewLayoutOptions.AutoSizeItemImage)
' set 4-pixel boundary around image
ListView.LayoutItemsCurrent.ImagePadding = New Padding(4)
' set image size to be 50 by 50 pixels (it is possible to set image sizes for sub-items as well by adding more Size instances in the collection)
ListView.LayoutItemsCurrent.ImageSizes = New ReadOnlyCollection(Of Size)(New Size() {New Size(50, 50)})

ListView.EndUpdate()

' we would like to draw over item's foreground (custom image)
AddHandler ListView.DrawItem, AddressOf ListViewDrawItem
' we would like to draw over item's background
AddHandler ListView.DrawItemBackground, AddressOf ListViewDrawItemBackground

DrawItem event handler draws on the item image area:

C#

void ListViewDrawItem(object sender, BetterListViewDrawItemEventArgs eventArgs)
{
    eventArgs.Graphics.SmoothingMode = SmoothingMode.HighQuality;

    Pen pen = new Pen(Color.BlueViolet, 2.5f);

    // draw ellipse in the image area
    eventArgs.Graphics.DrawEllipse(
        pen,
        eventArgs.ItemBounds.SubItemBounds[0].BoundsImage);

    pen.Dispose();
}

Visual Basic

Sub ListViewDrawItem(ByVal sender As Object, ByVal eventArgs As BetterListViewDrawItemEventArgs)

    eventArgs.Graphics.SmoothingMode = SmoothingMode.HighQuality

    Dim pen As New Pen(Color.BlueViolet, 2.5F)

    ' draw ellipse in the image area
    eventArgs.Graphics.DrawEllipse(pen, eventArgs.ItemBounds.SubItemBounds(0).BoundsImage)

    pen.Dispose()

End Sub

DrawItemBackground event handler draws on the item background area:

C#

void ListViewDrawItemBackground(object sender, BetterListViewDrawItemBackgroundEventArgs eventArgs)
{
    Brush brush = new LinearGradientBrush(
        eventArgs.ItemBounds.BoundsInner,
        Color.FromArgb(64, Color.DarkSeaGreen),
        Color.Transparent,
        LinearGradientMode.ForwardDiagonal);

    // draw over the item's background in the inner area
    eventArgs.Graphics.FillRectangle(brush, eventArgs.ItemBounds.BoundsInner);

    brush.Dispose();
}

Visual Basic

Sub ListViewDrawItemBackground(ByVal sender As Object, ByVal eventArgs As BetterListViewDrawItemBackgroundEventArgs)

    Dim brush As Brush = New LinearGradientBrush(
        eventArgs.ItemBounds.BoundsInner,
        Color.FromArgb(64, Color.DarkSeaGreen),
        Color.Transparent,
        LinearGradientMode.ForwardDiagonal)

    ' draw over the item's background in the inner area
    eventArgs.Graphics.FillRectangle(brush, eventArgs.ItemBounds.BoundsInner)

    brush.Dispose()

End Sub