-
May 4th, 2015, 08:46 PM
#1
Thread Starter
Lively Member
[RESOLVED] Expand Treeview using Background Worker
Hi,
I would like to run the below code in Background worker as it sometimes freezes up. When I run in BGW Dowork I get an illegal cross thread operation. Could someone assist in helping me run the populateChildNodes sub in background worker? I read the article in the Codebank but still unable to get this to work. Any help will greatly be appreciated. Thank in advance
PHP Code:
Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect
If e.Node.Nodes.Count = 0 Then
BackgroundWorker1.RunWorkerAsync()
Call populateChildNodes(e.Node)
End If
End Sub
PHP Code:
Private Sub populateChildNodes(parentNode As TreeNode)
ToolStripProgressBar1.Visible = True
ToolStripStatusLabel1.Visible = True
ToolStripStatusLabel1.Text = "starting..."
Application.DoEvents()
Dim LDAP As DirectoryEntry = CType(parentNode.Tag, DirectoryEntry)
For Each child As DirectoryEntry In LDAP.Children
Dim childName As String = GetName(child.Name)
Dim childNode As New TreeNode(childName)
childNode.Tag = child
parentNode.Nodes.Add(childNode)
Next
parentNode.Expand()
ToolStripProgressBar1.Visible = False
ToolStripStatusLabel1.Text = "Done"
End Sub
PHP Code:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
' How do I run populateChildNodes(parentNode As TreeNode) so it does not give the illegal crossthread operation
End Sub
-
May 5th, 2015, 12:08 AM
#2
Re: Expand Treeview using Background Worker
' How do I run populateChildNodes(parentNode As TreeNode) so it does not give the illegal crossthread operation
You don't. A BackgroundWorker is for background work and anything to do with a control is inherently foreground work, so cannot be done in or from the DoWork event handler of a BackgroundWorker. What you do is separate out the background work and the foreground work and then only do the background work in the DoWork event handler. When that's done, the RunWorkerCompleted event is raised and you can update the control(s) on the UI thread.
So, the steps are as follows:
1. Get any data from the UI that will be required by the background task.
2. Call RunWorkerAsync and pass in the data retrieved in step 1.
3. Handle the DoWork event.
4. Retrieve the data passed in in step 2.
5. Perform the background work using the data retrieved in step 4.
6. Pass out the result of the background work.
7. Handle the RunWorkerCompleted event.
8. Retrieve the data passed out in step 6.
9. Update the UI using the data retrieved in step 8.
For an example, follow the CodeBank link in my signature and check out my thread on Using The BackgroundWorker. In your case, the argument you pass in will be the Tag of the parent node and the result will be an array of TreeNode objects. You can then call AddRange to add that array of nodes to the parent in the RunWorkerCompleted event handler.
-
May 7th, 2015, 05:53 PM
#3
Thread Starter
Lively Member
Re: Expand Treeview using Background Worker
I've been working on it for a couple of days to no avail. The below is the best I came up with. Could someone help in getting this to work. Thanks in advance.
PHP Code:
Private Sub populateChildNodes(parentNode As TreeNode)
ToolStripProgressBar1.Visible = True
ToolStripStatusLabel1.Visible = True
ToolStripStatusLabel1.Text = "Starting..."
Run_ON_BGW(parentNode)
BackgroundWorker1.RunWorkerAsync()
ToolStripProgressBar1.Visible = False
ToolStripStatusLabel1.Text = "Done"
End Sub
PHP Code:
Sub Run_ON_BGW(parentNode As TreeNode)
Dim LDAP As DirectoryEntry = CType(parentNode.Tag, DirectoryEntry)
For Each child As DirectoryEntry In LDAP.Children
Dim childName As String = GetName(child.Name)
Dim childNode As New TreeNode(childName)
childNode.Tag = child
parentNode.Nodes.Add(childNode)
Next
parentNode.Expand()
End Sub
PHP Code:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Run_ON_BGW(parentNode As TreeNode)
Dim nodeList As List(Of TreeNode) = DirectCast(e.Result, List(Of TreeNode))
TreeView1.Nodes.AddRange(nodeList.ToArray())
End Sub
-
May 7th, 2015, 06:15 PM
#4
Re: Expand Treeview using Background Worker
I SPECIFICALLY said that you can't touch controls in the DoWork event handler and your code is adding nodes to the TreeView in the DoWork event handler. I can't see where you've even tried to do what I suggested. Did you check out my examples in the CodeBank? One of them (post #4) shows how to gather items in the DoWork event handler and then add them to a ListBox in the RunWorkerCompleted event handler. Why can't you do the same thing with TreeNodes and a TreeView?
-
May 7th, 2015, 06:44 PM
#5
Thread Starter
Lively Member
Re: Expand Treeview using Background Worker
I did check your CodeBank examples and learn some cool stuff to the point I went back and update some of my older projects. I however am finding this to be a issue. Perhaps I'm overthinking it.
-
May 7th, 2015, 07:43 PM
#6
Re: Expand Treeview using Background Worker
Originally Posted by UCPocoAPoco
I did check your CodeBank examples and learn some cool stuff to the point I went back and update some of my older projects. I however am finding this to be a issue. Perhaps I'm overthinking it.
Perhaps you are, you cannot work with the TreeView at all in the background worker.
Code:
TreeView1.Nodes.AddRange(nodeList.ToArray())
You cannot do anything to it. Your code shows you doing stuff to it. You can only prepare the data, then pass it to the worker completed event. In the worker completed event you can finally work with the treeview.
-
May 7th, 2015, 07:47 PM
#7
Re: Expand Treeview using Background Worker
Originally Posted by UCPocoAPoco
Perhaps I'm overthinking it.
Perhaps the opposite. There is one rule when using a BackgroundWorker: DO NOT touch the UI in the DoWork event handler. If you're breaking that rule then you're not over-thinking anything. Read the words: do not touch the TreeView in the DoWork event handler or any method called from the DoWork event handler. The ONLY thing you should be doing in the DoWork event handler is creating the TreeNodes that you intend to add.
If you need data from the TreeView to do that, get it BEFORE you call RunWorkerAsync and pass it in. My CodeBank thread demonstrates that. If you need to update the TreeView afterwards, pass the data out of the DoWork event handler into the RunWorkerCompleted event handler and update it there. My CodeBank thread demonstrates how to do that.
-
May 15th, 2015, 05:02 PM
#8
Re: Expand Treeview using Background Worker
@UCPocoAPoco
Here is a working list demonstrate how to call PopulateChildNodes from BackgroundWorker1_DoWork, try to adapt this code to work with your project.
Code:
Option Strict On
Option Explicit On
Imports System.IO
Imports System.ComponentModel
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TreeView1.Nodes.Add("C:\").Tag = "C:\"
TreeView1.Nodes.Add("D:\").Tag = "D:\"
End Sub
Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect
BackgroundWorker1.RunWorkerAsync(e.Node.Tag)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
e.Result = PopulateChildNodes(CStr(e.Argument))
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Dim n As List(Of TreeNode) = CType(e.Result, List(Of TreeNode))
TreeView1.SelectedNode.Nodes.AddRange(n.ToArray)
End Sub
Private Function PopulateChildNodes(strPath As String) As List(Of TreeNode)
Dim r As New List(Of TreeNode)
Dim n As TreeNode
Dim Dirs() As String = IO.Directory.GetDirectories(strPath, "*.*", IO.SearchOption.TopDirectoryOnly)
For j = 0 To Dirs.Length - 1
n = New TreeNode(IO.Path.GetFileName(Dirs(j))) ' Show folder name only
n.Tag = Dirs(j) ' Save folder full path in Tag
r.Add(n)
Next
Return r
End Function
End Class
-
May 15th, 2015, 09:29 PM
#9
Re: Expand Treeview using Background Worker
The same list with showing the progress of the background operation.
Code:
Option Strict On
Option Explicit On
Imports System.IO
Imports System.ComponentModel
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TreeView1.Nodes.Add("C:\").Tag = "C:\"
TreeView1.Nodes.Add("D:\").Tag = "D:\"
BackgroundWorker1.WorkerReportsProgress = True
End Sub
Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect
ProgressBar1.Show()
BackgroundWorker1.RunWorkerAsync(e.Node.Tag)
MessageBox.Show("TreeView1 is populating in a Background thread")
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
e.Result = PopulateChildNodes(CStr(e.Argument))
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Dim n As List(Of TreeNode) = CType(e.Result, List(Of TreeNode))
TreeView1.SelectedNode.Nodes.AddRange(n.ToArray)
ProgressBar1.Hide()
End Sub
Private Function PopulateChildNodes(strPath As String) As List(Of TreeNode)
Dim r As New List(Of TreeNode)
Dim n As TreeNode
Dim Dirs() As String = IO.Directory.GetDirectories(strPath, "*.*", IO.SearchOption.TopDirectoryOnly)
Dim t As Integer = Dirs.Length
For j = 0 To Dirs.Length - 1
n = New TreeNode(IO.Path.GetFileName(Dirs(j))) ' Show folder name only
n.Tag = Dirs(j) ' Save folder full path in Tag
r.Add(n)
BackgroundWorker1.ReportProgress(CInt((j / t) * 100))
Threading.Thread.Sleep(100) ' Delay to simulate a more complicated calculation.
Next
Return r
End Function
End Class
-
May 16th, 2015, 11:40 AM
#10
Thread Starter
Lively Member
Re: Expand Treeview using Background Worker
Thanks 4x2y for the sample. However I am having an issue with the below. Been over a week and still can't get it working in a BGW.
PHP Code:
Private Sub RadioButton1_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged
Dim parentNode As New TreeNode("LocationA")
If IsNothing(parentNode) = False Then TreeView1.Nodes.Clear() TreeView1.Nodes.Remove(parentNode) End If
Dim LDAP As New DirectoryEntry("LDAP://company.com")
parentNode.Tag = LDAP TreeView1.Nodes.Add(parentNode) TreeView1.SelectedNode = parentNode
End Sub
PHP Code:
Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect
If e.Node.Nodes.Count = 0 Then
BackgroundWorker1.RunWorkerAsync(e.Node) End If
End Sub
I am getting an error at the do work Error Value of type 'String' cannot be converted to 'System.Windows.Forms.TreeNode'.
PHP Code:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
e.Result = populateChildNodes(CStr(e.Argument))
End Sub
PHP Code:
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Dim n As List(Of TreeNode) = CType(e.Result, List(Of TreeNode)) TreeView1.SelectedNode.Nodes.AddRange(n.ToArray)
End Sub
PHP Code:
Private Sub populateChildNodes(parentNode As TreeNode)
ToolStripStatusLabel1.Text = "Starting..." Application.DoEvents()
Dim LDAP As DirectoryEntry = CType(parentNode.Tag, DirectoryEntry)
For Each child As DirectoryEntry In LDAP.Children
Dim childName As String = GetName(child.Name) Dim childNode As New TreeNode(childName) childNode.Tag = child parentNode.Nodes.Add(childNode)
Next
parentNode.Expand()
End Sub
Last edited by UCPocoAPoco; May 16th, 2015 at 11:46 AM.
-
May 16th, 2015, 02:26 PM
#11
Re: Expand Treeview using Background Worker
Try this, i hope it works for you!
vb Code:
Option Strict On Option Explicit On Public Class Form1 Private Sub TreeView1_AfterSelect(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterSelect If e.Node.Nodes.Count = 0 Then ToolStripStatusLabel1.Text = "Starting..." ' Moved here in order to not touch any UI control in the background thread. BackgroundWorker1.RunWorkerAsync(e.Node.Tag) End If End Sub Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork e.Result = PopulateChildNodes(CType(e.Argument, DirectoryEntry)) End Sub Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted Dim n As List(Of TreeNode) = CType(e.Result, List(Of TreeNode)) TreeView1.SelectedNode.Nodes.AddRange(n.ToArray) ToolStripStatusLabel1.Text = "Finished..." End Sub Private Sub RadioButton1_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged Dim parentNode As New TreeNode("LocationA") If IsNothing(parentNode) = False Then TreeView1.Nodes.Clear() TreeView1.Nodes.Remove(parentNode) End If Dim LDAP As New DirectoryEntry("LDAP://company.com") parentNode.Tag = LDAP TreeView1.Nodes.Add(parentNode) TreeView1.SelectedNode = parentNode End Sub Private Function PopulateChildNodes(LDAP As DirectoryEntry) As List(Of TreeNode) Dim r As New List(Of TreeNode) For Each child As DirectoryEntry In LDAP.Children Dim childName As String = GetName(child.Name) Dim childNode As New TreeNode(childName) childNode.Tag = child r.Add(childNode) Next Return r End Function End Class
-
May 16th, 2015, 03:35 PM
#12
Thread Starter
Lively Member
-
May 16th, 2015, 03:44 PM
#13
Re: Expand Treeview using Background Worker
Originally Posted by UCPocoAPoco
My project is now working beautifully.
Nice to here that,
Final step, mark this thread as RESOLVED
-
May 16th, 2015, 03:50 PM
#14
Thread Starter
Lively Member
Re: Expand Treeview using Background Worker
Sure. How would one do that? . Found it Thanks
How do I do it?
To mark your thread as resolved, find the Thread Tools link on the bar above the first post. Click on and a menu will drop down showing you some options. From these choose "Mark Thread Resolved" which is the bottom-most option.
Last edited by UCPocoAPoco; May 16th, 2015 at 04:06 PM.
-
May 16th, 2015, 06:05 PM
#15
Re: [RESOLVED] Expand Treeview using Background Worker
You could add 1k entrys in 0.1 of a second. So why use a secondary thread? Not that you would add 1k items.
-
May 16th, 2015, 06:54 PM
#16
Thread Starter
Lively Member
Re: [RESOLVED] Expand Treeview using Background Worker
I originally thought the same. If there are OU's that have around 3K workstations or more, it takes about 3 to 18 seconds to populate and freezes upon moving or selecting a different OU. All other OU's takes a few seconds or less to populate. Using a BGW makes a big difference from my testing.
-
May 16th, 2015, 07:08 PM
#17
Re: [RESOLVED] Expand Treeview using Background Worker
18 seconds to populate how many entrys? BGW cant speed up adding of entrys :/
-
May 16th, 2015, 07:56 PM
#18
Thread Starter
Lively Member
Re: [RESOLVED] Expand Treeview using Background Worker
Agree. It helps in two different ways.
1) The form is not unresponsive with OU's that has many computer account.
2) If an OU with many computer accounts is selected inadvertently, you will have to wait for it to populate before selecting a different OU. With the BGW, it does not cause an issue as the GUI is responsive allowing you to select a different OU or as fast as you can press the down arrow\ Up arrow.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|