Click here to Skip to main content
Email Password   helpLost your password?

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

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralCustom DrawNod
DblK
11:43 29 Jun '09  
Hi,

If I had set DrawMode to TreeViewDrawMode.OwnerDrawAll, this hints seems not to work.
I have draw some box in the right of each node, when I resize the treeview, glitches happen.
How can I fix this ?
The way using the internal graphics help me a lot to reduce flicker but now, some custom graphics aren't repainted.


Thx in advance
GeneralRIGHT TO LEFT DISPLAY NOT CORRECT
benchserv
18:17 18 May '08  
Hi,

This method is really a great help but Im having problem in displaying the list in right to left; it display in reverseFrown Please help. Thanks. Please email me in case you've already the solution sir, many thanks

Best regards
itservice_101@yahoo.com.ph, benchserv@yahoo.com
Generalget internalgraphics with alpha?
boxed
4:31 19 Jul '06  
I've created a ListView with the OnPaint event. The purpose is to draw the ListView with a transparant background. The problem is that the internalGraphics that was painted by the window procedure using WM_PRINTCLIENT has a white Item background.
The result is that the ListView itself has a transparant background, but the items and the surroundings have a white background.

Is it possible for the window procdure to draw the Items with the alpha data on the screenGraphics?
GeneralFlickerfree Treeview
Ivi22
1: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
Ivi22
3:10 6 Aug '07  
Hi, this works better for me:
...
Const WM_VSCROLL As Integer = &H115
Const WM_HSCROLL As Integer = &H114
Const OCM_NOTIFY As Integer = &H204E <-- Insert this
...

...
Case WM_HSCROLL
Del = True
Case OCM_NOTIFY <-- Insert this
Del = True <-- Insert this
Case WM_PAINT
Del = False
...

QuestionListView Pain Evetn in VB.NET
k^s
4:50 4 May '06  
Hi, ive tryed to traduce the codef rom your project posted to vb.net, i've done all coerrctly, but there are some lines that I'm unable to traduce:

[EditorBrowsableAttribute( EditorBrowsableState.Always ),
BrowsableAttribute(true)
]
public new event PaintEventHandler Paint {
add { base.Paint += value; }
remove{ base.Paint -= value; }
}

I've traduced this as:


< EditorBrowsable(EditorBrowsableState.Always), Browsable(True) > _
Public Shadows Event Paint As PaintEventHandler

But I don't know how to write the add / remove functions for de event in vb.net. I've tryed some translators but they were unable too.

Also, when he listview is created, the background of the ListView is the _real_ background (in example, a part of desktop!). Why? Have i to catch the paint event and write some code?

-- modified at 10:28 Thursday 4th May, 2006
QuestionRe: ListView Pain Evetn in VB.NET
k^s
7:10 4 May '06  
I've debugged and seena nother effect. This is the code in C# that must Raise de Paint event:

//Add the missing OnPaint() call
OnPaint( new PaintEventArgs( internalGraphics, Rectangle.FromLTRB(
updateRect.left,
updateRect.top,
updateRect.right,
updateRect.bottom ) ) );

And this is my codein vb.net:

Dim e As New PaintEventArgs(internalGraphics, _
Rectangle.FromLTRB(updateRect.left, updateRect.top, updateRect.right, updateRect.bottom))

Me.OnPaint(e)

Why the first is raising de paint event and mine not?

-- modified at 12:10 Thursday 4th May, 2006
GeneralLegal info?
Filip Fracz
11:47 30 Jan '06  
Great job! Are there any restrictions on using your code? Thanks,

- Filip
Generalorganization chart (horizontal tree view)
cahnakal
0:23 4 Oct '05  
I have browse this site for moment looking for article discussing 'horizontal' tree view. I means, organization chart is a treeview with its layout rotated 90 degree to right direction, am I correct ? look at this sample

TreeView:
node1
|- node2
|- node3
|- node4

Organization Chart:
node1
|
node2 ----- node3
|
node4

anybody has seen article like this somewhere ? please let me know.

cahnakal

nobody cares your life like you do!
GeneralRe: organization chart (horizontal tree view)
cahnakal
0:38 4 Oct '05  
hmm... the messageboard meshup my organization chart lay out. let me try relayout ( _ means empty space)

TreeView:
node1:
__|
__|--node2
__|
__|--node3
______|
______|--node4

Organization Chart:
______node1_________
____|---|----|______
____|________|______
__node2_____node3___
______________|_____
______________|_____
____________node4___

nobody cares your life like you do!
GeneralList View OnPaint Event?
Almutalibi
4:37 30 Jun '05  
That seems to be valid only with the TreeView I tried to use it with ListView but failed, could any one help me how to get the paint event for the listview?

Mal
GeneralRe: List View OnPaint Event?
brutalis
0:03 20 Sep '05  
I did it overriding WndProc and defining the WM_PAINT member constant:
(I got the WM_PAINT value from winuser.h)

private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PAINT:
{

.....
break;
}
}


}
GeneralJust to note how to do it in another way
Nacho Nachev
0:17 17 Jan '05  
public class CustomTreeView : TreeView
{
private const int WM_PAINT = 0x000F;

[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT)
{
Trace.WriteLine("painting ...");
}
base.WndProc (ref m);
}
}

Nacho Nachev,

Sciant AG
Bulgaria
Web: http://www.sciant.com

GeneralThings to do to make it work for VB.NET
Florent Geffroy
4:20 17 Aug '04  
Win32 structures and functions are not directly available in VB.NET, you have to import them. So just add a new class called Win32   :


Imports System.Runtime.InteropServices

Public Class Win32

      <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 System.IntPtr
            Public fErase As Boolean
            Public rcPaint As RECT
            Public fRestore As Boolean
            Public fIncUpdate As Boolean
            <MarshalAs(UnmanagedType.ByValArray, SizeConst:=32)> Public rgbReserved() As Byte
      End Structure


      Declare Auto Function GetUpdateRect Lib "user32.dll" (ByVal hWnd As System.IntPtr, ByRef lpRect As RECT, ByVal bErase As Boolean) As Boolean

      Declare Auto Function BeginPaint Lib "user32.dll" (ByVal hwnd As System.IntPtr, ByRef lpPaint As PAINTSTRUCT) As System.IntPtr

      Declare Auto Function EndPaint Lib "user32.dll" (ByVal hwnd As System.IntPtr, ByVal lpPaint As PAINTSTRUCT) As Boolean

End Class
GeneralGradient background
Dieg
11:16 24 Nov '03  
I tried adding:
Brush backgroundBrush = new LinearGradientBrush(Rectangle.FromLTRB(
updateRect.left,
updateRect.top,
updateRect.right,
updateRect.bottom ),
StartColor,EndColor,GradientMode);

screenGraphics.FillRectangle(backgroundBrush,this.ClientRectangle);

in the WM_PAINT case or in the onpaint event both had the same result:
a nice looking gradient background with no tree?!Cry
before seeing this article I also tried overiding the onpaint/onpaintbackground...D'Oh!
Any ideas on how to add this kind of background to the treeview?

10x in advance,
Dieg
GeneralWhat about OwnerData?
Trevor Magnusson
14:34 1 Oct '03  
The ListViewWithPaint suggests some features of the underlying COM object are not exposed by the .NET wrapper.
I am very interested in the LVS_OWNERDATA style, so that I can have large "virtual" lists where the data is not stored in the control.
Is there any way of exposing this feature?
GeneralGraphic Artifacts Problem
Liumas
8:44 29 Aug '03  
Great control, I saw this when you posted it, and finally got around to turning it into a ListView. It works great, but...

When an item is selected, then another item is selected, the previous item has graphic artifacts: a line on the left, and sometimes a line on the right. Also, somtimes when the columns are resized, artifacts get left on the right side of the rows in the empty ListView area beyond any columns.

I realize this is an old problem for ListView controls.

Any ideas on how to handle this problem?

Thanks.
GeneralRe: Graphic Artifacts Problem
Liumas
9:02 29 Aug '03  
(reply to myself!)

Graphics artifacts problem is partly solved. If the bounding rectangle makes sure not to go past the left edge of the item, it will get repainted when the new item is selected.

// in listViewWithPaint1_Paint(()
g.DrawRectangle( pen,
item.Bounds.X + 2, // fix: instead of + 1
item.Bounds.Y + 1,
item.Bounds.Width - 3,
item.Bounds.Height - 3 );

BUT, I'm still working on a fix for the right-side artifacts. Will post if I work it out. I may be willing to live with a kludge, depends...

Liumas

GeneralRe: Graphic Artifacts Problem
Terry Henning
17:15 4 Sep '03  
I'm making a quick guess here based on the code above, that the problem is because he's by passing the WM_ERASEBACKGROUND to prevent flicker. You may need to re-enable it and optimize from there to cut down on flicker.
GeneralTreeNode Paint event
Mikael Wiberg
15:59 16 Jul '03  
Great work!
Is it possible to do something similar so that a Paint event is raised for each TreeNode that is to be painted rather than one Paint event for the whole TreeView? Or why not both?
GeneralRe: TreeNode Paint event
nickafx
23:39 4 Jan '05  
do that in the Paint event of the tree

Try
Dim node2 As TreeNode = gtree.SelectedNode
Dim nb As New System.Drawing.Drawing2D.LinearGradientBrush(myrectangle2, Color.AliceBlue, gradOverColorUp, System.Drawing.Drawing2D.LinearGradientMode.Vertical)

e.Graphics.FillRectangle(nb, node2.Bounds.X - 2, node2.Bounds.Y, node2.Bounds.Width + 4, node2.Bounds.Height - 1)
e.Graphics.DrawRectangle(New Pen(color.White), node2.Bounds.X - 2, node2.Bounds.Y, node2.Bounds.Width + 4, node2.Bounds.Height - 1)
e.Graphics.DrawString(node2.Text, New Font("Arial", 8), Brushes.SteelBlue, node2.Bounds.X, node2.Bounds.Y + 4)

Catch ex As Exception
End Try


Last Updated 15 Jul 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010