C# 向上或向下移动树中的节点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2203975/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Move Node in Tree up or Down
提问by Kai
What is the most accurate way to move a node up and down in a treeview. I got a context menu on each node and the selected node should be moved with all its subnodes.
在树视图中上下移动节点的最准确方法是什么。我在每个节点上都有一个上下文菜单,所选节点应与其所有子节点一起移动。
I'm using C# .Net 3.5 WinForms
我正在使用 C# .Net 3.5 WinForms
采纳答案by Dynami Le Savard
You can use the following extensions :
您可以使用以下扩展:
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);
}
}
}
}
Child nodes will follow their parents.
子节点将跟随其父节点。
EDIT: Added case that node to move is a root in the TreeView.
编辑:添加了要移动的节点是 TreeView 中的根的情况。
回答by BillW
While I feel writing this code is a waste of time, given the lack of response to comments by the OP, the least I can do is show how the code example by Le-Savard can be fixed so that muliple clicks of the up or down choice on the context menu ... assuming the context menu is not auto-closed each time and the user is forced to select the same node over and over again ... will do the right thing with the orignally selected node, and not create un-intended side effects :
虽然我觉得编写这段代码是在浪费时间,但鉴于 OP 对评论缺乏回应,我至少可以展示如何修复 Le-Savard 的代码示例,以便多次点击向上或向下上下文菜单上的选择......假设上下文菜单不是每次都自动关闭并且用户被迫一遍又一遍地选择同一个节点......将对原始选择的节点做正确的事情,而不是创建意想不到的副作用:
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;
}
}
}
}
Of course this fix, still does not address the fact that in the example code that multiple root nodes cannot be moved (since they are 'parentless) : that's easiliy fixable.
当然,这个修复仍然没有解决在示例代码中无法移动多个根节点(因为它们是“无父节点”)的事实:这很容易修复。
Nor does it address the more interesting case where moving up a top child-node means you make some interpretation of where that "promoted" child code should go : exactly the same "strategic choice" is involved where you "move down" the last child node of a parent node and are thus required to decide where it should go. In Dynami Le-Savard's code : these cases are just ignored.
它也没有解决更有趣的情况,即向上移动顶部子节点意味着您对“提升”的子代码应该去哪里做出一些解释:在您“向下移动”最后一个子节点的地方涉及完全相同的“战略选择”一个父节点的节点,因此需要决定它应该去哪里。在 Dynami Le-Savard 的代码中:这些情况被忽略了。
However, it is a design-choiceto restrict child node from only being moved within their parent nodes Nodes collection : a design choice that may be perfectly suitable for one solution.
然而,这是一个设计选择,以从它们的父节点的节点集合中仅被移动限制子节点:一个设计选择,其可以是完全适合的解决方案。
Similarly, it is a design choiceto force a user to select a node and context-click to get a context menu that allows a choice of moving up or down every single time they want to move it: that's not a design choice I'd make : I'd be using drag-and-drop here or buttons that allow repeated rapid-fire relocation of any selected node anywhere in the tree.
同样,强制用户选择一个节点并单击上下文以获取上下文菜单是一种设计选择,该菜单允许在每次想要移动它时选择向上或向下移动:这不是我想要的设计选择make :我会在这里使用拖放或按钮,允许重复快速重新定位树中任何选定节点的位置。
By the way I like Dynami Le-Savard's use of extensions here.
顺便说一下,我喜欢 Dynami Le-Savard 在这里使用扩展。
回答by GrayDwarf
Here's a solution that allows you to drag & drop nodes to wherever you want. To move a node to the same level as another node, just hold down shift when dropping the node. This is a really easy way to go compared to the alternatives and their potential problems. Example was written with a more recent version of .Net (4.5).
这是一个解决方案,允许您将节点拖放到任何您想要的位置。要将节点移动到与另一个节点相同的级别,只需在删除节点时按住 shift。与替代方案及其潜在问题相比,这是一种非常简单的方法。示例是使用更新版本的 .Net (4.5) 编写的。
Note: Be sure and AllowDrop=true on the treeview control otherwise you can't drop nodes.
注意:确保在树视图控件上使用 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);
}