Introduction
Microsoft .NET forms controls like TreeView
and Listview
are just wrappers around the controls in ComCtl. As such, they do not normally invoke the Paint
event. The only suggestion I have seen posted is to set the ControlStyles.UserPaint
style and do all the drawing yourself!
TreeViewWithPaint Control
To solve this problem, an internal Graphics
object based on a Bitmap
was used. It is re-created during any Resize
.
protected override void OnResize( System.EventArgs e ) {
if( internalBitmap == null ||
internalBitmap.Width != Width || internalBitmap.Height != Height ) {
if( Width != 0 && Height != 0 ) {
DisposeInternal();
internalBitmap = new Bitmap( Width, Height );
internalGraphics = Graphics.FromImage( internalBitmap );
}
}
}
When the control receives a WM_PAINT
, three steps are performed:
- The ComCtl is painted into the internal
Graphics
object via a WM_PRINTCLIENT
message.
IntPtr hdc = internalGraphics.GetHdc();
Message printClientMessage = Message.Create( Handle,
WM_PRINTCLIENT, hdc, IntPtr.Zero );
DefWndProc( ref printClientMessage );
internalGraphics.ReleaseHdc( hdc );
- The
OnPaint()
is now invoked using PaintEventArgs
constructed from the internal Graphics
object.
OnPaint( new PaintEventArgs( internalGraphics, Rectangle.FromLTRB(
updateRect.left,
updateRect.top,
updateRect.right,
updateRect.bottom ) ) );
- The
Bitmap
of the internal Graphics
object is copied to the normal screen Graphics
device.
screenGraphics.DrawImage( internalBitmap, 0, 0 );
Also, the WM_ERASEBKGND
was filtered out to remove flicker.
case WM_ERASEBKGND:
return;
Also, the Paint
event was added to restore browsable attributes.
[
EditorBrowsableAttribute( EditorBrowsableState.Always ),
BrowsableAttribute(true)
]
public new event PaintEventHandler Paint {
add { base.Paint += value; }
remove{ base.Paint -= value; }
}
Using the Code
To use the TreeViewWithPaint
control:
- Just add the control to the toolbox.
- Drag it on to your form.
- Attach a
Paint
handler to the now exposed Paint
event.
To create a <AnotherComCtl>WithPaint
, modify TreeViewWithPaint
as follows:
- Use
<AnotherComCtl>
as the base class. - Copy the
<AnotherComCtl>
class attributes to the <AnotherComCtl>WithPaint
class. - Add an <AnotherComCtl>WithPaint.bmp, which is a 16x16 bit map used for the toolbox icon.
TreeViewWithPaint Control Test Bench
I created a simple Form
containing a single TreeViewWithPaint
control. The Paint
event can now be used.
treeViewWithPaint1.Paint += new PaintEventHandler(
treeViewWithPaint1_Paint );
This particular Paint
handler just draws a simple white band inside the selected node.
private void treeViewWithPaint1_Paint(object sender, PaintEventArgs e) {
Graphics g = e.Graphics;
TreeNode node = treeViewWithPaint1.SelectedNode;
if( node != null && node.IsVisible ) {
using( Pen pen = new Pen( Color.Yellow ) ) {
g.DrawRectangle( pen,
node.Bounds.X + 1,
node.Bounds.Y + 1,
node.Bounds.Width - 3,
node.Bounds.Height - 3
);
}
}
}
Points of Interest
- The needed attributes were discovered by using the VB (not C#) object browser for VS 2002 (not VS 2003).
- There is an undocumented feature of XP that horizontal scroll bars appear on
TreeView
if you add items at design time.
License
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.