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

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

我正在使用C#.Net 3.5 WinForms

您可以使用以下扩展名:

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中的根的情况。

虽然我觉得编写这段代码是浪费时间,但由于对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的代码中:这些案例被忽略了。

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

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

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

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

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

 ///  /// Handle user dragging nodes in treeview ///  private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) { DoDragDrop(e.Item, DragDropEffects.Move); } ///  /// Handle user dragging node into another node ///  private void treeView1_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Move; } ///  /// Handle user dropping a dragged node onto another node ///  private void treeView1_DragDrop(object sender, DragEventArgs e) { // Retrieve the client coordinates of the drop location. Point targetPoint = treeView1.PointToClient(new Point(eX, eY)); // 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); }