Переместить Node в Дерево вверх или вниз
Каков наиболее точный способ перемещения node вверх и вниз в древовидной структуре. Я получил контекстное меню для каждого node, и выбранный node должен быть перемещен со всеми его подузлами.
Я использую С#.Net 3.5 WinForms
Ответы
Ответ 1
Вы можете использовать следующие расширения:
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);
}
}
}
}
Детские узлы будут следовать за родителями.
EDIT: Добавлен случай, когда node для перемещения является корнем в TreeView.
Ответ 2
В то время как я чувствую, что писать этот код - пустая трата времени, учитывая отсутствие ответа на комментарии OP, наименьшее из того, что я могу сделать, это показать, как можно использовать пример кода Le-Savard, так что множественные клики вверх или вниз в контекстном меню... при условии, что контекстное меню не будет автоматически закрыто каждый раз, и пользователь будет снова и снова выбирать один и тот же node... будет делать правильные действия с выбранным orignally node, а не создавать нежелательные побочные эффекты:
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;
}
}
}
}
Конечно, это исправление, по-прежнему не учитывает тот факт, что в примере кода невозможно переместить несколько корневых узлов (так как они "без родителя" ): этот easiliy fixable.
И не относится ли это к более интересному случаю, когда перемещение вверху ребенка - node означает, что вы делаете некоторую интерпретацию, где должен идти этот "продвинутый" дочерний код: именно тот же "стратегический выбор" задействуется там, где вы "двигаетесь" вниз "последнего дочернего node родительского node и, следовательно, необходимо решить, куда он должен идти. В коде Dynami Le-Savard эти случаи просто игнорируются.
Тем не менее, выбор дизайна для ограничения дочернего node только перемещается внутри своих родительских узлов. Сбор узлов: выбор дизайна, который может быть идеально подходит для одного решения.
Аналогичным образом, это выбор дизайна, чтобы заставить пользователя выбрать node и контекстный щелчок, чтобы получить контекстное меню, которое позволяет выбирать перемещение вверх или вниз каждый раз, когда они хотят его переместить: это не выбор дизайна, который я бы сделал: я бы использовал drag-and-drop здесь или кнопки, которые позволяют многократное быстрое перемещение любого выбранного node в любом месте дерева.
Кстати, мне нравится использование расширений Dynami Le-Savard.
Ответ 3
Здесь представлено решение, позволяющее перетаскивать узлы туда, где вы хотите. Чтобы переместить node на тот же уровень, что и другой node, просто удерживайте нажатой клавишу при перемещении node. Это действительно простой способ пойти по сравнению с альтернативами и их потенциальными проблемами. Пример был написан с более поздней версией .Net(4.5).
Примечание. Убедитесь, что AllowDrop = true в элементе управления treeview, иначе вы не сможете удалить узлы.
/// <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 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);
}