向上或向下移动树中的节点

时间:2010-02-04 23:20:00

标签: c# winforms treeview

在树视图中上下移动节点的最准确方法是什么。我在每个节点上都有一个上下文菜单,所选节点应该随其所有子节点一起移动。

我正在使用C#.Net 3.5 WinForms

3 个答案:

答案 0 :(得分:38)

您可以使用以下扩展程序:

public static class Extensions
{
    public static void MoveUp(this TreeNode node)
    {
        TreeNode parent = node.Parent;
        TreeView view = node.TreeView;
        if (parent != null)
        {
            int index = parent.Nodes.IndexOf(node);
            if (index > 0)
            {
                parent.Nodes.RemoveAt(index);
                parent.Nodes.Insert(index - 1, node);
            }
        }
        else if (node.TreeView.Nodes.Contains(node)) //root node
        {
            int index = view.Nodes.IndexOf(node);
            if (index > 0)
            {
                view.Nodes.RemoveAt(index);
                view.Nodes.Insert(index - 1, node);
            }
        }
    }

    public static void MoveDown(this TreeNode node)
    {
        TreeNode parent = node.Parent;
        TreeView view = node.TreeView;
        if (parent != null)
        {
            int index = parent.Nodes.IndexOf(node);
            if (index < parent.Nodes.Count -1)
            {
                parent.Nodes.RemoveAt(index);
                parent.Nodes.Insert(index + 1, node);
            }
        }
        else if (view != null && view.Nodes.Contains(node)) //root node
        {
            int index = view.Nodes.IndexOf(node);
            if (index < view.Nodes.Count - 1)
            {
                view.Nodes.RemoveAt(index);
                view.Nodes.Insert(index + 1, node);
            }
        }
    }
}

子节点将跟随他们的父母。

编辑:添加了一个案例,即要移动的节点是TreeView中的根。

答案 1 :(得分:8)

虽然我觉得编写这段代码是浪费时间,但由于OP对评论的反应不足,我至少可以做的是展示如何修复Le-Savard的代码示例,以便多次点击在上下文菜单上选择向上或向下...假设每次上下文菜单都没有自动关闭,并且用户被迫一遍又一遍地选择同一个节点...将使用orignally selected节点做正确的事情,而不是产生无意的副作用:

public static class Extensions
{
    public static void MoveUp(this TreeNode node)
    {
        TreeNode parent = node.Parent;
        if (parent != null)
        {
            int index = parent.Nodes.IndexOf(node);
            if (index > 0)
            {
                parent.Nodes.RemoveAt(index);
                parent.Nodes.Insert(index - 1, node);

                // bw : add this line to restore the originally selected node as selected
                node.TreeView.SelectedNode = node;
            }
        }
    }

    public static void MoveDown(this TreeNode node)
    {
        TreeNode parent = node.Parent;
        if (parent != null)
        {
            int index = parent.Nodes.IndexOf(node);
            if (index < parent.Nodes.Count - 1)
            {
                parent.Nodes.RemoveAt(index);
                parent.Nodes.Insert(index + 1, node);

                // bw : add this line to restore the originally selected node as selected
                node.TreeView.SelectedNode = node;
            }
        }
    }
}

当然,这个修复程序仍未解决在示例代码中无法移动多个根节点的事实(因为它们是'无父对象的):这很容易解决。

它也没有解决更有趣的情况,即提升一个顶级子节点意味着你对“促进”子代码应该去的地方做出一些解释:在你“向下移动”的地方涉及完全相同的“战略选择”父节点的最后一个子节点因此需要决定它应该去哪里。在Dynami Le-Savard的代码中:这些案例被忽略了。

但是, design-choice 限制子节点仅在其父节点内移动Nodes集合:一种可能完全适合一种解决方案的设计选择。

类似地,强制用户选择节点和上下文单击以获取上下文菜单是设计选择,允许选择每次向上或向下移动想要移动它:这不是我做的设计选择:我会在这里使用拖放或按钮,允许在树中的任何位置重复快速重新定位任何选定节点。

顺便说一句,我喜欢Dynami Le-Savard在这里使用扩展。

答案 2 :(得分:1)

这是一个允许你拖动和解决的解决方案。将节点拖放到您想要的任何位置。要将节点移动到与另一个节点相同的级别,只需在删除节点时按住shift。与替代品及其潜在问题相比,这是一种非常简单的方法。示例是使用更新版本的.Net(4.5)编写的。

注意:确保树视图控件上的AllowDrop = true,否则您无法删除节点。

/// <summary>
/// Handle user dragging nodes in treeview
/// </summary>
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
    DoDragDrop(e.Item, DragDropEffects.Move);
}

/// <summary>
/// Handle user dragging node into another node
/// </summary>
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Move;
}

/// <summary>
/// Handle user dropping a dragged node onto another node
/// </summary>
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
    // Retrieve the client coordinates of the drop location.
    Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));

    // Retrieve the node that was dragged.
    TreeNode draggedNode = e.Data.GetData(typeof(TreeNode));

    // Sanity check
    if (draggedNode == null)
    {
        return;
    }

    // Retrieve the node at the drop location.
    TreeNode targetNode = treeView1.GetNodeAt(targetPoint);

    // Did the user drop the node 
    if (targetNode == null)
    {
        draggedNode.Remove();
        treeView1.Nodes.Add(draggedNode);
        draggedNode.Expand();
    }
    else
    {
        TreeNode parentNode = targetNode;

        // Confirm that the node at the drop location is not 
        // the dragged node and that target node isn't null
        // (for example if you drag outside the control)
        if (!draggedNode.Equals(targetNode) && targetNode != null)
        {
            bool canDrop = true;
            while (canDrop && (parentNode != null))
            {
                canDrop = !Object.ReferenceEquals(draggedNode, parentNode);
                parentNode = parentNode.Parent;
            }

            if (canDrop)
            {
                // Have to remove nodes before you can move them.
                draggedNode.Remove();

                // Is the user holding down shift?
                if (e.KeyState == 4)
                {
                    // Is the targets parent node null?
                    if (targetNode.Parent == null)
                    {
                        // The target node has no parent. That means 
                        // the target node is at the root level. We'll 
                        // insert the node at the root level below the 
                        // target node.
                        treeView1.Nodes.Insert(targetNode.Index + 1, draggedNode);
                    }
                    else 
                    {
                        // The target node has a valid parent so we'll 
                        // drop the node into it's index.
                        targetNode.Parent.Nodes.Insert(targetNode.Index + 1, draggedNode);
                    }
                }
                else
                { 
                    targetNode.Nodes.Add(draggedNode);
                }

                targetNode.Expand();
            }
        }
    }

    // Optional: The following lines are an example of how you might
    // provide a better experience by highlighting and displaying the 
    // content of the dropped node. 
    // treeView1.SelectedNode = draggedNode;
    // NavigateToNodeContent(draggedNode.Tag); 
}