Locking a Control from Another Thread in Visual Basic
I am developing a parser application to build a call tree from DDL files that have been extracted from a database. The idea is to take a large number of these DDL files and determine exactly what calls what. To do this I am using a .NET TreeView. The final output I am working toward is something like this:
-Proc1
-Proc2
-Proc3
-Proc4
-Proc2
-Proc3
-Proc3
-Proc4
Now, all of my parsing is working correctly. Though, this process is quite lengthy. As such I have decided to move all of the heavyweight processing onto its own thread. Everything is working as intended until I get to the point where I need to update my TreeView. I am attempting to keep all of the actual update logic on the separate thread and only update the TreeView. However, despite having my main form SyncLocked, I am still getting an exception when I try to access the tree.
I have found quite a few examples online that show how to use Delegates for thread-safe access but, unfortunately, they are all a bit simplistic for my needs. Most simply show how to set a text property. As I mentioned before, I am trying to keep as much of the processing as possible on the worker thread and only call the respective TreeView methods to update as this process can be quite long winded (hundreds of procedures to parse and display at a time).
Is there a good way to do this or should I just take my lumps and pass my entire dependency tree back to my main form?
Here is the code I am currently using to display the first level of dependencies. Remember, this will eventually be recursive (currently in "get it working" mode) which is why I want to keep it off the UI thread:
Public Sub updateTreeView()
Dim arrNodeList As ArrayList
Dim childNode As clsProcedureNode
Dim currentNode As clsProcedureNode
Dim intChildIndex As Integer
Dim intNodeListIndex As Integer
Dim treeView As TreeView
//Lock main form
SyncLock mMainForm
//Check that we are actually running on a seprate thread
If mMainForm.InvokeRequired() = True Then
//Call delegate to get handle to TreeView
treeView = mMainForm.Invoke(mGetTreeViewDelegate)
//Add Parsed array to main form TreeView
For intNodeListIndex = 0 To mProcedureNodes.Length - 1
//Get current node and its child list
currentNode = mProcedureNodes(intNodeListIndex)
arrNodeList = currentNode.getProcsCalled()
//Add node and all children to TreeView
With treeView
.BeginUpdate()
.Nodes.Add(currentNode.getNam开发者_如何学JAVAe())
For intChildIndex = 0 To arrNodeList.Count
childNode = arrNodeList.Item(intChildIndex)
.Nodes(intNodeListIndex).Nodes.Add(childNode.getName())
Next
.EndUpdate()
End With
Next
End If
End SyncLock
End Sub
Why don't you use BackgroundWorker? It's way easier to use than Invoke and synchronization. In fact, it was designed to ease things like this.
You can do your background work in DoWork
event handler which runs on other thread and safely interact with you UI in ProgressChanged
event handler which runs on UI thread. The need for invoking ProgressChanged
is signaled in DoWork
using ReportProgress
method of BackgroundWorker
.
Well, the only place to safely manipulate UI controls is the UI thread. This is so in WinForms and also in WPF. You can either pass back the data to the UI thread and populate the whole tree there or you do something like this (pseudo code):
Sub updateView()
foreach item in data
mainForm.AddToTreeView(item);
Thread.Sleep(1);
Doing that on a separate thread should avoid overloading the UI thread but it might take a long time to populate. AddToTreeView needs to invoke the add on the UI thread.
精彩评论