Переместить узел в дереве вверх или вниз - PullRequest
17 голосов
/ 05 февраля 2010

Какой самый точный способ перемещения узла вверх и вниз в виде дерева. Я получил контекстное меню на каждом узле, и выбранный узел должен быть перемещен со всеми его подузлами.

Я использую C # .Net 3.5 WinForms

Ответы [ 3 ]

38 голосов
/ 05 февраля 2010

Вы можете использовать следующие расширения:

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.

8 голосов
/ 05 февраля 2010

Хотя я считаю, что написание этого кода - пустая трата времени, учитывая отсутствие реакции на комментарии со стороны 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;
            }
        }
    }
}

Конечно, это исправление по-прежнему не учитывает тот факт, что в примере кода, что несколько корневых узлов не могут быть перемещены (поскольку они «без родителей»): это легко исправить.

Также это не относится к более интересному случаю, когда перемещение вверх по верхнему дочернему узлу означает, что вы делаете некоторую интерпретацию того, куда должен идти этот «продвигаемый» дочерний код: точно такой же «стратегический выбор» используется там, где вы «двигаетесь вниз». последний дочерний узел родительского узла и, следовательно, должен решить, куда он должен идти. В коде Dynami Le-Savard: эти случаи просто игнорируются.

Однако design-choice ограничивает перемещение дочерних узлов только в пределах своих родительских узлов. Коллекция узлов: выбор конструкции, который может идеально подходить для одного решения.

Аналогично, это выбор дизайна , чтобы заставить пользователя выбрать узел и щелкнуть по контексту, чтобы получить контекстное меню, в котором можно выбрать перемещение вверх или вниз каждый раз, когда он хочет чтобы переместить его : я бы не выбрал дизайн: я бы использовал перетаскивание или кнопки, которые позволяют повторять быстрое перемещение любого выбранного узла в любом месте дерева.

Кстати, мне нравится, что Dynami Le-Savard использует здесь расширения.

1 голос
/ 25 января 2018

Вот решение, которое позволяет вам перетаскивать узлы куда угодно. Чтобы переместить узел на тот же уровень, что и другой узел, просто удерживайте нажатой клавишу 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); 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...