Baumstruktur im Treeview-Steuerelement erstellen

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Baumstruktur im Treeview-Steuerelement erstellen

    Hallo!

    Wie kann ich zur Laufzeit eine Baumstruktur in einem Treeview-Steuerelement erstellen?

    Dabei sollen aus einer Ini-Datei Struktur-Daten ausgelesen werden und eine entsprechende Baumstruktur erstellt werden.

    Der Datensatz ist etwa so aufgebaut:

    Baum1=Getränk|Wasser
    Baum2=Getränk|Limonade
    Baum3=Schokolade
    Baum4=Früchte|Obst|Apfel
    Baum5=Früchte|Obst|Birne

    Der Vertikalstrich soll einen neuen Unterknoten erstellen.

    VB.NET-Quellcode

    1. For j = 1 To TreeView1.Nodes.Count
    2. If TreeView1.Nodes(j - 1).Text = Knoten Then
    3. Vorhanden = True
    4. KnotenNr = j - 1
    5. Exit For
    6. End If
    7. Next


    Dieser Code scheint bereits existierende Unterknoten nicht zu durchlaufen. Außerdem soll geprüft werden, ob der Unterknoten weitere Unterknoten enthält, die durchlaufen werden müssten.

    Erst wenn nichts gefunden wurde, soll der Knoten, bzw. Unterknoten erstellt werden.
    Hi, Selter98

    Wie wäre es mit einer "For Each" schleife?

    Damit kann die arbeit mit den Indexen/Zahlen wegfallen.

    "TreeNode.ChildNodes" ist die Aufzählung der Kinderelemente.
    Ein "ausgwähltes" Element wiederum kann in eine Variable abgelegt werden und abermals mit einer Schleife durchlaufen werden.


    VB.NET-Quellcode

    1. For Each rootNode In TreeView1.Nodes
    2. If rootNode.Text = Knoten Then 'Vergleich auf Knotenname z.B. "Obst"
    3. Vorhanden = True
    4. KnotenNr = TreeView1.Nodes.IndexOf(rootNode) 'hier wird der Nullbasierte Index ermittelt
    5. For Each choosenGoldenChildNode In rootNode.Nodes 'hier keine Notwendigkeit einer Variable, aber zugriff auf das KindElement
    6. Debug.WriteLine("Ich bin Groot! Ja du bist ein abkömmling... " & choosenGoldenChildNode.Text)
    7. Next
    8. Exit For 'Falls gewünscht kann hier ein ContinueFor stehen, damit wird weiter durchlaufen
    9. End If
    10. Next


    Wenn eine Baumstrukur verwendt wird, kann mit LINQ die arbeit mit den Element stark erleichtert werden.
    Besonders die "Rekursion" in der Baumstuktur ist mit LINQ sehr einfach zu Implementieren.
    Im obigen beispiel wird ja nur das Kind, und nicht die Enkel oder jüngere angesprochen... (Verdammter Jugendwahn)

    Hoffe das da oben läuft und läuft und läu....... :rolleyes:

    c.u. Joshi aus HH

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Joshi“ () aus folgendem Grund: Falsches Member angegeben: .Children ist jetzt .Nodes für den zugriff auf die Kinderelemente.

    Die Zeile

    VB.NET-Quellcode

    1. ​For Each choosenGoldenChildNode In rootNode.Children


    führt zu folgendem Fehler:
    Ein Ausnahmefehler des Typs "System.MissingMemberException" ist in Microsoft.VisualBasic.dll aufgetreten.
    Der öffentliche Member Children für den Typ TreeNode wurde nicht gefunden.
    Na da läuft nur meine Nase... Männooo!

    Ich habe das nicht gezielt getestet, daher eher aus dem Kopf.

    Es soll nicht "rootNode.Children" sondern "rootNode.Nodes" sein.

    Verdammter Sauron, der ist an allem schuld. Ich war das nicht...

    c.u. Joshi

    Selter98 schrieb:

    Microsoft.VisualBasic.dll
    Schmeiß mal solch Zeugs aus Deinem Projekt raus.
    Visual Studio - Empfohlene Einstellungen
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    Irgendetwas scheint bei der Erstellung der Knoten noch nicht zu stimmen.

    Hinzugefügt wurde folgende Zeile:

    VB.NET-Quellcode

    1. ​Unternr = TreeView1.Nodes(KnotenNr).Nodes.IndexOf(rootNode)


    Hier der Code für die Erstellung der Knoten:

    VB.NET-Quellcode

    1. If Not Vorhanden Then
    2. Select Case Instanz
    3. Case 1
    4. TreeView1.Nodes.Add(Kette)
    5. Case 2
    6. TreeView1.Nodes(KnotenNr).Nodes.Add(Kette)
    7. Case 3
    8. TreeView1.Nodes(KnotenNr).Nodes(UnterNr).Nodes.Add(Kette)
    9. End Select
    10. End If


    Bei der Erstellung eines Knotens in dritter Instanz tritt folgender Fehler auf:
    Ein Ausnahmefehler des Typs "System.ArgumentOutOfRangeException" ist in System.Windows.Forms.dll aufgetreten.
    Das angegebene Argument liegt außerhalb des gültigen Wertebereichs.

    In diesem Fall trägt die Variable UnterNr den Wert -1.

    Selter98 schrieb:

    Baum1=Getränk|Wasser
    Baum2=Getränk|Limonade
    Baum3=Schokolade
    Baum4=Früchte|Obst|Apfel
    Baum5=Früchte|Obst|Birne

    Der Vertikalstrich soll einen neuen Unterknoten erstellen.
    Das ist eine leidlich gängige Darstellung von Baumstrukturen in Text-Dateien.
    Erfordert allerdings ziemlich trickreichen Code, um daraus den Baum wieder aufzubauen.

    Ich hab da iwo was vergleichbares, müsste ich allerdings anpassen.
    Hast du inzwischen Visual Studio - Empfohlene Einstellungen umgesetzt?
    Weil sonst wäre ich eher minder motiviert, da Entwicklungszeit reinzustecken.

    Also wenn ja, poste mal ein komplettes Beispiel der Datei - am besten gleich ein ganzes Projekt anhängen, wo ich das Einlesen nur noch einfügen muss.

    Ups - soo gängig ist das doch nicht.
    Was nicht passt ist, dass es da scheints 5 verschiedene Bäume gibt - dabei willst du doch nur einen baum erstellen.
    Der Datensatz müsste also besser so aussehen:

    Quellcode

    1. Baum=Getränk|Wasser
    2. Baum=Getränk|Limonade
    3. Baum=Schokolade
    4. Baum=Früchte|Obst|Apfel
    5. Baum=Früchte|Obst|Birne
    oder so ähnlich

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Der Inhalt der Ini-Datei ist wie folgt:

    [Material]
    Stufe7=Bruchrechnen|Brüche darstellen
    Stufe8=Bruchrechnen|Brüche addieren
    Stufe9=Bruchrechnen|Brüche subtrahieren
    Stufe10=Bruchrechnen|Brüche multiplizieren
    Stufe11=Bruchrechnen|Brüche dividieren
    Stufe13=Dezimalzahlen
    Stufe14=Schriftliches Rechnen|Schriftliche Addition
    Stufe15=Schriftliches Rechnen|Schriftliche Subtraktion
    Stufe16=Schriftliches Rechnen|Schriftliche Multiplikation
    Stufe17=Schriftliches Rechnen|Schriftliche Division
    Stufe19=Maßeinheiten|Metrische Maßeinheiten
    Stufe20=Maßeinheiten|Angloamerikanische Maßeinheiten
    Stufe21=Maßeinheiten|Unregelmäßige Maßeinheiten
    Stufe22=Maßeinheiten|Zeiteinheiten
    Stufe24=Prozentrechnen
    Stufe25=Dreisatz
    Stufe26=Dreisatz|Antiproportionaler Dreisatz
    Stufe27=Dreisatz|Verschachlteter Dreisatz
    Stufe29=Mathematische Spielereien|Dreieckszahlen
    Stufe30=Mathematische Spielereien|Quadratzahlen
    Stufe31=Mathematische Spielereien|Sechseckszahlen
    Stufe32=Mathematische Spielereien|Primzahlen
    Stufe33=Mathematische Spielereien|Binärzahlen
    Stufe35=Geometrie|Flächen|Quadrat
    Stufe36=Geometrie|Flächen|Rechteck
    Stufe37=Geometrie|Flächen|Raute
    Stufe38=Geometrie|Flächen|Parallelogramm
    Stufe39=Geometrie|Flächen|Dreieck
    Stufe40=Geometrie|Flächen|Trapez
    Stufe41=Geometrie|Flächen|Kreis
    Stufe42=Geometrie|Flächen|Ellipse

    Und sobald es Einträge für die dritte Instanz gibt, bricht das Programm ab, da es nicht weiß, wo die Einträge 35 bis 42 eingeordnet werden sollen.

    Hier nochmal der komplette Code:

    VB.NET-Quellcode

    1. ​ Ini.Pfad = My.Application.Info.DirectoryPath & "\data\matheass.ini"
    2. TreeView1.Nodes.Clear()
    3. For i = 1 To 42
    4. Verzeichnis = Ini.WertLesen("Material", "Stufe" & i)
    5. Knoten = ""
    6. Kette = ""
    7. If Verzeichnis <> "" Then
    8. RestKette = Verzeichnis
    9. Instanz = 1
    10. Do
    11. p = InStr(RestKette, "|")
    12. 'If Kette <> "" Then Knoten = Kette
    13. If p <> 0 Then
    14. Kette = Mid(RestKette, 1, p - 1)
    15. RestKette = ZeichenLöschen(RestKette, 1, p)
    16. Else
    17. Kette = RestKette
    18. End If
    19. Vorhanden = False
    20. 'KnotenNr = -1
    21. For Each rootNode In TreeView1.Nodes
    22. If rootNode.Text = Kette Then 'Vergleich auf Knotenname z.B. "Obst"
    23. Vorhanden = True
    24. KnotenNr = TreeView1.Nodes.IndexOf(rootNode) 'hier wird der Nullbasierte Index ermittelt
    25. UnterNr = TreeView1.Nodes(KnotenNr).Nodes.IndexOf(rootNode) 'hier wird der Nullbasierte Index ermittelt
    26. For Each choosenGoldenChildNode In rootNode.nodes 'hier keine notwendigkeit einer Variable, aber zugriff auf das KindElement
    27. Debug.WriteLine("Ich bin Groot! Ja du bist ein abkömmling... " & choosenGoldenChildNode.Text)
    28. Next
    29. Exit For 'Falls gewünscht kann hier ein ContinueFor stehen, damit wird weiter durchlaufen
    30. End If
    31. Next
    32. If Not Vorhanden Then
    33. Select Case Instanz
    34. Case 1
    35. TreeView1.Nodes.Add(Kette)
    36. Case 2
    37. TreeView1.Nodes(KnotenNr).Nodes.Add(Kette)
    38. Case 3
    39. TreeView1.Nodes(KnotenNr).Nodes(UnterNr).Nodes.Add(Kette)
    40. End Select
    41. End If
    42. Instanz = Instanz + 1
    43. Loop Until p = 0
    44. End If
    45. Next
    @Selter98 Da war noch eine Hausaufgabe offen:

    RodFromGermany schrieb:

    Schmeiß mal solch Zeugs aus Deinem Projekt raus.
    Visual Studio - Empfohlene Einstellungen

    ErfinderDesRades schrieb:

    Hast du inzwischen Visual Studio - Empfohlene Einstellungen umgesetzt?

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    VB-Fragen über PN / Konversation werden ignoriert!
    Ich habe nun Option Strict auf On gesetzt. Aber nun scheint das ganze Projekt fehlerhaft zu sein.

    Zeichenketten werden nicht mehr in Zahlen umgewandelt:

    VB.NET-Quellcode

    1. ​Richtig = Val(Ini.WertLesen(Benutzer, "Richtig"))

    Mit der Begründung "Option Strict On" lässt keine Impliziten Konvertierungen von "Double" in "Integer" zu.

    Es werden keine Ganzzahlige Divisionen durchgeführt:

    VB.NET-Quellcode

    1. ​Ganze = Int(Zähler / Nenner)

    Mit der Begründung "Option Strict On" lässt keine Impliziten Konvertierungen von "Double" in "Integer" zu.

    Auch diese Berechnungen werden nicht mehr durchgeführt:

    VB.NET-Quellcode

    1. ​ Lösung = z * (z + 1) / 2
    2. AnfangX = Me.Width / 2 - 16
    3. AnfangY = AnfangY + 32 * 0.866

    Mit der Begründung "Option Strict On" lässt keine Impliziten Konvertierungen von "Double" in "Integer" zu.

    Es können keine Daten und Uhrzeiten gesetzt werden:

    VB.NET-Quellcode

    1. ​DateTimePicker1.Value = "30.12.1899 00:00:00"

    Mit der Begründung "Option Strict On" lässt keine Impliziten Konvertierungen von "String" in "Date" zu.

    Insgesamt sind 589 solcher Fehler aufgetreten.
    Option Strict On ist einfach Mist. Da funktioniert gar nichts mehr. Ich werde diese Funktion wieder rückgängig machen.
    Nein nein nein. Option Strict On ist kein Mist.
    Hast du dir durchgelesen wofür das gut ist?

    Alle deine Fehler die im Projekt sind bügelt der Compiler auf Vermutung aus und dadurch können viele viele Fehler entstehen.
    Alle deine Fehler sind schlicht weg ein Grund dafür das es zu anderen Fehlern kommen kann.

    Deshalb sollte man immer Option Strict On setzten und Typsicher programmieren.
    Du rechnest halt überall eigentlich mit falschen Datentypen und der Compiler "konvertiert" diese nach Vermutung richtig/falsch.

    Dein Datum kannst du z.b. mit DateTime.TryParse richtig Konvertieren.

    Aber da gibt es nu noch soviel leider muss ich jetzt weg aber ich denke mal der @RodFromGermany wird dir da gleich ne längere Antwort geben :)
    Grüße , xChRoNiKx

    Nützliche Links:
    Visual Studio Empfohlene Einstellungen | Try-Catch heißes Eisen
    Ich habe das Problem erstmal so gelöst:

    VB.NET-Quellcode

    1. Select Case Instanz
    2. Case 1
    3. Vorhanden = False
    4. For j = 1 To TreeView1.Nodes.Count
    5. If TreeView1.Nodes(j - 1).Text = Knoten(1) Then
    6. Vorhanden = True
    7. Exit For
    8. End If
    9. Next
    10. If Not Vorhanden Then TreeView1.Nodes.Add(Knoten(1))
    11. Case 2
    12. For j = 1 To TreeView1.Nodes.Count
    13. If TreeView1.Nodes(j - 1).Text = Knoten(1) Then
    14. KnotenNr(1) = j - 1
    15. Exit For
    16. End If
    17. Next
    18. Vorhanden = False
    19. For j = 1 To TreeView1.Nodes(KnotenNr(1)).Nodes.Count
    20. If TreeView1.Nodes(KnotenNr(1)).Nodes(j - 1).Text = Knoten(2) Then
    21. Vorhanden = True
    22. Exit For
    23. End If
    24. Next
    25. If Not Vorhanden Then TreeView1.Nodes(KnotenNr(1)).Nodes.Add(Knoten(2))
    26. Case 3
    27. For j = 1 To TreeView1.Nodes.Count
    28. If TreeView1.Nodes(j - 1).Text = Knoten(1) Then
    29. KnotenNr(1) = j - 1
    30. Exit For
    31. End If
    32. Next
    33. For j = 1 To TreeView1.Nodes(KnotenNr(1)).Nodes.Count
    34. If TreeView1.Nodes(KnotenNr(1)).Nodes(j - 1).Text = Knoten(2) Then
    35. KnotenNr(2) = j - 1
    36. Exit For
    37. End If
    38. Next
    39. Vorhanden = False
    40. For j = 1 To TreeView1.Nodes(KnotenNr(1)).Nodes(KnotenNr(2)).Nodes.Count
    41. If TreeView1.Nodes(KnotenNr(1)).Nodes(KnotenNr(2)).Nodes(j - 1).Text = Knoten(3) Then
    42. Vorhanden = True
    43. Exit For
    44. End If
    45. Next
    46. If Not Vorhanden Then TreeView1.Nodes(KnotenNr(1)).Nodes(KnotenNr(2)).Nodes.Add(Knoten(3))
    47. End Select


    Mit jeder weiteren Instanz muss die Abfrage nach der Knotennummer aus der vorherigen wiederholt werden. Und zum Schluss wird abgefragt, ob der Knoten der jeweiligen Instanz existiert.

    Kann man das irgendwie abkürzen?
    Hallo @Selter98
    Ich weis nicht, ob ich dein ursprüngliches Problem richtig verstanden habe, aber hier meine Lösung.
    Class IniFile

    VB.NET-Quellcode

    1. Public Class IniFile
    2. Inherits Dictionary(Of String, Dictionary(Of String, String))
    3. Public Sub Load(filename As String)
    4. Dim sectionName As String = String.Empty
    5. Using reader As New System.IO.StreamReader(filename, System.Text.Encoding.Default)
    6. While Not reader.EndOfStream
    7. Dim line As String = reader.ReadLine
    8. If line.StartsWith("[") AndAlso line.EndsWith("]") Then
    9. sectionName = line.Substring(1, line.Length - 2)
    10. If Not ContainsKey(sectionName) Then
    11. Add(sectionName, New Dictionary(Of String, String))
    12. End If
    13. ElseIf line.StartsWith(";") Then
    14. ElseIf line.Contains("=") Then
    15. Dim idx As Integer = line.IndexOf("="c)
    16. Item(sectionName).Item(line.Substring(0, idx)) = line.Substring(idx + 1, line.Length - idx - 1)
    17. End If
    18. End While
    19. End Using
    20. End Sub
    21. End Class

    Class Form1

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim strIniFile As String = "Pfad zu Ini-Datei"
    3. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    4. Dim f As New IniFile
    5. f.Load(strIniFile)
    6. Dim n As TreeNode
    7. For Each key As String In f.Keys
    8. n = TreeView1.Nodes.Add(key, key)
    9. For Each entry As String In f(key).Keys
    10. Dim SubKeyValues As String() = f(key)(entry).Split("|"c)
    11. If Not TreeView1.Nodes.Find(key, True)(0).Nodes.ContainsKey(SubKeyValues(0)) Then
    12. n = TreeView1.Nodes.Find(key, True)(0).Nodes.Add(SubKeyValues(0), SubKeyValues(0))
    13. End If
    14. If SubKeyValues.Length > 1 Then
    15. For x As Integer = 1 To SubKeyValues.Length - 1
    16. If Not TreeView1.Nodes.Find(SubKeyValues(x - 1), True)(0).Nodes.ContainsKey(SubKeyValues(x)) Then
    17. n = TreeView1.Nodes.Find(SubKeyValues(x - 1), True)(0).Nodes.Add(SubKeyValues(x), SubKeyValues(x))
    18. End If
    19. Next
    20. End If
    21. Next
    22. Next
    23. End Sub
    24. End Class
    wie gesagt, grundsätzlich ists ein Standard-Task, allerdings codemässig durchaus trickreich.

    Das Grundsätzliche: Es werden vollständige Pfade eingelesen, um daraus eine Baumstruktur zu bilden (es gäbe auch annere Möglichkeiten, Bäume abzuspeichern).
    Der Algorithmus zum Einfügen eines solchen Pfades in den Baum zerfällt dabei in 2 Phasen:
    1. Zunächst sucht ein Such-Algo pfad-segment-weise im Baum, welche Nodes bereits vorhanden sind.
    2. Kommt er an den Node, welcher keinen dem nächsten Segment entsprechenden Child-Node enthält, so ist der 2. Algo anzuwenden, welcher den Rest des Pfades als Node-Kette in den zuvor gefundenen Node einhängt.

    Ich habe dafür eine TreenodeCollection.Approach-Extension geschrieben, die den ersten Algo ausführt ("approach"=sich nähern). Sie gibt ein ApproachResult zurück, mit allerlei Informationen über den gefundenen Node, nämlich, ob ühaupt einer gefunden wurde, und wieviele Pfad-Segmente noch übrig sind (oder keines: IsFullMatch), und dergleichen.
    Ausserdem kann das ApproachResult den RestPfad auch selbst einhängen. Genutzt wird das so:

    VB.NET-Quellcode

    1. For Each line In File.ReadAllLines(_FileListData.FullName)
    2. With TreeView1.Nodes.Approach(line.Split("\"c))
    3. If Not .IsFullMatch Then .InsertRest()
    4. End With
    5. Next
    Für diesen einfachen Anwendungsfall wären die vielen Informationen der ApproachResult-Klasse eiglich garnet nötig, aber oft benötigt man sone "Annäherungs-Suche" auch zu anneren Zwecken, als nur um den Rest-Pfad einzuhängen.

    Des weiteren kann die Anwendung auch ChildNodes zählen, den Tree abspeichern, oder auch die "Baum-Datei" überschreiben mit Ordner-Pfaden, die aus dem Dateisystem gelesen werden.

    Den TE wird der Code vmtl. überfordern, aber annere Coder könnten ihn evtl. ganz interessant finden.
    Dateien

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „ErfinderDesRades“ ()