views:

4409

answers:

3

How can I maintain the scroll position of a treeview control in .NET application? For example, I have a treeview control and go through a process of adding various nodes to it tacking them on to the bottom. During this process, I can scroll through the treeview and view different nodes. The problem is when the process completes, the treeview scrolls to the very bottom.

It appears that calling treenode.Expand() is what is throwing me off track here. When a parent node is expanded, it gets the focus.

Is there a way around this? If I'm looking at a specific node while the process is running, I don't want it to jump around on me when the process is done.

+4  A: 

I think I figured it out:

  1. Get the node at the top of the treeview.
  2. Expand the parent node.
  3. Make the node that was previously at the top visible.
If treeNodeParent.IsExpanded = False Then
    Dim currentNode As TreeNode = TreeViewHosts.GetNodeAt(0, 0)
    treeNodeParent.Expand()
    currentNode.EnsureVisible()
End If

Is the a better way to do this?

Matt Hanson
This is the best way I have found, you just beat me to the answer as I was typing it up :)
johnc
Well I'll take that as confirmation for my answer then. Thanks lagerdalek.
Matt Hanson
+6  A: 

I'm not a VB guy but in C# I do it this way:

Some Win32 native functions:

[DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
public static extern int GetScrollPos(int hWnd, int nBar);

[DllImport("user32.dll")]
static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

private const int SB_HORZ = 0x0;
private const int SB_VERT = 0x1;

A method which returns a point for the current scroll position:

private Point GetTreeViewScrollPos(TreeView treeView)
{
    return new Point(
        GetScrollPos((int)treeView.Handle, SB_HORZ), 
        GetScrollPos((int)treeView.Handle, SB_VERT));
}

A method to set the scroll position:

private void SetTreeViewScrollPos(TreeView treeView, Point scrollPosition)
{
    SetScrollPos((IntPtr)treeView.Handle, SB_HORZ, scrollPosition.X, true);
    SetScrollPos((IntPtr)treeView.Handle, SB_VERT, scrollPosition.Y, true); 
}

Then when you update your tree, do the following:

Point ScrollPos = GetTreeViewScrollPos(treeMain);
// write your update code here
SetTreeViewScrollPos(treeMain, ScrollPos);
Stefan Koell
Just tested. Works good for me. I also added the functions as extension methods to TreeView :) ty
Steve
I tryed this code, but it affects only scroll bar on my tree, not contents.
Veton
I have the same problem as Veton. Does anyone has a solution for this?
Martijn
What exactly do you mean by "it doesn't affect content"? Of course you need to set the previous selected again and with the above code your tree shouldn't change at all except for a very brief flicker. If your content changed between the refreshes, the scroll position will be slightly off, of course.
Stefan Koell
+2  A: 

I found it's best to wrap the "SetTreeViewScrollPosition(point)" with a Begin and End Update...

    private void treeViewXml1_Scroll(object sender, ScrollEventArgs e)
    {
        Point point = treeViewXml1.GetTreeViewScrollPosition();

        treeViewXml2.BeginUpdate();
        treeViewXml2.SetTreeViewScrollPosition(point);
        treeViewXml2.EndUpdate();
    }

    private void treeViewXml2_Scroll(object sender, ScrollEventArgs e)
    {
        Point point = treeViewXml2.GetTreeViewScrollPosition();

        treeViewXml1.BeginUpdate();
        treeViewXml1.SetTreeViewScrollPosition(point);
        treeViewXml1.EndUpdate();
    }
D Lyonnais
+1 The `BeginUpdate()` and `EndUpdate()` did the trick for me!
Martijn