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:
Nun die Worker-Klasse wo die arbeit passiert:
So und jetzt zum Problem-Code:
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!
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
- Private Sub Open_Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Open_Button.Click
- Try
- Dim a As String = FrmConstr.FileBrowser()
- If a IsNot Nothing Then
- 'C ist eine Klasse in der ich alle Laufenden Threads manage
- If C.LoaderInstance Is Nothing Then
- C.LoaderInstance = New Worker.Loader(a, DecryptionKey_TextBox.Text) 'initialisiere neue instanz des threads
- Else
- If C.LoaderInstance.IsRunning = True Then
- C.LoaderInstance.Abort()
- End If
- 'events entfernen falls vorhanden
- RemoveHandler C.LoaderInstance.EnableControls, AddressOf EnableControlls
- RemoveHandler C.LoaderInstance.DirectoriesLoaded, AddressOf DirectoryTreeLoaded
- RemoveHandler C.LoaderInstance.ProgressIntermediate, AddressOf ProgressBarIsIntermediateChanged
- C.LoaderInstance = New Worker.Loader(a, DecryptionKey_TextBox.Text)
- End If
- 'events zur instanz hinzufügen
- AddHandler C.LoaderInstance.EnableControls, AddressOf EnableControlls
- AddHandler C.LoaderInstance.DirectoriesLoaded, AddressOf DirectoryTreeLoaded
- AddHandler C.LoaderInstance.ProgressIntermediate, AddressOf ProgressBarIsIntermediateChanged
- Try
- 'Thread department auf STA setzen... dies ist notwendig damit ich im Thread WPF controlls erstellen kann...sonst bekomme ich ne böse exception
- C.LoaderInstance.Thread.TrySetApartmentState(System.Threading.ApartmentState.STA)
- C.LoaderInstance.Start()
- Catch ex As System.Threading.ThreadStateException
- C.LoaderInstance.Abort()
- C.LoaderInstance = Nothing
- End Try
- End If
- Catch ex As Exception
- LableText(ex.Message, Status_Lable)
- End Try
- End Sub
Nun die Worker-Klasse wo die arbeit passiert:
VB.NET-Quellcode
- Public Class Worker
- 'threadwrapper
- Public MustInherit Class ThreadWrapper
- Public ReadOnly Thread As System.Threading.Thread
- Public Sub New()
- Me.Thread = New System.Threading.Thread(AddressOf Me.StartTask)
- End Sub
- Public Overridable Sub Start()
- _IsRunning = True
- Me.Thread.Start()
- End Sub
- Public Overridable Sub [Stop]()
- Me.Thread.Abort()
- End Sub
- Private _IsCompleted As Boolean = False
- Private _IsRunning As Boolean
- Public ReadOnly Property IsCompleted() As Boolean
- Get
- Return _IsCompleted
- End Get
- End Property
- Public ReadOnly Property IsRunning() As Boolean
- Get
- Return _IsRunning
- End Get
- End Property
- Private Sub StartTask()
- _IsCompleted = False
- DoTask()
- _IsCompleted = True
- End Sub
- Protected MustOverride Sub DoTask()
- End Class
- Public Class Loader
- Inherits ThreadWrapper
- Public Event EnableControls(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.Bool)
- Public Event DirectoriesLoaded(ByVal senders As Object, ByVal e As UpdateProgressEventArgs.DirectoryTreeItem)
- Public Event ProgressIntermediate(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.Bool)
- Public Sub New(ByVal FilePath As String, ByVal DecryptionKey As String)
- _filepath = FilePath
- _key = DecryptionKey
- End Sub
- Public Sub Clear()
- Try
- _filepath = Nothing
- _key = Nothing
- Catch
- End Try
- End Sub
- Private _key As String
- Private _filepath As String
- Protected Overrides Sub DoTask()
- RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(True))
- RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(False))
- 'SteamCache ist eine DLL die ich geschrieben hab, ist auch hier im Forum zu finden
- 'Mountedfile ist eine GCF/NCF datei
- If _key.Length = 32 Then
- C.MountedFile = New SteamCache(_filepath, False, False, _key)
- Else
- C.MountedFile = New SteamCache(_filepath, False, False)
- End If
- 'mount the file
- C.MountedFile.Mount(FileAccess.Read, FileShare.Read)
- 'Build the directory-tree
- If C.MountedFile.IsMounted = True Then
- If C.MountedFile.CacheFileType = SteamCache.CacheType.GameCacheFile Then
- 'hier wird ein neues item erstellt, das erste(root) item, dieses wird dann an die GetAllSubDirectories methode weitergegeben (byRef)
- Dim root As New TreeViewItem
- root.Header = "\"
- GetAllSubDirectories(C.MountedFile.MountedGCF.FileMap(0), 0, root)
- 'und wenn alle items(Ordner) hinzugefügt wurden wird der event ausgelöst damit es zum TreeView hinzugefügt wird
- RaiseEvent DirectoriesLoaded(Me, New UpdateProgressEventArgs.DirectoryTreeItem(root))
- ElseIf C.MountedFile.CacheFileType = SteamCache.CacheType.NonCacheFile Then
- Dim root As New TreeViewItem
- root.Header = "\"
- GetAllSubDirectories(C.MountedFile.MountedNCF.FileMap(0), 0, root)
- RaiseEvent DirectoriesLoaded(Me, New UpdateProgressEventArgs.DirectoryTreeItem(root))
- End If
- End If
- RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(False))
- RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(True))
- End Sub
- Private Sub GetAllSubDirectories(ByVal FObject As SteamCache.FileObject.GcfFileObject, ByVal StartIndex As Integer, ByRef StartItem As TreeViewItem)
- If FObject.Attribute <> SteamCache.Manifest.Nodes.NodeAttribute.Directory Then
- Exit Sub
- End If
- For i As Integer = StartIndex To C.MountedFile.MountedGCF.Manifest.Nodes.Count - 1
- If C.MountedFile.MountedGCF.Manifest.Nodes(i).ParentIndex = FObject.NodeIndex Then
- If C.MountedFile.MountedGCF.Manifest.Nodes(i).Attributes = FObject.Attribute Then ' directory only
- 'wäre der Thread MTA anstatt STA würde die folgende Zeile nicht funktionieren
- Dim child As New TreeViewItem
- child.Header = C.MountedFile.MountedGCF.FileMap(i).FileName
- child.DataContext = i
- GetAllSubDirectories(C.MountedFile.MountedGCF.FileMap(i), i, child)
- StartItem.Items.Add(child)
- End If
- End If
- Next
- End Sub
- Private Sub GetAllSubDirectories(ByVal FObject As SteamCache.FileObject.NcfFileObject, ByVal StartIndex As Integer, ByRef StartItem As TreeViewItem)
- If FObject.Attribute <> SteamCache.Manifest.Nodes.NodeAttribute.Directory Then
- Exit Sub
- End If
- For i As Integer = StartIndex To C.MountedFile.MountedNCF.Manifest.Nodes.Count - 1
- If C.MountedFile.MountedNCF.Manifest.Nodes(i).ParentIndex = FObject.NodeIndex Then
- If C.MountedFile.MountedNCF.Manifest.Nodes(i).Attributes = FObject.Attribute Then ' directory only
- Dim child As New TreeViewItem
- child.Header = C.MountedFile.MountedNCF.FileMap(i).FileName
- child.DataContext = i
- GetAllSubDirectories(C.MountedFile.MountedNCF.FileMap(i), i, child)
- StartItem.Items.Add(child)
- End If
- End If
- Next
- End Sub
- Public Sub Abort()
- Finalize()
- End Sub
- Protected Overrides Sub Finalize()
- Clear()
- RaiseEvent ProgressIntermediate(Me, New UpdateProgressEventArgs.Bool(False))
- RaiseEvent EnableControls(Me, New UpdateProgressEventArgs.Bool(True))
- End Sub
- End Class
- End Class
So und jetzt zum Problem-Code:
VB.NET-Quellcode
- Delegate Sub TreeViewDlg(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
- Private Sub DirectoryTreeLoaded(ByVal sender As Object, ByVal e As UpdateProgressEventArgs.DirectoryTreeItem)
- AddTreeViewItem(e.Value, DirectoryTree)
- End Sub
- Public Sub AddTreeViewItem_(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
- Try
- 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
- ControlTreeView.Items.Add(item)
- Catch ex As Exception
- 'hier bekomme ich folgende Fehlermeldung: "The calling thread cannot access this object because a different thread owns it."
- End Try
- End Sub
- Public Sub AddTreeViewItem(ByVal item As System.Windows.Controls.TreeViewItem, ByVal ControlTreeView As System.Windows.Controls.TreeView)
- Try
- If ControlTreeView.Dispatcher.CheckAccess Then
- ControlTreeView.Items.Clear()
- ControlTreeView.Items.Add(C.DirectoryTree)
- Else
- Dim d As New TreeViewDlg(AddressOf AddTreeViewItem_)
- Me.Dispatcher.Invoke(d, New Object() {item, ControlTreeView})
- End If
- Catch ex As Exception
- End Try
- 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!