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 a 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.