Переместить 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); 
}