WPF TreeView via Thread updaten

  • VB.NET

    WPF TreeView via Thread updaten

    Hallo Leute, ich habe folgendes Problem: Ich will ein TreeViewItem zu einem TreeView-Control von einem anderen (STA)Thread hinzufügen, dies funktioniert aber trotz invoke nicht. Ich schreibe gerade ein kleines Programm, das GCF bzw NCF Datein entpacken soll. Zunächst will ich den Verzeichnissbaum des Archives in einem TreeView darstellen. Alle anderen sachen wie das Manipulieren der Progressbar etc funktioniern einwandfrei.

    Hier die Fehlermeldung: "The calling thread cannot access this object because a different thread owns it."

    Die Dll, die ich verwendet habe findet ihr hier: [VB.NET] CacheLib - GCF/NCF Library

    Mein code:

    VB.NET-Quellcode

    1. Private Sub Open_Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Open_Button.Click
    2. Try
    3. Dim a As String = FrmConstr.FileBrowser()
    4. If a IsNot Nothing Then
    5. 'C ist eine Klasse in der ich alle Laufenden Threads manage
    6. If C.LoaderInstance Is Nothing Then
    7. C.LoaderInstance = New Worker.Loader(a, DecryptionKey_TextBox.Text) 'initialisiere neue instanz des threads
    8. Else
    9. If C.LoaderInstance.IsRunning = True Then
    10. C.LoaderInstance.Abort()
    11. End If
    12. 'events entfernen falls vorhanden
    13. RemoveHandler C.LoaderInstance.EnableControls, AddressOf EnableControlls
    14. RemoveHandler C.LoaderInstance.DirectoriesLoaded, AddressOf DirectoryTreeLoaded
    15. RemoveHandler C.LoaderInstance.ProgressIntermediate, AddressOf ProgressBarIsIntermediateChanged
    16. C.LoaderInstance = New Worker.Loader(a, DecryptionKey_TextBox.Text)
    17. End If
    18. 'events zur instanz hinzufügen
    19. AddHandler C.LoaderInstance.EnableControls, AddressOf EnableControlls
    20. AddHandler C.LoaderInstance.DirectoriesLoaded, AddressOf DirectoryTreeLoaded
    21. AddHandler C.LoaderInstance.ProgressIntermediate, AddressOf ProgressBarIsIntermediateChanged
    22. Try
    23. 'Thread department auf STA setzen... dies ist notwendig damit ich im Thread WPF controlls erstellen kann...sonst bekomme ich ne böse exception
    24. C.LoaderInstance.Thread.TrySetApartmentState(System.Threading.ApartmentState.STA)
    25. C.LoaderInstance.Start()
    26. Catch ex As System.Threading.ThreadStateException
    27. C.LoaderInstance.Abort()
    28. C.LoaderInstance = Nothing
    29. End Try
    30. End If
    31. Catch ex As Exception
    32. LableText(ex.Message, Status_Lable)
    33. End Try
    34. End Sub


    Nun die Worker-Klasse wo die arbeit passiert:

    VB.NET-Quellcode

    1. Public Class Worker
    2. 'threadwrapper
    3. Public MustInherit Class ThreadWrapper
    4. Public ReadOnly Thread As System.Threading.Thread
    5. Public Sub New()
    6. Me.Thread = New System.Threading.Thread(AddressOf Me.StartTask)
    7. End Sub
    8. Public Overridable Sub Start()
    9. _IsRunning = True
    10. Me.Thread.Start()
    11. End Sub
    12. Public Overridable Sub [Stop]()
    13. Me.Thread.Abort()
    14. End Sub
    15. Private _IsCompleted As Boolean = False
    16. Private _IsRunning As Boolean
    17. Public ReadOnly Property IsCompleted() As Boolean
    18. Get
    19. Return _IsCompleted
    20. End Get
    21. End Property
    22. Public ReadOnly Property IsRunning() As Boolean
    23. Get
    24. Return _IsRunning
    25. End Get
    26. End Property
    27. Private Sub StartTask()
    28. _IsCompleted = False
    29. DoTask()
    30. _IsCompleted = True
    31. End Sub
    32. Protected MustOverride Sub DoTask()
    33. End Class
    34. Public Class Loader
    35. Inherits ThreadWrapper
    36. Public Event EnableControls(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.Bool)
    37. Public Event DirectoriesLoaded(ByVal senders As Object, ByVal e As UpdateProgressEventArgs.DirectoryTreeItem)
    38. Public Event ProgressIntermediate(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.Bool)
    39. Public Sub New(ByVal FilePath As String, ByVal DecryptionKey As String)
    40. _filepath = FilePath
    41. _key = DecryptionKey
    42. End Sub
    43. Public Sub Clear()
    44. Try
    45. _filepath = Nothing
    46. _key = Nothing
    47. Catch
    48. End Try
    49. End Sub
    50. Private _key As String
    51. Private _filepath As String
    52. Protected Overrides Sub DoTask()
    53. RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(True))
    54. RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(False))
    55. 'SteamCache ist eine DLL die ich geschrieben hab, ist auch hier im Forum zu finden
    56. 'Mountedfile ist eine GCF/NCF datei
    57. If _key.Length = 32 Then
    58. C.MountedFile = New SteamCache(_filepath, False, False, _key)
    59. Else
    60. C.MountedFile = New SteamCache(_filepath, False, False)
    61. End If
    62. 'mount the file
    63. C.MountedFile.Mount(FileAccess.Read, FileShare.Read)
    64. 'Build the directory-tree
    65. If C.MountedFile.IsMounted = True Then
    66. If C.MountedFile.CacheFileType = SteamCache.CacheType.GameCacheFile Then
    67. 'hier wird ein neues item erstellt, das erste(root) item, dieses wird dann an die GetAllSubDirectories methode weitergegeben (byRef)
    68. Dim root As New TreeViewItem
    69. root.Header = "\"
    70. GetAllSubDirectories(C.MountedFile.MountedGCF.FileMap(0), 0, root)
    71. 'und wenn alle items(Ordner) hinzugefügt wurden wird der event ausgelöst damit es zum TreeView hinzugefügt wird
    72. RaiseEvent DirectoriesLoaded(Me, New UpdateProgressEventArgs.DirectoryTreeItem(root))
    73. ElseIf C.MountedFile.CacheFileType = SteamCache.CacheType.NonCacheFile Then
    74. Dim root As New TreeViewItem
    75. root.Header = "\"
    76. GetAllSubDirectories(C.MountedFile.MountedNCF.FileMap(0), 0, root)
    77. RaiseEvent DirectoriesLoaded(Me, New UpdateProgressEventArgs.DirectoryTreeItem(root))
    78. End If
    79. End If
    80. RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(False))
    81. RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(True))
    82. End Sub
    83. Private Sub GetAllSubDirectories(ByVal FObject As SteamCache.FileObject.GcfFileObject, ByVal StartIndex As Integer, ByRef StartItem As TreeViewItem)
    84. If FObject.Attribute <> SteamCache.Manifest.Nodes.NodeAttribute.Directory Then
    85. Exit Sub
    86. End If
    87. For i As Integer = StartIndex To C.MountedFile.MountedGCF.Manifest.Nodes.Count - 1
    88. If C.MountedFile.MountedGCF.Manifest.Nodes(i).ParentIndex = FObject.NodeIndex Then
    89. If C.MountedFile.MountedGCF.Manifest.Nodes(i).Attributes = FObject.Attribute Then ' directory only
    90. 'wäre der Thread MTA anstatt STA würde die folgende Zeile nicht funktionieren
    91. Dim child As New TreeViewItem
    92. child.Header = C.MountedFile.MountedGCF.FileMap(i).FileName
    93. child.DataContext = i
    94. GetAllSubDirectories(C.MountedFile.MountedGCF.FileMap(i), i, child)
    95. StartItem.Items.Add(child)
    96. End If
    97. End If
    98. Next
    99. End Sub
    100. Private Sub GetAllSubDirectories(ByVal FObject As SteamCache.FileObject.NcfFileObject, ByVal StartIndex As Integer, ByRef StartItem As TreeViewItem)
    101. If FObject.Attribute <> SteamCache.Manifest.Nodes.NodeAttribute.Directory Then
    102. Exit Sub
    103. End If
    104. For i As Integer = StartIndex To C.MountedFile.MountedNCF.Manifest.Nodes.Count - 1
    105. If C.MountedFile.MountedNCF.Manifest.Nodes(i).ParentIndex = FObject.NodeIndex Then
    106. If C.MountedFile.MountedNCF.Manifest.Nodes(i).Attributes = FObject.Attribute Then ' directory only
    107. Dim child As New TreeViewItem
    108. child.Header = C.MountedFile.MountedNCF.FileMap(i).FileName
    109. child.DataContext = i
    110. GetAllSubDirectories(C.MountedFile.MountedNCF.FileMap(i), i, child)
    111. StartItem.Items.Add(child)
    112. End If
    113. End If
    114. Next
    115. End Sub
    116. Public Sub Abort()
    117. Finalize()
    118. End Sub
    119. Protected Overrides Sub Finalize()
    120. Clear()
    121. RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(False))
    122. RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(True))
    123. End Sub
    124. End Class
    125. End Class


    So und jetzt zum Problem-Code:

    VB.NET-Quellcode

    1. Delegate Sub TreeViewDlg(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
    2. Private Sub DirectoryTreeLoaded(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.DirectoryTreeItem)
    3. AddTreeViewItem(e.Value, DirectoryTree)
    4. End Sub
    5. Public Sub AddTreeViewItem_(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
    6. Try
    7. ControlTreeView.Items.Clear() 'diese Zeile funktioniert komischer weise noch, erst wenn ich ein element(TreeViewItem) das im Thread erstelltwurde hinzufügen will (nächste Zeile) bekomme ich die Fehlermeldung
    8. ControlTreeView.Items.Add(item)
    9. Catch ex As Exception
    10. 'hier bekomme ich folgende Fehlermeldung: "The calling thread cannot access this object because a different thread owns it."
    11. End Try
    12. End Sub
    13. Public Sub AddTreeViewItem(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
    14. Try
    15. If ControlTreeView.Dispatcher.CheckAccess Then
    16. ControlTreeView.Items.Clear()
    17. ControlTreeView.Items.Add(C.DirectoryTree)
    18. Else
    19. Dim d As New TreeViewDlg(AddressOf AddTreeViewItem_)
    20. Me.Dispatcher.Invoke(d, New Object() {item, ControlTreeView})
    21. End If
    22. Catch ex As Exception
    23. End Try
    24. End Sub


    wie kann ich ein Control-Element (TreeViewItem) das nicht im Besitzer-Thread erstellt wurde zu einem Control (TreeView) hinzufügen?

    danke für eure hilfe!