Click here to Skip to main content
Licence 
First Posted 14 Jul 2003
Views 124,287
Downloads 377
Bookmarked 63 times

Generating missing Paint event for TreeView and ListView controls

By J Young | 14 Jul 2003
Article on generating missing Paint event for TreeView, ListView
2 votes, 8.7%
1
1 vote, 4.3%
2

3
3 votes, 13.0%
4
17 votes, 73.9%
5
4.85/5 - 23 votes
3 removed
μ 4.23, σa 2.22 [?]

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.

//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.
    //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.
    //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.
    //Draw Screen Graphics
    screenGraphics.DrawImage( internalBitmap, 0, 0 );

Also the WM_ERASEBKGND was filtered out to remove flicker.

case WM_ERASEBKGND:
    //removes flicker
    return;

Also the Paint event was added to restore browsable attributes.

[
//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.

treeViewWithPaint1.Paint += new PaintEventHandler( 
       treeViewWithPaint1_Paint );

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

//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

About the Author

J Young



United States United States

Member


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralCustom DrawNod PinmemberDblK11:43 29 Jun '09  
GeneralRIGHT TO LEFT DISPLAY NOT CORRECT Pinmemberbenchserv18:17 18 May '08  
Questionget internalgraphics with alpha? Pinmemberboxed4:31 19 Jul '06  
GeneralFlickerfree Treeview PinmemberIvi221:17 10 Jul '06  
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 PinmemberIvi223:10 6 Aug '07  
QuestionListView Pain Evetn in VB.NET Pinmemberk^s4:50 4 May '06  
QuestionRe: ListView Pain Evetn in VB.NET Pinmemberk^s7:10 4 May '06  
AnswerRe: ListView Pain Evetn in VB.NET PinmemberGraznok10:42 29 Sep '11  
QuestionLegal info? PinmemberFilip Fracz11:47 30 Jan '06  
Generalorganization chart (horizontal tree view) Pinmembercahnakal0:23 4 Oct '05  
GeneralRe: organization chart (horizontal tree view) Pinmembercahnakal0:38 4 Oct '05  
QuestionList View OnPaint Event? PinmemberAlmutalibi4:37 30 Jun '05  
AnswerRe: List View OnPaint Event? Pinmemberbrutalis0:03 20 Sep '05  
GeneralJust to note how to do it in another way PinmemberNacho Nachev0:17 17 Jan '05  
GeneralThings to do to make it work for VB.NET PinmemberFlorent Geffroy4:20 17 Aug '04  
GeneralGradient background PinmemberDieg11:16 24 Nov '03  
QuestionWhat about OwnerData? PinmemberTrevor Magnusson14:34 1 Oct '03  
GeneralGraphic Artifacts Problem PinmemberLiumas8:44 29 Aug '03  
GeneralRe: Graphic Artifacts Problem PinmemberLiumas9:02 29 Aug '03  
GeneralRe: Graphic Artifacts Problem PinsussTerry Henning17:15 4 Sep '03  
GeneralTreeNode Paint event PinmemberMikael Wiberg15:59 16 Jul '03  
GeneralRe: TreeNode Paint event Pinmembernickafx23:39 4 Jan '05  

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

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120210.1 | Last Updated 15 Jul 2003
Article Copyright 2003 by J Young
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid