Intermediate State für TreeView

  • VB.NET

Es gibt 42 Antworten in diesem Thema. Der letzte Beitrag () ist von Kababär.

    Intermediate State für TreeView

    Hi Leute,

    mittlerweile bin ich echt am verzweifeln.
    Es geht um folgendes: Ein Threeview hat normalerweise einen State, entweder checked oder unchecked.
    Der Einfachheit halber beschränken wir uns in dem Beispiel mal auf ein Parent.Node, welches Child-Nodes hat.
    Sind nun alle Child-Nodes auf checked, wird auch parent auf checked und vice versa. Das Gleiche gilt für unchecked.
    Es gibt allerdings noch einen anderen Status, den Parent annehmen soll: Intermediate. Dieser Status wird erreicht, wenn mindestens ein Child-Node checked und nicht alle Child-Nodes checked sind.
    Das Problem ist, dass sich das Icon für die Checkbox ändern soll wie etwa hier zu sehen:


    Es gibt zwar einige Sample Code wie etwa von CodeGuru, CodeSamples etc., aber diese will ich meiden, da ich kene fremden Quellen implementieren will und ich alles schon selbst geschrieben habe, mir nur noch der letzte Teil fehlt.
    Nach ewig langer Suche bin ich im Internet auf diesen Code gestoßen: activevb.de/cgi-bin/tippupload/show/361/ThreeStateTreeview
    Allerdings weiß ich nicht, wie ich diesen Code implementieren soll bzw. mit meinem Treeview verknüpfen soll. Wie setze ich da ein Node nun auf intermediate?

    Abwägig von diesem Code von der Internetseite habe ich versucht, mit CheckState.Intermediate oder Checked = Nothing zu arbeiten, leider alles erfolgslos.
    Mein derzeitiger Code sieht so aus:

    Quellcode

    1. Imports System.IO
    2. Imports CASHelper
    3. Imports System.Windows.Forms
    4. Imports System.Drawing
    5. Imports System.ComponentModel
    6. ''' <summary>
    7. ''' This class provides a TreeView to exclude packages
    8. ''' </summary>
    9. ''' <remarks></remarks>
    10. Public Class cExcludePackages
    11. Private sTopFolder As String
    12. Public Sub New(ByRef path As String) 'Parameter path from the folder. It's the parent
    13. InitializeComponent()
    14. sTopFolder = path
    15. GetDir(sTopFolder)
    16. End Sub
    17. ''' <summary>
    18. ''' Change states: if a parent node is checked, all subnodes are checked too. If parent is unchecked, all subnodes are unchecked too.
    19. ''' </summary>
    20. ''' <remarks></remarks>
    21. Private Sub TreeView1_AfterCheck(ByVal sender As Object, ByVal e As TreeViewEventArgs) Handles TreeView1.AfterCheck
    22. RemoveHandler TreeView1.AfterCheck, AddressOf TreeView1_AfterCheck
    23. Call CheckAllChildNodes(e.Node)
    24. If e.Node.Checked Then
    25. If e.Node.Parent Is Nothing = False Then
    26. Dim allChecked As Boolean = True
    27. Call IsEveryChildChecked(e.Node.Parent, allChecked)
    28. 'If every child is checked, check the parent too!
    29. If allChecked Then
    30. e.Node.Parent.Checked = True
    31. Call ShouldParentsBeChecked(e.Node.Parent)
    32. End If
    33. End If
    34. Else
    35. 'And vice versa
    36. Dim parentNode As TreeNode = e.Node.Parent
    37. While parentNode Is Nothing = False
    38. parentNode.Checked = False
    39. parentNode = parentNode.Parent
    40. End While
    41. End If
    42. AddHandler TreeView1.AfterCheck, AddressOf TreeView1_AfterCheck
    43. End Sub
    44. Private Sub CheckAllChildNodes(ByVal parentNode As TreeNode)
    45. 'When you start the application, al nodes should already be checked
    46. For Each childNode As TreeNode In parentNode.Nodes
    47. childNode.Checked = parentNode.Checked
    48. CheckAllChildNodes(childNode)
    49. Next
    50. End Sub
    51. Private Sub IsEveryChildChecked(ByVal parentNode As TreeNode, ByRef checkValue As Boolean)
    52. 'Recursive call: are all children checked?
    53. For Each node As TreeNode In parentNode.Nodes
    54. Call IsEveryChildChecked(node, checkValue)
    55. If Not node.Checked Then
    56. checkValue = False
    57. End If
    58. Next
    59. End Sub
    60. Private Sub ShouldParentsBeChecked(ByVal startNode As TreeNode)
    61. 'Determine if parents node should be checked or not
    62. If startNode.Parent Is Nothing = False Then
    63. Dim allChecked As Boolean = True
    64. Call IsEveryChildChecked(startNode.Parent, allChecked)
    65. If allChecked Then
    66. startNode.Parent.Checked = True
    67. Call ShouldParentsBeChecked(startNode.Parent)
    68. Else
    69. 'Actually this should be the intermediate status, but it doesn't work
    70. For Each child As TreeNode In startNode.Nodes
    71. If child.Nodes.Count > 0 And Not allChecked Then
    72. startNode.Parent.Checked = Nothing
    73. Call ShouldParentsBeChecked(startNode.Parent)
    74. End If
    75. Next
    76. End If
    77. End If
    78. End Sub
    79. Private Sub bFinish_Click(sender As Object, e As EventArgs) Handles bFinish.Click
    80. Me.Close()
    81. End Sub
    82. Private Sub GetDir(ByVal dirPath As String)
    83. Dim drives() As String = IO.Directory.GetDirectories(dirPath)
    84. Dim rootDir As String = String.Empty
    85. 'Set the parent nodes
    86. For i As Integer = 0 To drives.Count - 1
    87. rootDir = drives(i)
    88. 'add the nodes
    89. TreeView1.Nodes.Add(Path.GetDirectoryName(rootDir))
    90. PopulateTreeView(rootDir, TreeView1.Nodes(i))
    91. Next
    92. End Sub
    93. Private Sub PopulateTreeView(ByVal dir As String, ByVal parentNode As TreeNode)
    94. Dim folder As String = String.Empty
    95. Try
    96. 'Add the files to treeview
    97. Dim files() As String = IO.Directory.GetFiles(dir)
    98. If files.Length <> 0 Then
    99. Dim fileNode As TreeNode = Nothing
    100. For Each file As String In files
    101. fileNode = parentNode.Nodes.Add(IO.Path.GetFileNameWithoutExtension(file))
    102. fileNode.Checked = True
    103. fileNode.Tag = file
    104. Next
    105. End If
    106. 'Add folders to treeview
    107. Dim folders() As String = IO.Directory.GetDirectories(dir)
    108. If folders.Length <> 0 Then
    109. Dim folderNode As TreeNode = Nothing
    110. Dim folderName As String = String.Empty
    111. For Each folder In folders
    112. folderName = IO.Path.GetFileName(folder)
    113. folderNode = parentNode.Nodes.Add(folderName)
    114. folderNode.Checked = True
    115. folderNode.Tag = folder
    116. PopulateTreeView(folder, folderNode)
    117. Next
    118. End If
    119. 'If something went wrong...
    120. Catch ex As UnauthorizedAccessException
    121. parentNode.Nodes.Add("Access Denied")
    122. End Try
    123. End Sub
    124. End Class


    Zudem noch ein kleines Problemchen: Wenn ich einen Ordner angebe, in dem mehrere Ordner sind, wie zeige ich nur einen bestimmten Ordner im TreeView an? ich will also nicht alle gefundenen Ordner anzeigen, sondern nur einen. Habe es schon mit "if equal" versucht, nachdem ich den Name des Ordners vom Pfad extrahiert habe, klappt leider nicht ganz.
    Ich wäre überaus glücklich, wenn mir jemand helfen kann :)

    Grüße
    also der 3-state-Treeview ist von mir. Mit deinem Treeeview kannsteden nicht verknüpfen- was immer du damit gemeint haben magst. Dein Treeview muß weg, mein Treeview muss aus der Toolbox stattdessen aufs Form gezogen werden.
    Selber die Nodes auf CState.Indeterminated zu setzen ist garnet vorgesehen, sondern die Logik, wann ein Node Indeterminated ist (nämlich wenn seine Childs beide anneren States aufweisen) ist bereits eingebaut.
    Ernsthaft? Was ein Zufall, direkt den Mann am langen Hebel zu treffen.
    Es hätte mich auch gewundert, wenn ich das hätte verknüpfen können..
    Wie kriege ich den TreeView in die Toolbox bzw. auf meine Form? Ich benutze Visual Studio 2012 Ultimate.
    Kann ich das überhaupt mit VS12 öffnen? Als ich heute Mittag probiert habe, die vbproj-Datei zu öffnen, stand da, dass die nicht installiert ist.
    Ich installiere gerade mal schnell VS12 aufm Privatrechner und probiere es nochmal.
    Wunderbar, konnte die Fehler beseitigen, die aufgetreten sind. War wohl ein Anpassungsfehler von VS05 auf 12. Mittem im Code stand sowas wie "En" und "t".
    Die TreeView habe ich auch erfolgreich in die Toolbox bekommen, so wie du beschrieben hast.
    Die TreeView macht genau das, was ich will :)

    Kann man noch irgendwie die Smileys und die Icons zum Anhaken editieren? Die Smileys sollten weg und die Knarre zum Anzeigen der zurzeit selektierten Node müsste ich noch entfernen. Verrätst du mir wie das geht? :)

    Und statt Knoten bräuchte die die Namen der Dateien und Ordner.. :/

    Aber echt Respekt für die Arbeit! :) Ist ein straffes Projekt! ;)

    Die Frage kurz formuliert: Wie bekomme ich diese Logik deiner Anwendung in eine neue Anwendung rein bzw. übertrage sie auf ein neues Treeview?

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Kababär“ ()

    Es ist halt ein Treeview, nicht mehr und nicht weniger.
    die Bildle hab ich vergessen, wie das gemacht ist. Vermutlich ist das ebenso gemacht, wie man es in einem normalen Treeview auch machen würde.
    Auch das Befüllen aus dem Dateisystem ändert sich um keinen Deut, ob man nun den Standard- oder meinen TV nimmt.

    Zum Dateisystem habich übrigens ein wirklich mordsmäßiges Projekt gemacht, mit LazyLoading im NebenThread und so Sperenzchen: Ordnerstruktur im Treeview
    Vermutlich oversized für deinen Bedarf.

    jdfs. die Befüllung ist eine annere Frage, da mach besser einen eigenen Thread zu auf. Vmtl. hat ja jmd. schon was dazu.

    Kababär schrieb:

    Die Smileys sollten weg und die Knarre zum Anzeigen der zurzeit selektierten Node müsste ich noch entfernen. Verrätst du mir wie das geht?
    jetzt hab ich nochmal geguckt: Ja, das ist Standard-Treeview-Handling: Dem Treeview die ImageList-Property setzen, und dann einen ImageIndex und einen SelectedImageIndex.
    Macht man alles im Designer.
    Update: Ich schreibe es nun doch selbst, weil mir das implementieren etwas Probleme verursacht.
    Ich bin nun soweit gekommen, dass ich eine ImageList erstellt habe, diese dem TV zugewiesen habe. Von Start an wird ein leeres Kästchen von meiner ImageIndex angezeigt. Ist in Ordnung.
    Später implementiere ich noch die Logik, wann welches Kästchen gewählt werden soll, aber muss ich erst wissen, wie ich dieses Icon als Checkbox funktionieren lasse..
    Wie mache ich das? Der Rest dürfte einfach sein..
    Habs hinbekommen, die ImageList als Checkbox zu verwenden.. geht relativ einfach: Treeview1.StateImageList = ImageList1 wenn Checkboxes auf true gesetzt ist.
    Jetzt muss ich noch an der Logik rumfeilen, dann sollte alles funktionieren, dankeschön :)
    Genau das ist mein Vorhaben :) Im Prinzip ist es ganz einfach wenn ich mir das mal so überlege.
    ImageList laden und an die StateImageList hängen, ein Enum für die 3-States definieren und jedem State ein Bildchen zuweisen und dann die Logik einbauen.
    Nach 2 Tagen bin ich jetzt auch mal dahintergekommen.. im Netz findet man dazu gar nichts.. nur Sample Codes wie auf Codeproject und .dll, was für einen Anfänge wie mich nicht zu gebrauchen ist.
    Zum Glück bin ich auf dich und deinen Code gestoßen ;D
    Jetzt muss ich doch nochmal fragen.
    Wenn ich ein Enum so wie du es hast erstelle und die imagelist der stateimagelist zuweise:
    1. Wie realisiere ich, dass sich das Icon für die Checkbox nach jedem Klick auf die Checkbox ändert?
    2. Wie verbinde ich den Status des normalen Treeviews (2) mit meinem Enum (3)?
    Genau, das klingt doch super. Nur an der Umsetzung hapert es. Brauche wohl nicht zu erwähnen, dass ich ein Anfänger bin.
    Denn wie erreiche ich überhaupt den Indeterminated State? Vielleicht ist es ein Bug, aber ich konnte herausfinden, dass man ihm per Doppelklick auf die Checkbox erreicht, wenn die Checkbox = Checked ist. Das ist ja aber nicht Sinn der Sache.. als ich in den Debugger ging, um den StateImageIndex abzufragen, kam jedoch immer -1 raus. Also StateImageIndex(-1) für Checked und Unchecked.
    Folgende Funktion wollte ich benutzen, um die Reihenfolge der Bilder zu definieren:

    Quellcode

    1. Private Sub CheckState(ByVal nd As TreeNode)
    2. Dim node As TreeNode = nd
    3. Dim state As CState
    4. If node.StateImageIndex = CState.Checked Then
    5. state = CState.Indeterminated
    6. End If
    7. If node.StateImageIndex = CState.Indeterminated Then
    8. state = CState.Unchecked
    9. End If
    10. If node.StateImageIndex = CState.Unchecked Then
    11. state = CState.Checked
    12. End If
    13. End Sub


    Dieser Code ist natürlich Humbug habe ich gemerkt, denn er wird immer nur zwischen Checked und Unchecked hin und her springen.. :D Die If-Statements kommen ja direkt hintereinander, ich Trottel. Deshalb ein Select Case:

    Quellcode

    1. Private Function CheckState(ByVal nd As TreeNode) As CState
    2. Dim node As TreeNode = nd
    3. Dim state As CState
    4. Select node.StateImageIndex
    5. Case 0
    6. state = CState.Indeterminated
    7. node.StateImageIndex += 1
    8. Case 1
    9. state = CState.Unchecked
    10. node.StateImageIndex -= 2
    11. Case -1
    12. state = CState.Checked
    13. node.StateImageIndex += 1
    14. End Select
    15. Return node.StateImageIndex
    16. End Function


    Der StateImageIndex stimmt nun soweit, allerdings will er das Bild des Indeter. nicht annehmen..

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Kababär“ ()

    Kababär schrieb:

    Brauche wohl nicht zu erwähnen, dass ich ein Anfänger bin.
    Lass es.
    Du merkst: Control-Entwicklung ist nicht trivial.
    Interessant, dass du den Bug beim Doppelklick bereits gefunden hast - da haben mich erst annere drauf aufmerksam machen müssen, und ich war schon ganz verzweifelt, weil ich in meim TST den Bug nicht fund.
    bis ich mal drauf kam, dass der Bug bereits im Original-Treeview besteht, und bei meim nur noch bisserl deutlicher rauskommt.

    ErfinderDesRades schrieb:

    Kababär schrieb:

    Brauche wohl nicht zu erwähnen, dass ich ein Anfänger bin.
    Lass es.
    Du merkst: Control-Entwicklung ist nicht trivial.


    Neeee.. jetzt bin ich schon so nah dran, da schmeiß ich das nicht einfach so hin.
    Gerade die Nichtrivialität ist doch das Interessante daran.
    Die Logik ist bereits implementiert, mir fehlt nur noch, dass das Indetermediate-Icon gesetzt wird, wenn der StateImageIndex(SII) auf 1 ist. Zwar stimmt auch da schon der SII, allerdings will er ihn nicht setzen. Vielleicht gehe ich da über die ImageList, wenn das nicht funktioniert, muss ich mir was anderes überlegen.

    Dieser Bug ist lustig. Denn vor der Implementierung der Logik, wann welches Icon gesetzt werden soll, wurde durch ein Doppelklick das Indetermediate-Icon angezeigt, wenn der vorherige Zustand Checked war. Traurigerweise frage ich mich gerade, wieso man nicht solche Bugs fixt..

    Edit: Apropo Bug bzw. Feature bezüglich des Forums hier: Ist man in einem Thread, den man abonniert hat und ihn aktualisiert, bekommt man eine Benachrichtung, dass ein neuer Eintrag in dem Thread erschienen ist, obwohl man die Antwort gelesen hat, weil man ja die Seite aktualisierte. Vielleicht ist das so gewollt?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Kababär“ ()