Click here to Skip to main content
15,892,059 members
Articles / Programming Languages / XML

Generating Missing Paint Event for TreeView and ListView Controls

Rate me:
Please Sign up or sign in to vote.
4.77/5 (25 votes)
14 Jul 20032 min read 174.4K   1.6K   66   24
Article on generating missing Paint event for TreeView, ListView

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.

C#
//Recreate internal graphics object
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:

  1. The ComCtl is painted into the internal Graphics object via a WM_PRINTCLIENT message.
    C#
    //Draw Internal Graphics
    IntPtr hdc = internalGraphics.GetHdc();
    Message printClientMessage = Message.Create( Handle, 
         WM_PRINTCLIENT, hdc, IntPtr.Zero );  
    DefWndProc( ref printClientMessage );
    internalGraphics.ReleaseHdc( hdc );
  2. The OnPaint() is now invoked using PaintEventArgs constructed from the internal Graphics object.
    C#
    //Add the missing OnPaint() call
    OnPaint( new PaintEventArgs( internalGraphics, Rectangle.FromLTRB( 
        updateRect.left,
        updateRect.top,
        updateRect.right,
        updateRect.bottom ) ) );
  3. The Bitmap of the internal Graphics object is copied to the normal screen Graphics device.
    C#
    //Draw Screen Graphics
    screenGraphics.DrawImage( internalBitmap, 0, 0 );

Also, the WM_ERASEBKGND was filtered out to remove flicker.

C#
case WM_ERASEBKGND:
    //removes flicker
    return;

Also, the Paint event was added to restore browsable attributes.

C#
[
//Re-enable Attributes for the Paint Event
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:

  1. Just add the control to the toolbox.
  2. Drag it on to your form.
  3. Attach a Paint handler to the now exposed Paint event.

To create a <AnotherComCtl>WithPaint, modify TreeViewWithPaint as follows:

  1. Use <AnotherComCtl> as the base class.
  2. Copy the <AnotherComCtl> class attributes to the <AnotherComCtl>WithPaint class.
  3. 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.

C#
treeViewWithPaint1.Paint += new PaintEventHandler( 
       treeViewWithPaint1_Paint );

This particular Paint handler just draws a simple white band inside the selected node.

C#
//Add a simple yellow 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.


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionListView Attributes Pin
CodingLikeABoss17-May-17 11:35
CodingLikeABoss17-May-17 11:35 
QuestionLicence information Pin
sanjay Choudhary26-Jun-16 21:16
sanjay Choudhary26-Jun-16 21:16 
GeneralCustom DrawNod Pin
DblK29-Jun-09 10:43
DblK29-Jun-09 10:43 
GeneralRIGHT TO LEFT DISPLAY NOT CORRECT Pin
benchserv18-May-08 17:17
benchserv18-May-08 17:17 
Questionget internalgraphics with alpha? Pin
boxed19-Jul-06 3:31
boxed19-Jul-06 3:31 
GeneralFlickerfree Treeview Pin
Ivi2210-Jul-06 0:17
Ivi2210-Jul-06 0:17 
Hi, a flicker free treeview (Erase background only when scrolling to prevent
artifacts)

Imports System.Runtime.InteropServices
Public Class xTreeView
Inherits System.Windows.Forms.TreeView

#Region " Vom Windows Form Designer generierter Code "

Public Sub New()
MyBase.New()

' Dieser Aufruf ist für den Windows Form-Designer erforderlich.
InitializeComponent()

End Sub

'UserControl überschreibt den Löschvorgang zum Bereinigen der Komponentenliste.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

' Für Windows Form-Designer erforderlich
Private components As System.ComponentModel.IContainer

'HINWEIS: Die folgende Prozedur ist für den Windows Form-Designer erforderlich
'Sie kann mit dem Windows Form-Designer modifiziert werden.
'Verwenden Sie nicht den Code-Editor zur Bearbeitung.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
components = New System.ComponentModel.Container
End Sub

#End Region

<StructLayout(LayoutKind.Sequential)> Public Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure

<StructLayout(LayoutKind.Sequential)> Public Structure PAINTSTRUCT
Public hdc As Integer
Public fErase As Boolean
Public rcPaint As RECT
Public fRestore As Boolean
Public fIncUpdate As Boolean
Public reserved1 As Integer
Public reserved2 As Integer
Public reserved3 As Integer
Public reserved4 As Integer
Public reserved5 As Integer
Public reserved6 As Integer
Public reserved7 As Integer
Public reserved8 As Integer
End Structure


Declare Auto Function GetClientRect Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpRect As RECT) As Boolean
Declare Auto Function BeginPaint Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As IntPtr
Declare Auto Function EndPaint Lib "user32.dll" (ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As Boolean
Declare Auto Function BitBlt Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal destX As Integer, ByVal destY As Integer, ByVal Width As Integer, ByVal Height As Integer, ByVal Src As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal xAction As Integer) As Boolean
Declare Auto Function CreateCompatibleDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As IntPtr
Declare Auto Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As IntPtr
Declare Auto Function SelectObject Lib "gdi32.dll" (ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
Declare Auto Function DeleteDC Lib "gdi32.dll" (ByVal hdc As IntPtr) As Boolean
Declare Auto Function DeleteObject Lib "gdi32.dll" (ByVal hObject As IntPtr) As Boolean

Const SRCCOPY As Integer = &HCC0020

Const WM_ERASEBKGND As Integer = &H14
Const WM_PAINT As Integer = &HF
Const WM_PRINTCLIENT As Integer = &H318
Const WM_VSCROLL As Integer = &H115
Const WM_HSCROLL As Integer = &H114

Dim Del As Boolean = False
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_ERASEBKGND
If Del = False Then
Return
End If
Case WM_VSCROLL
Del = True
Case WM_HSCROLL
Del = True
Case WM_PAINT
Del = False

Dim paintStruct As PAINTSTRUCT = New PAINTSTRUCT
Dim hdc As IntPtr = BeginPaint(m.HWnd, paintStruct)

'Get the client dimensions of the window
Dim aRect As RECT = New RECT
GetClientRect(m.HWnd, aRect)

'Create a memory DC to avoid any flickering
Dim memDC As IntPtr = CreateCompatibleDC(hdc)

'Create a bitmap to be used by the memory dc.
'The size of this bitmap equals the size of the client area of your window.
Dim hBmp As IntPtr = CreateCompatibleBitmap(hdc, aRect.right, aRect.bottom)
'Select the created bitmap into our memory DC.
Dim hOldBitmap As IntPtr = SelectObject(memDC, hBmp)

'Clear background of memory DC with background color.
Dim memGfx As Graphics = Graphics.FromHdc(memDC)
memGfx.Clear(Me.BackColor)

Dim printClientMessage As Message = Message.Create(Handle, WM_PRINTCLIENT, memDC, IntPtr.Zero)
DefWndProc(printClientMessage)

'Blit (copy) memory DC to screen
BitBlt(hdc, 0, 0, aRect.right, aRect.bottom, memDC, 0, 0, SRCCOPY)

'Select old bitmap into memory DC.
'This should be done before delete the DC.
SelectObject(memDC, hOldBitmap)
'Delete our bitmap to cleanup memory
DeleteObject(hBmp)
'Delete our memory DC to cleanup memory
DeleteDC(memDC)

memGfx.Dispose()
EndPaint(m.HWnd, paintStruct)
Return
End Select
MyBase.WndProc(m)
End Sub

Private Sub XTreeView1_BeforeCollapse(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles Me.BeforeCollapse
Me.BeginUpdate()
End Sub
Private Sub XTreeView1_AfterCollapse(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles Me.AfterCollapse
Me.EndUpdate()
End Sub
Private Sub XTreeView1_BeforeExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewCancelEventArgs) Handles Me.BeforeExpand
Me.BeginUpdate()
End Sub
Private Sub XTreeView1_AfterExpand(ByVal sender As Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles Me.AfterExpand
Me.EndUpdate()
End Sub
End Class


Helps: http://cgdev.iworld.com/forum/showthread.php?t=296611&page=1&pp=15
GeneralUpdate Pin
Ivi226-Aug-07 2:10
Ivi226-Aug-07 2:10 
QuestionListView Pain Evetn in VB.NET Pin
JoanComasFdz4-May-06 3:50
JoanComasFdz4-May-06 3:50 
QuestionRe: ListView Pain Evetn in VB.NET Pin
JoanComasFdz4-May-06 6:10
JoanComasFdz4-May-06 6:10 
AnswerRe: ListView Pain Evetn in VB.NET Pin
Graznok29-Sep-11 9:42
Graznok29-Sep-11 9:42 
QuestionLegal info? Pin
Filip Fracz30-Jan-06 10:47
Filip Fracz30-Jan-06 10:47 
Generalorganization chart (horizontal tree view) Pin
cahnakal3-Oct-05 23:23
cahnakal3-Oct-05 23:23 
GeneralRe: organization chart (horizontal tree view) Pin
cahnakal3-Oct-05 23:38
cahnakal3-Oct-05 23:38 
QuestionList View OnPaint Event? Pin
Almutalibi, Mohamad30-Jun-05 3:37
Almutalibi, Mohamad30-Jun-05 3:37 
AnswerRe: List View OnPaint Event? Pin
brutalis19-Sep-05 23:03
brutalis19-Sep-05 23:03 
GeneralJust to note how to do it in another way Pin
Nacho Nachev16-Jan-05 23:17
Nacho Nachev16-Jan-05 23:17 
GeneralThings to do to make it work for VB.NET Pin
17-Aug-04 3:20
suss17-Aug-04 3:20 
GeneralGradient background Pin
Diego Resnik24-Nov-03 10:16
Diego Resnik24-Nov-03 10:16 
QuestionWhat about OwnerData? Pin
Trevor Magnusson1-Oct-03 13:34
Trevor Magnusson1-Oct-03 13:34 
GeneralGraphic Artifacts Problem Pin
Chris Conboy29-Aug-03 7:44
Chris Conboy29-Aug-03 7:44 
GeneralRe: Graphic Artifacts Problem Pin
Chris Conboy29-Aug-03 8:02
Chris Conboy29-Aug-03 8:02 
GeneralRe: Graphic Artifacts Problem Pin
Terry Henning4-Sep-03 16:15
Terry Henning4-Sep-03 16:15 
GeneralTreeNode Paint event Pin
Mikael Wiberg16-Jul-03 14:59
Mikael Wiberg16-Jul-03 14:59 
GeneralRe: TreeNode Paint event Pin
nickafx4-Jan-05 22:39
nickafx4-Jan-05 22:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.