Spezielles Problem beim Zufügen von Items zu einer Liste

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Spezielles Problem beim Zufügen von Items zu einer Liste

    Hallo miteinander,

    ich hoffe mal das gilt jetzt nicht als Doppelposting, aber dieses Problem rührt von meinem letzten Posting im WPF-Forum her, ich hab das eigentliche Problem aber ein bisschen separiert, weil das sonst im WPF-Post zu kompliziert wird und zu viel Info auf einmal.

    Ich zerbrech mir jetzt seit Stunden darüber den Kopf wie ich das am besten lösen kann, komm aber irgendwie auf keinen grünen Ast:

    Ich habe eine Klasse Kapitel,die unter anderem eine Property Prefixes As List(of Integer) hat, die ein geparstes Prefix von variabler Länge hält, z.B. 1.2.1 sieht so aus:

    Kapitel.Prefixes(0) = 1
    Kapitel.Prefixes(1) = 2
    Kapitel.Prefixes(2) = 1

    Und z.B. 3:

    Kapitel(0) = 3

    Auch hat diese Klasse eine Property Unterkapitel As List(Of Kapitel), ist also verschachtelt. Und eine Property Ueberschrft As String.

    Dann habe ich eine Kapitels As List(Of Kapitel).

    Zu dieser Liste möchte ich eine Instanz von Kapitel zufügen. Soweit kein Problem. Wenn ich jetzt aber eine Instanz zufüge, die z.B. das Prefix 3 hat, soll gleichzeitig auch eine Instanz mit dem Prefix 1 und Prefix 2 und einer leeren Überschrift erzeugt werden, also alles was unter 3 liegt. Wichtig: Es kann z.B. sein, dass es schon eine Instanz mit dem Prefix 2 exisitert, diese soll beibehalten werden.

    Die Reihenfolge ist egal, ich kann die LIste hinterher sortieren.

    Und wenn ich z.B. eine Instanz mit dem Prefix 3.2.2 zufügen möchte, soll in Kaptitels(2).Unterkategorie(1) eine Instanz mit dem Prefix 3.2.1 mit einer leeren Unterschrift und 3.2.2 mit einer gegebenen Überschrift erzeugt werden.

    Wisst ihr wie ich meine? Ich wäre dankbar für ein bisschen Input.

    Grüsse, kaffee

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „kafffee“ ()

    Du wirfst einiges durcheinander, denke ich, zumindest kann ich keine konsistente Struktur aus deinen Erklärungen ableiten.
    Zeig doch mal die Kapitelklasse

    Verifiziere für mich auch mal folgendes:
    1: 1.2.1 -> Ist der erste Unterabschnitt des zweiten Abschitts von Kapitel 1
    2: Unterabschnitte und Abschnitte sind Kapitel

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

    Haudruferzappeltnoch schrieb:

    Verifiziere für mich auch mal folgendes:
    1: 1.2.1 -> Ist der erste Unterabschnitt des zweiten Abschitts von Kapitel 1
    2: Unterabschnitte und Abschnitte sind Kapitel


    Trifft beides zu, da liegst du richtig.

    Meine Kapitelklasse:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Kapitel
    2. Public Property Prefix As String
    3. Public Property Ueberschrift As String
    4. Private _Inhalt As String
    5. Public Property Inhalt As String
    6. Get
    7. Return _Inhalt
    8. End Get
    9. Set(value As String)
    10. _Inhalt = value
    11. End Set
    12. End Property
    13. Private _BildPfad As String
    14. Public Property BildPfad As String
    15. Get
    16. Return _BildPfad
    17. End Get
    18. Set(value As String)
    19. _BildPfad = value
    20. End Set
    21. End Property
    22. Private _Icon As String
    23. Public Property Icon As String
    24. Get
    25. Return _Icon
    26. End Get
    27. Set(value As String)
    28. _Icon = value
    29. End Set
    30. End Property
    31. Private _UnterKapitel As New List(Of Kapitel)
    32. Public Property UnterKapitel As List(Of Kapitel)
    33. Get
    34. Return _UnterKapitel
    35. End Get
    36. Set(value As List(Of Kapitel))
    37. _UnterKapitel = value
    38. End Set
    39. End Property
    40. Public ReadOnly Property Prefixes As List(Of Integer)
    41. Get
    42. Dim strPrefixes() As String
    43. Dim Separator() As String = {"."}
    44. strPrefixes = Prefix.Split(Separator, StringSplitOptions.None)
    45. Dim retPrefixes As New List(Of Integer)
    46. For Each Eintrag In strPrefixes
    47. retPrefixes.Add(CInt(Eintrag))
    48. Next
    49. Return retPrefixes
    50. End Get
    51. End Property
    52. Public Sub New(argPrefix As String, argUeberschrift As String, argInhalt As String, argBildPfad As String, argIcon As String)
    53. Prefix = argPrefix
    54. Ueberschrift = argUeberschrift
    55. Inhalt = argInhalt
    56. BildPfad = argBildPfad
    57. Icon = argIcon
    58. End Sub


    Mir ist gerade eben noch diese Idee hier eingefallen, das ist mal ein Anfang, aber so 100%ig ist das noch nicht... muss mal weiter testen wann es sich wie verhält:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub AddeEintrag(ByRef Ebene As List(Of Kapitel), argPrefixes As List(Of Integer))
    2. Dim VorhandenePrefixes As New List(Of Integer)
    3. For i = 0 To Ebene.Count - 1
    4. VorhandenePrefixes.Add(i + 1)
    5. Next
    6. For j = 1 To argPrefixes(argPrefixes.Count - 1) - 1
    7. If Not VorhandenePrefixes.Contains(j) Then
    8. Ebene.Add(New Kapitel(CStr(j), "<nicht festgelegt>", "", "", "")) 'ich glaube hier ist noch ein Fehler im ersten Argument des Konstruktors, das klappt nur richtig wenn argPrefixes.Count = 1 ist;
    9. End If
    10. Next
    11. Ebene.Add(New Kapitel(Prefix, Ueberschrift, Inhalt, BildPfad, Icon))
    12. End Sub


    Wird so aufgerufen:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Select Case Prefixes.Count
    2. Case 1 'Hauptkapitel
    3. AddeEintrag(Kapitels, Prefixes)
    4. Case 2 'Unterkapitel erster Ebende
    5. AddeEintrag(Kapitels(Prefixes(0) - 1).UnterKapitel, Prefixes)
    6. '...usw.
    7. End Select


    _______________________________________________________________________________________

    Edit: Ich glaub ich hab die Lösung schon. Aber gut das hab ich heut und gestern schon fünf Mal geglaubt. Muss ich mal noch ausgiebiger testen:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub AddeEintrag(ByRef Ebene As List(Of Kapitel), argPrefixes As List(Of Integer))
    2. Dim strPrefix As String = ""
    3. For Each number In argPrefixes
    4. strPrefix += CStr(number) & "."
    5. Next
    6. Dim VorhandenePrefixes As New List(Of Integer)
    7. For i = 0 To Ebene.Count - 1
    8. VorhandenePrefixes.Add(i + 1)
    9. Next
    10. For j = 1 To argPrefixes(argPrefixes.Count - 1) - 1
    11. If Not VorhandenePrefixes.Contains(j) Then
    12. Ebene.Add(New Kapitel(strPrefix.Substring(0, strPrefix.Length - 2) & CStr(j), "<nicht festgelegt>", "", "", ""))
    13. End If
    14. Next
    15. Ebene.Add(New Kapitel(Prefix, Ueberschrift, Inhalt, BildPfad, Icon))
    16. End Sub


    Manchmal hilft es schon das Problem mal schriftlich niederzulegen...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()

    Die Prefix Geschichte als String und als Integer Liste finde ich bissel grob. Sowas müsste man wahrscheinlich schon als eigene Klasse weiter abkapseln. Die Ebene würde ich benennen, damit man sich nicht verhaspelt.
    Z.B.:

    VB.NET-Quellcode

    1. Public Class Kapitel
    2. Public Property Prefix As Prefix
    3. ...
    4. End Class
    5. Public Class Prefix
    6. Private _Items As Integer()
    7. Public ReadOnly Property Ebene As Ebene
    8. Default Public ReadOnly Property Items(ebene As Ebene) As Integer
    9. Get
    10. Return _Items(ebene - 1)
    11. End Get
    12. End Property
    13. Public Sub New(from As String)
    14. _Items = from.Split("."c, StringSplitOptions.RemoveEmptyEntries).Select(AddressOf Integer.Parse).ToArray
    15. _Ebene = DirectCast(_Items.Length, Ebene)
    16. End Sub
    17. Public Overrides Function ToString() As String
    18. Return String.Join("."c, _Items)
    19. End Function
    20. End Class
    21. Public Enum Ebene
    22. None = 0
    23. Kapitel = 1
    24. Abschnitt = 2
    25. Unterabschnitt = 3
    26. End Enum
    Über ToString kriegste die entsprechende Zeichenfolge, und per Default Property kannst du z.b. sagen: Gib mir die "Abschnitt"-Nummer. Beides deutlich verständlicher zu händeln, meiner Ansicht nach und ordentlich schreibschützbar.

    Das Hinzufügen von Kapiteln in eine Kapitelliste hat eigentlich nichts in der Kapitel-Klasse zu suchen.
    Da brauchst vielleicht eine Custom-Collection oder besser die Hinzufüge-Logik liegt im aufrufenden Kontext. An der Stelle: Was hat denn Kapitel? Z.B. ein Buch; also hätte die Buch Klasse die Aufgabe Kapitel zu ihrer Kapitelliste hinzuzufügen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Hey danke für die Antwort :)

    Ob ich das jetzt nochmal in eine eigene Klasse abkapsel weiss ich nicht, für mich ist das so wie ich s jetzt habe deutlich und lesbar und ich habe wirklich lange, lange gebraucht um mir das so auszudenken. Aber das ein oder andere werde ich sicherlich übernehmen:

    (1) Das mit String.Join und Overrides ist für mich neu, aber hab intuitiv verstanden wies funktioniert.
    (2) Auch das mit den Enums finde ich super, das mach ich dann wahrscheinlich so (es wird ein Programm zum Erstellen eines Quick Start Guides für mein Projekt):

    VB.NET-Quellcode

    1. Public Enum Ebene
    2. [Tab] = Kapitels
    3. Kapitel = Kapitels(Prefixes(0) - 1).UnterKapitel
    4. Abschnitt = Kapitels(Prefixes(0) -1).UnterKapitel(Prefixes(1) - 1).UnterKapitel
    5. End Enum


    Oder kann man einem Enum nur Konstanten zufügen und keine variablen Klassen?

    (3) Das mit dem from.Split ist mir auch neu, aber lässt sich verwenden denke ich:
    Wo kommt das from denn her? Heisst das er soll den Wert vor dem Gleichheitszeichen nehmen? Und wozu dann das c?
    Und was macht das .Select(AddressOf Integer.Parse). Filtert das alle nicht numerischen Zeichen aus?

    Ich denke auch daran meine Prefixes schon beim Eingeben durch den User zu validieren, weil wär blöd wenn das Programm während der Arbeit crasht und alles wäre weg...

    VB.NET-Quellcode

    1. Private _Prefixes As String
    2. Public Property Prefixes As String 'Durch den User eingegebenes Prefix zum Adden der Kapitel
    3. Get
    4. Return _Prefixes
    5. End Get
    6. Set(value As String)
    7. If value.IsNumeric then
    8. _Prefixes = value
    9. Else
    10. MessageBox.Show("Beim Prefix sind nur Zahlen und Punkte erlaubt. Bitte gebe hier keine Buchstaben oder sonstige Zeichen ein")
    11. End If
    12. End Set
    13. End Property


    Haudruferzappeltnoch schrieb:

    Das Hinzufügen von Kapiteln in eine Kapitelliste hat eigentlich nichts in der Kapitel-Klasse zu suchen.


    Ist es auch nicht. Kapitel ist eine reine Datenklasse, alles Andere ist in anderen Klassen.


    Wenn dich näher interessiert was ich da mache, ich werde mein kleines Projekt wahrscheinlich die nächsten Tage in den SourceCode-Austausch einstellen.

    Man kann damit ein JSON-File erstellen, das die Daten von z.B. einer Bedienungsanleitung hält und während dem Editieren in Echtzeit in einem TreeView und einer Detailansicht anzeigen lassen.

    Die Klasse(n) mit der Echtheitsansicht lassen sich dann auch 1:1 in Projekt einbinden.

    PS: Bei deinem Code in Zeile 8 müsste es glaube ich heissen:

    Public ReadOnly Property _Ebene As Ebene

    So far :)

    kafffee schrieb:

    Oder kann man einem Enum nur Konstanten zufügen und keine variablen Klassen?
    Ein Enum ist nur eine Benennung von Zahlen (Integer). Also ja nur Konstanten. Liegt einfach daran, dass bei 7 Auswahlmöglichkeiten sich keiner merken will, was jetzt hinter der Zahl 5 steckt. Deswegen schreibt mans dran.
    Ausnahme: Flagged Enums die bieten noch die Möglichkeit mehrere Zahlen gleichzeitig zu überprüfen.

    kafffee schrieb:

    Wo kommt das from denn her?

    from ist das Argument für die Sub New der Prefix Klasse, ein String z.b. "1.2.1", das kann ich auch abC33 nennen.

    kafffee schrieb:

    Und wozu dann das c?
    Wenn du ein c hinter einen String machst ist es ein Char. Anderer Datentyp, Voraussetzung natürlich der String darf nur ein Zeichen haben. Split() Funktioniert mit String und Char. Aber je nach Framework gibt es da unterschiedliche Funktionsweisen, so hab ichs in Erinnerung. Mit Char kann man da nix falsch machen.
    Sowas nennt sich Datentyp Literal. Bei Single ist es zum Beispiel das !.

    Enumerable.Select ist LINQ, diese Methoden nehmen in den meisten Fälle u.a. einen Delegaten als Argument entgegen.
    AddressOf ist eine Möglichkeit für auf der aktuellen Ebene zugängliche Funktionen einen Delegaten bereitzustellen, solange die Signatur für das entsprechende LINQ geeignet ist.
    Das Ergebnis von Split ist eine Liste an Strings. Select erstellt eine neue Liste, nimmt jeden String einzeln, schiebt ihn durch die Integer.Parse Funktion (macht den String also zu einer Zahl) und setzt das Ergebnis in die neue Liste.
    So als Schleife ist dasselbe, bis auf ein paar LINQ Feinheiten:

    VB.NET-Quellcode

    1. Dim strings = from.Split("."c, StringSplitOptions.RemoveEmptyEntries)
    2. Dim integers(strings.Length - 1) as Integer
    3. For i = 0 To strings.Length - 1
    4. integers(i) = Integer.Parse(strings(i))
    5. Next


    kafffee schrieb:

    PS: Bei deinem Code in Zeile 8 müsste es glaube ich heissen:
    Ne, automatisch generierte Eigenschaften haben entsprechend automatisch ein Feld mit dem Namen _NameDerProperty. Probier aus.
    Ebene ist die Property _Ebene ist das zugehörige Feld. Beide existieren in meinen Code, _Ebene indirekt.

    Syntaktisch äqiuvalent:

    VB.NET-Quellcode

    1. Public Property Ebene as Integer

    VB.NET-Quellcode

    1. Private _Ebene as Integer
    2. Public Property Ebene as Integer
    3. Get
    4. Return _Ebene
    5. End Get
    6. Set(value as Integer)
    7. _Ebene = value
    8. End Set
    9. End Property

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Haudruferzappeltnoch schrieb:

    Ne, automatisch generierte Eigenschaften haben entsprechend automatisch ein Feld mit dem Namen _NameDerProperty.


    Ah okay, deswegen streicht er mir das auch immer rot an, wenn anfange einzutippen...:

    VB.NET-Quellcode

    1. Private _Ebene as Integer
    2. Public Property Ebene as Integer
    3. '...


    ...und das geht dann wieder weg, sobald ich Getter und Setter definiere...