2010-02-04 8 views
15

Jaki jest najdokładniejszy sposób przesuwania węzła w widoku drzewa. Mam menu kontekstowe na każdym węźle, a wybrany węzeł powinien zostać przeniesiony wraz ze wszystkimi podwęzłami.Przeniesienie węzła w drzewie w górę lub w dół

Używam C# .NET 3.5 WinForm

+0

Czy możesz wyjaśnić, WinForms i WebForms :) –

+0

Cholera, zapomniałem przepraszam - WinForms – Kai

+0

trochę więcej szczegółów proszę: używasz standardowego WinForm TreeView? Czy rozważyłeś przeciąganie i upuszczanie, aby to zrobić? : wiele przykładów tego, jak to zrobić w MSDN, CodeProject itp.Rozważmy przypadek: masz węzeł "1a", który ma węzły potomne: jest to pierwszy węzeł podrzędny węzła "1": wybierasz, za pośrednictwem menu kontekstowego (?), Aby przesunąć węzeł "1a" o jeden: co powinno się stać : nic ? węzeł "1a" powinien stać się "rodzeństwem" węzła "1"? Jeśli węzeł "1a" staje się "rodzeństwem" węzła "1": czy powinien być umieszczony nad lub pod węzłem "1" w TreeView? – BillW

Odpowiedz

34

można wykorzystywać następujące rozszerzenia:

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

węzły potomne będą śledzić ich rodziców.

EDYCJA: Dodano przypadek, w którym węzeł do przeniesienia jest katalogiem głównym w TreeView.

+0

Bracie Dynami, cieszę się, że wziąłeś co najmniej jedną rzecz, którą powiedziałem poważnie :) najlepiej, – BillW

+1

Aby uzyskać najlepsze wyniki, dodaj 'view.BeginUpdate()', 'view.EndUpdate()' i 'node.TreeView.SelectedNode = węzeł '. –

+2

int index = parent.Nodes.IndexOf (node); można to zastąpić przez 'int index = node.Index;' – David

8

Chociaż wydaje mi się, że pisanie tego kodu jest stratą czasu, biorąc pod uwagę brak odpowiedzi na uwagi OP, to przynajmniej mogę pokazać, jak można poprawić przykład kodu Le-Savarda, tak aby wielość kliknięć wybór w menu kontekstowym w górę lub w dół ... zakładając, że menu kontekstowe nie jest automatycznie zamykane za każdym razem, a użytkownik jest zmuszony do wielokrotnego wybierania tego samego węzła ... zrobi to, co należy, z wybranym przez siebie węzłem , a nie tworzyć un-ma skutki uboczne:

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

oczywiście tę poprawkę, jednak nie odnosi się do faktu, że w przykładowym kodzie, że wiele węzłów korzeni nie można przenieść (ponieważ są one „parentless): to easiliy naprawialny.

Nie odnosi się również do bardziej interesującego przypadku, w którym przejście do najwyższego węzła podrzędnego oznacza dokonanie interpretacji miejsca, w którym powinien znajdować się "promowany" kod podrzędny: dokładnie taki sam "wybór strategiczny" ma miejsce w przypadku "przesunięcia w dół" "ostatni węzeł podrzędny węzła nadrzędnego, a zatem wymagane jest podjęcie decyzji, gdzie powinien się udać. W kodzie Dynami Le-Savarda: te przypadki są po prostu ignorowane.

jednak to konstrukcja wyboru ograniczyć węzeł podrzędny z tylko przemieszczane w ich węzły zbiór węzłów macierzystych: wyboru projektu, który może być w pełni nadaje się do jednego rozwiązania.

Podobnie, jest to wybór projekt zmusić użytkownika, aby wybrać węzeł i kontekst kliknij, aby uzyskać menu kontekstowe, które umożliwia wybór przesuwa się w górę lub w dół za każdym razem chcą, aby przenieść go : to nie jest wybór projektu, który bym zrobił: używałbym tu przeciągania i upuszczania lub przycisków, które umożliwiają powtarzalne szybkie przenoszenie dowolnego wybranego węzła w dowolnym miejscu drzewa.

Tak przy okazji, lubię używać rozszerzeń Dynami Le-Savard.

+0

Hm, interesujące. Udało ci się odjąć, z niewyraźnych 2 wierszy opisu OP, że: 1-Menu kontekstowe jest faktycznie rzeczą zawierającą funkcje MoveUp() i MoveDown(); 2 - Żądanie polegało na przeniesieniu węzłów przez ich poziomy wcięcia zamiast ich rodzeństwa; 3 - Użytkownik oprogramowania faktycznie wykonał akcję przesuwania węzłów w górę i w dół, zamiast automatycznego działania. Mam na myśli to, dlaczego tam się zatrzymać, dodajmy lśniące płynne przejścia, które przesuwają węzeł płynnie do pozycji, może nawet dodają kilka odbić i całkiem jasne iskry. –

+0

Bracie Dynami, oboje używamy tutaj "mocy psychicznych", z uwagi na brak odpowiedzi ze strony OP na uzasadnione pytania o to, co zamierzali :) Przepraszam, jeśli cokolwiek, co powiedziałem powyżej, wydaje się krytyczne w stosunku do twojej odpowiedzi. Faktem jest, że twoja odpowiedź będzie miała dziwny efekt uboczny, gdy poruszony węzeł dostanie się na szczyt swojego rodzeństwa, a "ruch w górę" powtarza się. Jeśli wskazałeś lub skorygowałeś takie zachowanie w kodzie, który opublikowałem, byłbym szczęśliwy i zagłosowałbym na ciebie :) Jak już powiedziałem, podoba mi się sposób, w jaki używałeś metod rozszerzeń tutaj! I podobała mi się poetycka kreatywność w twoim "wysłaniu" mojej odpowiedzi! – BillW

+0

1. Wybranie przesuwania lub opuszczania węzła za pomocą menu kontekstowego jest jedynym rozsądnym założeniem, które możesz wykonać tutaj: oczywiście spowoduje to, że kod będzie "ukryty" (gdzie indziej będzie "kłamać"?) . 2. Nie zakładałem nic na przesuwanie węzła przez poziomy wcięć: podniosłem tylko kwestię, aby spróbować uzyskać OP, aby jasno powiedzieć o tym, co robią: poruszyłem kwestię: nie powiedziałem, że problem musi być dodany. 3. Z wyborem menu kontekstowego, kto inny niż użytkownik końcowy dokonałby wyboru przesunięcia węzła w górę lub w dół: Czarodziej z Oz :)? – BillW

0

Oto rozwiązanie, które umożliwia przeciągnięcie węzłów o wartości & w dowolne miejsce. Aby przenieść węzeł na ten sam poziom, co inny węzeł, po prostu przytrzymaj klawisz Shift podczas upuszczania węzła. To naprawdę łatwa droga w porównaniu z alternatywami i ich potencjalnymi problemami. Przykład został napisany z nowszą wersją .Net (4.5).

Uwaga: Upewnij się, i AllowDrop = true na kontroli widoku drzewa, w przeciwnym razie nie można usunąć węzłów.

/// <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); 
} 
Powiązane problemy