在树视图中上下移动节点的最准确方法是什么。我在每个节点上都有一个上下文菜单,所选节点应该随其所有子节点一起移动。
我正在使用C#.Net 3.5 WinForms
答案 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);
}