保持树视图的滚动位置

时间:2008-12-02 01:42:43

标签: .net winforms treeview

如何在.NET应用程序中维护树视图控件的滚动位置?例如,我有一个树视图控件,并通过添加各种节点的过程将它们固定到底部。在此过程中,我可以滚动树视图并查看不同的节点。问题是当过程完成时,树视图会滚动到最底层。

看来调用treenode.Expand()是让我偏离轨道的原因。展开父节点时,它将获得焦点。

有解决方法吗?如果我在进程运行时查看特定节点,我不希望它在进程完成时跳转到我身上。

9 个答案:

答案 0 :(得分:26)

我不是VB人,但在C#中我是这样做的:

一些Win32原生函数:

[DllImport("user32.dll",  CharSet = CharSet.Unicode)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);

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

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

返回当前滚动位置的点的方法:

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

设置滚动位置的方法:

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); 
}

然后在更新树时,请执行以下操作:

BeginUpdate();
Point ScrollPos = GetTreeViewScrollPos(treeMain);
// write your update code here
SetTreeViewScrollPos(treeMain, ScrollPos);
EndUpdate();

答案 1 :(得分:17)

我想我明白了:

  1. 将节点置于树视图的顶部。
  2. 展开父节点。
  3. 使之前位于顶部的节点可见。
  4. If treeNodeParent.IsExpanded = False Then
        Dim currentNode As TreeNode = TreeViewHosts.GetNodeAt(0, 0)
        treeNodeParent.Expand()
        currentNode.EnsureVisible()
    End If
    

    这是更好的方法吗?

答案 2 :(得分:7)

另一种可以在没有外部函数的情况下保留滚动位置的方法是使用树的TopNode属性...

TopNode获取或设置树视图控件中第一个完全可见的树节点。

如果您只想扩展节点并让它保留顶级节点:

TreeNode topNode = m_Tree.TopNode;
treenode.Expand();
m_Tree.TopNode = topNode;

否则,如果要重建树(例如刷新文件结构),可以使用以下方法...

清除树之前,请存储顶级节点的完整路径:

string topNodePath = null;
TreeNode topNode = null;
if (m_Tree.TopNode != null)
{
    topNodePath = m_Tree.TopNode.FullPath;
}

m_Tree.Clear();

添加节点后,请针对topNodePath检查其FullPath:

nodes.Add(node)
if ((topNodePath != null) && (node.FullPath == topNodePath))
{
    topNode = node;
}

添加所有节点后,更新树的TopNode属性:

if (topNode != null)
{
    m_Tree.TopNode = topNode;
}

我对选定和扩展节点使用类似的技术。 SelectedNode几乎与上面显示的TopNode完全一样。 对于扩展节点,我使用递归函数来遍历子节点,并将扩展节点的路径添加到列表中。然后在添加子项后,根据路径扩展它们。

当然,如果你有很多同名的兄弟节点,这可能也行不通: - )

答案 3 :(得分:2)

我发现最好将SetTreeViewScrollPosition(point)BeginUpdateEndUpdate ...

包起来
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();
}

答案 4 :(得分:1)

我也有同样的问题滚动本身更新,但树视图的内容没有滚动。通过在BeginUpdate()周围添加EndUpdate()SetScrollPos()可以轻松解决此问题。

this.hierarchyTreeView.BeginUpdate();
SetScrollPos(this.hierarchyTreeView.Handle, SB_VERT, 5, true);
this.hierarchyTreeView.EndUpdate();

答案 5 :(得分:0)

myTreeView.Nodes[0].EnsureVisible();

答案 6 :(得分:0)

这是对Stefan Koell的美丽回应的修订,作为TreeViewExtension: (完整的解决方案)

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;

public static class TreeViewExtension
{
    #region Constants

    private const int ScrollBarHorizontal = 0x0;

    private const int ScrollBarVertical = 0x1;

    #endregion

    #region Public Methods and Operators

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

    public static Point ScrollPosition(this TreeView treeView)
    {
        return new Point(
            GetScrollPos((int)treeView.Handle(), ScrollBarHorizontal), 
            GetScrollPos((int)treeView.Handle(), ScrollBarVertical));
    }

    public static void ScrollTo(this TreeView treeView, Point scrollPosition)
    {
        SetScrollPos(treeView.Handle(), ScrollBarHorizontal, (int)scrollPosition.X, true);
        SetScrollPos(treeView.Handle(), ScrollBarVertical, (int)scrollPosition.Y, true);
    }

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

    #endregion

    #region Methods

    private static IntPtr Handle(this Visual treeView)
    {
        var handle = IntPtr.Zero;
        var hwndSource = PresentationSource.FromVisual(treeView) as HwndSource;
        if (hwndSource != null)
        {
            handle = hwndSource.Handle;
        }

        return handle;
    }

    #endregion
}

也许它简化了你的工作; - )

答案 7 :(得分:0)

这很好。保存TopNode并在以下时间还原:

this.treeView.BeginUpdate();
TreeNode topNode = this.treeView.TopNode;

// your code
this.treeView.Sort();
this.treeView.SelectedNode = auxNode;

this.treeView.TopNode = topNode;
this.treeView.EndUpdate();

答案 8 :(得分:-3)

最好的方法是使用UpdatePanel并在其中嵌套treeview标记。例如,

<asp:UpdatePanel id="UpdatePanel">
     <ContentTemplate>
         <asp:TreeView id="TreeView">

         </asp:TreeView>
     </ContentTemplate>
</asp:UpdatePanel>

它对我有用,我希望它能解决你的问题。