2010-02-04 39 views
15

¿Cuál es la forma más precisa de mover un nodo hacia arriba y hacia abajo en una vista de árbol? Obtuve un menú contextual en cada nodo y el nodo seleccionado debe moverse con todos sus subnodos.Mover nodo en árbol hacia arriba o hacia abajo

estoy usando C# .NET 3.5 Windows Forms

+0

Puede aclarar, Windows Forms o Web Forms :) –

+0

maldición, lo siento olvidó - WinForms – Kai

+0

Un poco más de detalle, por favor: ¿Está utilizando el Windows Forms TreeView estándar? ¿Has considerado arrastrar y soltar para hacer esto? : muchos ejemplos de cómo hacer esto en MSDN, CodeProject, etc.Considere el caso: tiene un nodo "1a" que tiene nodos secundarios: es el primer nodo hijo del nodo "1": selecciona, a través de su menú contextual (?), Para mover el nodo "1a" uno arriba: lo que debería suceder : nada ? el nodo "1a" debería convertirse en un "hermano" del nodo "1"? Si el nodo "1a" se convierte en un "hermano" del nodo "1": ¿debería colocarse encima o debajo del nodo "1" en el TreeView? – BillW

Respuesta

34

Puede usar las siguientes extensiones:

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); 
      } 
     } 
    } 
} 

nodos hijos seguirán sus padres.

EDITAR: Caso agregado que el nodo para mover es una raíz en el TreeView.

+0

Brother Dynami, estoy encantado de haber tomado al menos una cosa que dije en serio :) mejor, – BillW

+1

Para obtener los mejores resultados, agregue 'view.BeginUpdate()', 'view.EndUpdate()' y 'node.TreeView.SelectedNode = node '. –

+2

int index = parent.Nodes.IndexOf (node); esto puede ser reemplazado por 'int index = node.Index;' – David

8

Si bien creo que escribir este código es una pérdida de tiempo, dada la falta de respuesta a los comentarios del OP, lo menos que puedo hacer es mostrar cómo el código del ejemplo de Le-Savard se puede reparar para que varios clics de la opción hacia arriba o hacia abajo en el menú contextual ... suponiendo que el menú contextual no se cierra automáticamente cada vez y el usuario se ve obligado a seleccionar el mismo nodo una y otra vez ... hará lo correcto con el nodo seleccionado de manera correcta , y no crear efectos secundarios de la ONU-previsto:

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; 
      } 
     } 
    } 
} 

por supuesto esta revisión, aún no aborda el hecho de que en el código de ejemplo que múltiples nodos raíz no se pueden mover (ya que son 'sin padres): eso es easiliy fijable.

Tampoco aborda el caso más interesante en el que mover un nodo secundario superior significa hacer una interpretación de dónde debería ir ese código secundario "promocionado": exactamente la misma "elección estratégica" involucra "mover hacia abajo" "el último nodo hijo de un nodo padre y, por lo tanto, se requiere que decida dónde debe ir. En el código de Dynami Le-Savard: estos casos simplemente se ignoran.

Sin embargo, es un diseño-elección para restringir nodo hijo de solamente ser movido dentro de sus nodos padre Nodos colección: una opción de diseño que puede ser perfectamente adecuado para una solución.

Del mismo modo, se trata de una elección diseño puede obligar al usuario a seleccionar un nodo y hacer un clic contextual para obtener un menú contextual que permite una elección de moverse hacia arriba o hacia abajo cada vez que quieren moverlo : esa no es una opción de diseño que yo haría: usaría arrastrar y soltar aquí o botones que permitan la reubicación rápida repetida de cualquier nodo seleccionado en cualquier parte del árbol.

Por cierto, me gusta el uso de extensiones de Dynami Le-Savard aquí.

+0

Hm, interesante. Usted logró deducir, a partir de las 2 líneas vagas de descripción de OP, que: 1-El menú contextual es realmente lo que contiene las funcionalidades MoveUp() y MoveDown(); 2-La solicitud era en realidad mover nodos a través de sus niveles de sangría, en lugar de entre sus hermanos; 3-El usuario del software estaba realmente haciendo la acción de mover los nodos hacia arriba y hacia abajo, en lugar de ser una acción automatizada subyacente. Es decir, en ese punto, ¿por qué detenerse ahí, agreguemos transiciones de fluidos brillantes que muevan suavemente al nodo en su posición, tal vez incluso agreguen un par de saltos y chispas bastante brillantes. –

+0

Hermano Dynami, los dos estamos "usando poderes psíquicos" aquí, dada la falta de respuesta de OP a preguntas razonables sobre lo que pretendían :) Me disculpo si algo de lo que dije arriba parece crítico de su respuesta. El hecho es que su respuesta tendrá un efecto secundario extraño una vez que el nodo movido llegue a la cima de su orden de hermanos, y se repita "mover hacia arriba". Si señalaste o corrigieras tal comportamiento en el código que publiqué, me sentiría feliz y te votaría :) Como dije, me gusta la forma en que usaste los métodos de extensión aquí. ¡Y disfruté de la creatividad poética en tu "envío" de mi respuesta! – BillW

+0

1. seleccionar un movimiento hacia arriba o hacia abajo del nodo a través del menú contextual es la única suposición razonable que puede hacer aquí: por supuesto que activará el código que está "subyacente" (¿dónde más "mentiría"?) . 2. No asumí nada acerca de mover el nodo a través de los niveles de sangría: acabo de plantear la pregunta para intentar que el OP haga una declaración clara sobre exactamente lo que estaban haciendo: planteé el problema: no dije que el problema tenía que ser Dirigido. 3. Con una opción de menú contextual, ¿quién más sino el usuario final tomaría la decisión de mover el nodo hacia arriba o hacia abajo: el Mago de Oz :)? – BillW

0

Aquí hay una solución que le permite arrastrar & colocar nodos donde desee. Para mover un nodo al mismo nivel que otro nodo, solo mantenga presionada la tecla shift cuando deje caer el nodo. Esta es una manera realmente fácil de seguir en comparación con las alternativas y sus posibles problemas. El ejemplo fue escrito con una versión más reciente de .Net (4.5).

Nota: Asegúrese de que AllowDrop = true en el control TreeView, de lo contrario, no podrá colocar nodos.

/// <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); 
} 
Cuestiones relacionadas