Frage

Ich habe eine WinForm TreeView-Steuerelement, das die Eltern-Kind-Beziehung von CaseNotes zeigt (ich weiß, dass nichts den meisten von Ihnen bedeutet, aber es hilft mir die Antworten zu visualisieren).

Ich habe eine Datatable der CaseNotes, die ich angezeigt werden müssen. Das Eltern- / Kind ist wie folgt definiert: Wenn die Zeile ein ParentNoteID hat, dann ist es ein childNode dieser Note sonst ist es ein rootNode ist. Es könnte auch ein Elternteil Note (aber kein rootNode), wenn eine andere Zeile es hat ID, wie es ParentNoteID ist.

(vielleicht vereinfachen) die Dinge zu komplizieren habe ich die unten arbeiten (meistens) Code, der den Knoten abwechselnd Farben. Ich habe manuell eine statische Sammlung für den treeview und Farben sie ziemlich richtig. Jetzt brauche ich, um dynamisch die Knoten aus meiner Datentabelle zu füllen.

Da ich bereits durch Strukturansichts Knoten für Knoten werde sollte ich nicht in der Lage sein, die Daten in diesen Prozess irgendwie anhängen? Vielleicht muss ich zuerst die Knoten bauen und dann Farbe als separate Routine, aber die Rekursion Methode wäre immer noch gelten, oder?

Lets sagen, ich will für jeden Knoten CaseNoteID anzuzeigen. Das wird in der Datatable zurück und ist einzigartig.

foreach (TreeNode rootNode in tvwCaseNotes.Nodes)
        {
            ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);

        }
protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
    {
        root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;

        foreach (TreeNode childNode in root.Nodes)
        {
            Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;

            if (childNode.Nodes.Count > 0)
            {
                // alternate colors for the next node
                if (nextColor == firstColor)
                    ColorNodes(childNode, secondColor, firstColor);
                else
                    ColorNodes(childNode, firstColor, secondColor);
            }
        }
    }

EDIT

Meine Gedanken / Versuche so weit:

        public void BuildSummaryView()
    {
        tvwCaseNotes.Nodes.Clear();

        DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);
        foreach (var cNote in cNotesForTree.Rows)
        {

            tvwCaseNotes.Nodes.Add(new TreeNode("ContactDate"));
        }
        FormPaint();
    }

Dies ist offensichtlich fehlerhaft. Man kann es nur das Display ContactDate immer und immer wieder. Zugegeben, es zeigt ihm die richtige Anzahl von Zeiten, aber ich würde den Wert von ContactDate (die eine Spalte in der Datenbank mag und in der Datatable zurückgegeben wird. Zweitens muß ich childNode Logik hinzufügen. A if (node.parentNode = node.CaseNoteID) blah...

EDIT 2

Also habe ich diesen Link gefunden, hier , und es macht es scheint, wie ich meine Datatable in eine Arraylist benötigen. Ist das richtig?

EDIT 3

Okay, dank Cerebus dieser funktioniert meistens. Ich habe nur noch eine Frage. Wie kann ich nehme diese ->

DataTable cNotesForTree = CurrentCaseNote.GetAllCNotes(Program._CurrentPerson.PersonID);

und meine zurückDataTable in diesem benutzen? Muss ich dies nur ersetzen ->

    dt = new DataTable("CaseNotes");
dt.Columns.Add("NoteID", typeof(string));
dt.Columns.Add("NoteName", typeof(string));
DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
dc.AllowDBNull = true;
dt.Columns.Add(dc);

// Add sample data.
dt.Rows.Add(new string[] { "1", "One", null });
dt.Rows.Add(new string[] { "2", "Two", "1" });
dt.Rows.Add(new string[] { "3", "Three", "2" });
dt.Rows.Add(new string[] { "4", "Four", null });
dt.Rows.Add(new string[] { "5", "Five", "4" });
dt.Rows.Add(new string[] { "6", "Six", null });
dt.Rows.Add(new string[] { "7", "Seven", null });
dt.Rows.Add(new string[] { "8", "Eight", "7" });
dt.Rows.Add(new string[] { "9", "Nine", "8" });

Meine Verwirrung, glaube ich, ist brauche ich noch die Column.Add und Row.Adds zu tun? Auch würden, wie die Datacolumn zu meiner eigentlichen Datenstruktur übersetzen? Sorry für die sehr unwissend Fragen, die gute Nachricht ist, ich muss nie zweimal bitten.

EDIT 4

Im Folgenden stellt einen Laufzeitfehler.

if (nodeList.Find(FindNode) == null)
  {
    DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
    if (childRows.Length > 0)
    {
      // Recursively call this function for all childRowsl
      TreeNode[] childNodes = RecurseRows(childRows);

      // Add all childnodes to this node.
      node.Nodes.AddRange(childNodes);
    }

    // Mark this noteID as dirty (already added).
    //doneNotes.Add(noteID);
    nodeList.Add(node);
  }

Der Fehler ist wie folgt -> Sie finden nicht Spalte [ea8428e4] , die die ersten 8 Stellen der richtigen NoteID ist (Ich habe einen Guid verwenden). Sollte es für eine Spalte dieses Namens suchen sein ?? Weil ich ein Guid bin mit ist es etwas anderes muss ich tun? Ich änderte alle Referenzen in sich und Ihr Code Guid ...

War es hilfreich?

Lösung

Um zu versuchen, dieses Problem zu lösen, habe ich eine Probe Fenster Form und schrieb den folgenden Code ein. Ich stellte mir die Datatable Design wie folgt:

 NoteID  NoteName  ParentNoteID
   "1"    "One"        null
   "2"    "Two"        "1"
   "3"    "Three"      "2"
   "4"    "Four"       null
...

Dies sollte einen Baum erstellen, wie ( sorry, ich mit ASCII-Kunst bin nicht sehr gut !):

One
 |
 ——Two
 |
 ————Three
 |
Four

Pseudocode lautet wie folgt:

  1. Iteration durch alle Zeilen in der Datentabelle.
  2. Für jede Zeile eine TreeNode erstellen und es Eigenschaften ist. Recursively wiederholen Sie den Vorgang für alle Zeilen, die eine ParentNodeID haben diese Zeile der ID entsprechen.
  3. Jede vollständige Iteration gibt einen Knoten, der alle passenden childnodes mit unendlicher Verschachtelung enthalten.
  4. Fügen Sie den ausgefüllten Nodelist zum TreeView.

Das Problem in Ihrem Szenario ergibt sich aus der Tatsache, der „Fremdschlüssel“ auf eine Spalte in der gleichen Tabelle bezieht. Dies bedeutet, dass, wenn wir durch die Reihen durchlaufen, müssen wir verfolgen, welche Zeilen bereits analysiert worden. Zum Beispiel in der obigen Tabelle, wobei der Knoten der zweiten und dritten Reihen übereinstimmen, sind bereits in der ersten vollständigen Iteration hinzugefügt. Deshalb müssen wir sie nicht wieder hinzufügen. Es gibt zwei Möglichkeiten, den Überblick über diese zu halten:

  1. Achten Sie auf eine Liste von IDs, die durchgeführt wurden (doneNotes). Vor jedem neuen Knoten hinzufügen, überprüfen, ob die noteID in dieser Liste vorhanden ist. Dies ist die schnellere Methode und soll in der Regel bevorzugt werden. ( Diese Methode wird in dem unten stehenden Code kommentiert out )
  2. Für jede Iteration, verwenden Sie ein Prädikat generic Delegaten (FindNode), um die Liste des hinzugefügten Knoten (Buchhaltung für verschachtelten Knoten) zu suchen, um zu sehen, ob die zu hinzugefügt werden Knoten in dieser Liste vorhanden ist. Dies ist die langsamere Lösung, aber ich irgendwie wie kompliziert Code! : P

Ok, hier ist der bewährte Code (C # 2.0):


public partial class TreeViewColor : Form
{
  private DataTable dt;
  // Alternate way of maintaining a list of nodes that have already been added.
  //private List<int> doneNotes;
  private static int noteID;

  public TreeViewColor()
  {
    InitializeComponent();
  }

  private void TreeViewColor_Load(object sender, EventArgs e)
  {
    CreateData();
    CreateNodes();

    foreach (TreeNode rootNode in treeView1.Nodes)
    {
      ColorNodes(rootNode, Color.MediumVioletRed, Color.DodgerBlue);
    }
  }

  private void CreateData()
  {
    dt = new DataTable("CaseNotes");
    dt.Columns.Add("NoteID", typeof(string));
    dt.Columns.Add("NoteName", typeof(string));
    DataColumn dc = new DataColumn("ParentNoteID", typeof(string));
    dc.AllowDBNull = true;
    dt.Columns.Add(dc);

    // Add sample data.
    dt.Rows.Add(new string[] { "1", "One", null });
    dt.Rows.Add(new string[] { "2", "Two", "1" });
    dt.Rows.Add(new string[] { "3", "Three", "2" });
    dt.Rows.Add(new string[] { "4", "Four", null });
    dt.Rows.Add(new string[] { "5", "Five", "4" });
    dt.Rows.Add(new string[] { "6", "Six", null });
    dt.Rows.Add(new string[] { "7", "Seven", null });
    dt.Rows.Add(new string[] { "8", "Eight", "7" });
    dt.Rows.Add(new string[] { "9", "Nine", "8" });
  }

  private void CreateNodes()
  {
    DataRow[] rows = new DataRow[dt.Rows.Count];
    dt.Rows.CopyTo(rows, 0);
    //doneNotes = new List<int>(9);

    // Get the TreeView ready for node creation.
    // This isn't really needed since we're using AddRange (but it's good practice).
    treeView1.BeginUpdate();
    treeView1.Nodes.Clear();

    TreeNode[] nodes = RecurseRows(rows);
    treeView1.Nodes.AddRange(nodes);

    // Notify the TreeView to resume painting.
    treeView1.EndUpdate();
  }

  private TreeNode[] RecurseRows(DataRow[] rows)
  {
    List<TreeNode> nodeList = new List<TreeNode>();
    TreeNode node = null;

    foreach (DataRow dr in rows)
    {
      node = new TreeNode(dr["NoteName"].ToString());
      noteID = Convert.ToInt32(dr["NoteID"]);

      node.Name = noteID.ToString();
      node.ToolTipText = noteID.ToString();

      // This method searches the "dirty node list" for already completed nodes.
      //if (!doneNotes.Contains(doneNoteID))

      // This alternate method using the Find method uses a Predicate generic delegate.
      if (nodeList.Find(FindNode) == null)
      {
        DataRow[] childRows = dt.Select("ParentNoteID = " + dr["NoteID"]);
        if (childRows.Length > 0)
        {
          // Recursively call this function for all childRowsl
          TreeNode[] childNodes = RecurseRows(childRows);

          // Add all childnodes to this node.
          node.Nodes.AddRange(childNodes);
        }

        // Mark this noteID as dirty (already added).
        //doneNotes.Add(noteID);
        nodeList.Add(node);
      }
    }

    // Convert this List<TreeNode> to an array so it can be added to the parent node/TreeView.
    TreeNode[] nodeArr = nodeList.ToArray();
    return nodeArr;
  }

  private static bool FindNode(TreeNode n)
  {
    if (n.Nodes.Count == 0)
      return n.Name == noteID.ToString();
    else
    {
      while (n.Nodes.Count > 0)
      {
        foreach (TreeNode tn in n.Nodes)
        {
          if (tn.Name == noteID.ToString())
            return true;
          else
            n = tn;
        }
      }
      return false;
    }
  }

  protected void ColorNodes(TreeNode root, Color firstColor, Color secondColor)
  {
    root.ForeColor = root.Index % 2 == 0 ? firstColor : secondColor;

    foreach (TreeNode childNode in root.Nodes)
    {
      Color nextColor = childNode.ForeColor = childNode.Index % 2 == 0 ? firstColor : secondColor;

      if (childNode.Nodes.Count > 0)
      {
        // alternate colors for the next node
        if (nextColor == firstColor)
          ColorNodes(childNode, secondColor, firstColor);
        else
          ColorNodes(childNode, firstColor, secondColor);
      }
    }
  }
}

Andere Tipps

Ich habe viel einfachere Erweiterung Methode für TreeView erstellt, an denen die Verwendung von neuen einfachen abgeleitete Klasse, die zwei nützliche Eigenschaften TreeNode hinzufügt.

    internal class IdNode : TreeNode
    {
        public object Id { get; set; }
        public object ParentId { get; set; }
    }

    public static void PopulateNodes(this TreeView treeView1, DataTable dataTable, string name, string id, string parentId)
    {
        treeView1.BeginUpdate();
        foreach (DataRow row in dataTable.Rows)
        {
            treeView1.Nodes.Add(new IdNode() { Name = row[name].ToString(), Text = row[name].ToString(), Id = row[id], ParentId = row[parentId], Tag = row });
        }
        foreach (IdNode idnode in GetAllNodes(treeView1).OfType<IdNode>())
        {
            foreach (IdNode newparent in GetAllNodes(treeView1).OfType<IdNode>())
            {
                if (newparent.Id.Equals(idnode.ParentId))
                {
                    treeView1.Nodes.Remove(idnode);
                    newparent.Nodes.Add(idnode);
                    break;
                }
            }
        }
        treeView1.EndUpdate();
    }

    public static List<TreeNode> GetAllNodes(this TreeView tv)
    {
        List<TreeNode> result = new List<TreeNode>();
        foreach (TreeNode child in tv.Nodes)
        {
            result.AddRange(GetAllNodes(child));
        }
        return result;
    }
    public static List<TreeNode> GetAllNodes(this TreeNode tn)
    {
        List<TreeNode> result = new List<TreeNode>();
        result.Add(tn);
        foreach (TreeNode child in tn.Nodes)
        {
            result.AddRange(GetAllNodes(child));
        }
        return result;
    }

Dank der Modix für seine Methoden alle (verschachtelte) Knoten zu erhalten.

Überprüfen Sie dies:

Public Sub BuildTree(ByVal dt As DataTable, ByVal trv As TreeView, ByVal expandAll As [Boolean])
    ' Clear the TreeView if there are another datas in this TreeView
    trv.Nodes.Clear()
    Dim node As TreeNode
    Dim subNode As TreeNode
    For Each row As DataRow In dt.Rows
        'search in the treeview if any country is already present
        node = Searchnode(row.Item(0).ToString(), trv)
        If node IsNot Nothing Then
           'Country is already present
            subNode = New TreeNode(row.Item(1).ToString())
            'Add cities to country
            node.Nodes.Add(subNode)
        Else
            node = New TreeNode(row.Item(0).ToString())
            subNode = New TreeNode(row.Item(1).ToString())
            'Add cities to country
            node.Nodes.Add(subNode)
            trv.Nodes.Add(node)
        End If
    Next
    If expandAll Then
        ' Expand the TreeView
        trv.ExpandAll()
    End If
End Sub

Für mehr und vollständigen Quellcode: Wie treeview füllen von Datatable in vb.net

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top