23

I came to know that by adding TreeView.BeginUpdate will prevent flickering of treeview, but when i added it in to my project all nodes of my treeview disappears, Can any body tell me why it happens, here is the code snippet where i used TreeView.BeginUpdate and TreeView.EndUpdate

  TreeNode treeNode = new TreeNode("Windows");
        treeView1.Nodes.Add(treeNode);
        //
        // Another node following the first node.
        //
        treeNode = new TreeNode("Linux");
        treeView1.Nodes.Add(treeNode);
        //
        // Create two child nodes and put them in an array.
        // ... Add the third node, and specify these as its children.
        //
        TreeNode node2 = new TreeNode("C#");
        TreeNode node3 = new TreeNode("VB.NET");
        TreeNode[] array = new TreeNode[] { node2, node3 };
        //
        // Final node.
        //
        treeNode = new TreeNode("Dot Net Perls", array);
        treeView1.Nodes.Add(treeNode);
vettori
  • 731
  • 8
  • 17
  • 26
  • 6
    That first one doesn't look right - you're doing `BeginUpdate`/`EndUpdate` within a loop - and worse, the `EndUpdate` is nested inside some conditional code - I'd normally expect to see something suppressing visual updates to be wrapped around the entire set of updates (as per your second example) – Damien_The_Unbeliever Apr 28 '12 at 11:21
  • despite the questionable code, it appears you trying to remedy a symptom of the problem. Is the real question why did you get flickering in the first place? or there was no flicker? – Jeremy Thompson Apr 28 '12 at 11:25
  • if i used just above the addition of nodes i mean (.Nodes.Add) will that be sufficient?? – vettori Apr 28 '12 at 11:25
  • @Jeremy Thompson you mean first place is not required at all contains less node??but any way using that is a best practise?? – vettori Apr 28 '12 at 11:27
  • I wouldn't bother with it, where did you read its best practice? seems more problems than its worth to me en.wikipedia.org/wiki/KISS_principle. If I did face a flicker in the population I'd be careful about my paint events (`which the Begin and EndUpdate do precisely - but you've got them nested in loops`), dbl buffering and may choose to lazy load - or set visible to false and true after population is complete. – Jeremy Thompson Apr 28 '12 at 11:31
  • Can you give me a sample code snippet proving about it? – vettori Apr 28 '12 at 11:37
  • vettori - what is the problem? you've edited your question but you still have this line `m_adminTV.EndUpdate();` in a for loop - take it out and put it at the end of the function. Instead of me providing sample code to prove I can populate a treeview without flickering, instead how about you show me the code I can use to see a treeview flicking and I'll fix it? – Jeremy Thompson Apr 28 '12 at 11:54
  • 1
    Add/assign the root node you are working with, only after constructing the tree. IIRC this is a lot faster. – leppie Apr 28 '12 at 11:54
  • some how i figure it by moving out from loop – vettori Apr 28 '12 at 12:19
  • I rolled it back to a question. If you answered it yourself, please feel free to *post* an answer. – LarsTech Apr 28 '12 at 13:06
  • Please do not destroy your question after it has been answered, for posterity's sake. – BoltClock Apr 30 '12 at 04:33

3 Answers3

92

The Begin/EndUpdate() methods were not designed to eliminate flicker. Getting flicker at EndUpdate() is inevitable, it repaints the control. They were designed to speed-up adding a bulk of nodes, that will be slow by default since every single item causes a repaint. You made it a lot worse by putting them inside the for loop, move them outside for an immediate improvement.

That will probably be sufficient to solve your problem. But you can make it better, suppressing flicker requires double-buffering. The .NET TreeView class overrides the DoubleBuffered property and hides it. Which is a historical accident, the native Windows control only supports double buffering in Windows XP and later. .NET once supported Windows 2000 and Windows 98.

That's not exactly relevant anymore these days. You can put it back by deriving your own class from TreeView. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form, replacing the existing TreeView. The effect is very noticeable, particularly when scrolling.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class BufferedTreeView : TreeView {
    protected override void OnHandleCreated(EventArgs e) {
       SendMessage(this.Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);
        base.OnHandleCreated(e);
    }
    // Pinvoke:
    private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44;
    private const int TVM_GETEXTENDEDSTYLE = 0x1100 + 45;
    private const int TVS_EX_DOUBLEBUFFER = 0x0004;
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 7
    MSDN suggests that your use of TVM_SETEXTENDEDSTYLE isn't quite correct; per http://msdn.microsoft.com/en-us/library/windows/desktop/bb773744(v=vs.85).aspx the WPARAM is a mask and the LPARAM is the flag. So skip getting the old style and just do SendMessage(this.Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER); – EricLaw Nov 07 '12 at 19:23
  • 2
    Ugh, indeed. I'm using an old copy of the MSDN library that documents this wrong. Thanks for the correct! – Hans Passant Nov 07 '12 at 19:41
3

It is not absolutely necessary to derive a new class for this, you can also write a static class function to which you pass the control to enable the doublebuffering:

public static class Helper
{
    public static void EnableDoubleBuffering(Control control)
    {
         SendMessage(control.Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);
    }
        
    private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44;
    private const int TVM_GETEXTENDEDSTYLE = 0x1100 + 45;
    private const int TVS_EX_DOUBLEBUFFER = 0x0004;
        
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
rogie
  • 31
  • 3
2

If you are new like me and need it in vb.net here is @Hans Passant answer. I used it and the change is remarkable

Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
    SendMessage(Me.Handle, TVM_SETEXTENDEDSTYLE, CType(TVS_EX_DOUBLEBUFFER, IntPtr), CType(TVS_EX_DOUBLEBUFFER, IntPtr))
    MyBase.OnHandleCreated(e)
End Sub

Private Const TVM_SETEXTENDEDSTYLE As Integer = &H1100 + 44
Private Const TVM_GETEXTENDEDSTYLE As Integer = &H1100 + 45
Private Const TVS_EX_DOUBLEBUFFER As Integer = &H4
<DllImport("user32.dll")>
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wp As IntPtr, ByVal lp As IntPtr) As IntPtr
End Function