Horhe El Fenenado
Horhe El Fenenado

Reputation: 125

How can i invoke a method called from backgroundworker dowork event?

This is the method:

private TreeNode CreateDirectoryNode(string path, string name)
        {
            var directoryNode = new TreeNode(name);
            var directoryListing = GetDirectoryListing(path);

            var directories = directoryListing.Where(d => d.IsDirectory);
            var files = directoryListing.Where(d => !d.IsDirectory);

            foreach (var dir in directories)
            {
                i ++;
                directoryNode.Nodes.Add(CreateDirectoryNode(dir.FullPath, dir.Name));
                int percentage = (i + 1) * 100 / 100;
                backgroundWorker1.ReportProgress(percentage);
            }
            foreach (var file in files)
            {
                directoryNode.Nodes.Add(new TreeNode(file.Name));
            }
            return directoryNode;
        }

Then in background do work:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {            
            var root = txtHost.Text;
            treeView1.Nodes.Clear();            
            treeView1.Nodes.Add(CreateDirectoryNode(root, "root"));           
        }

And the progresschanged:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.toolStripProgressBar2.Value = Math.Min(this.toolStripProgressBar2.Maximum, e.ProgressPercentage);
        }

After 20 seconds of work it throw exception in the DoWork event on the line:

treeView1.Nodes.Add(CreateDirectoryNode(root, "root"));

InvalidOperationException

Action being performed on this control is being called from the wrong thread. Marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action

System.InvalidOperationException was unhandled by user code
  HResult=-2146233079
  Message=Action being performed on this control is being called from the wrong thread. Marshal to the correct thread using Control.Invoke or Control.BeginInvoke to perform this action.
  Source=System.Windows.Forms
  StackTrace:
       at System.Windows.Forms.TreeNode.Realize(Boolean insertFirst)
       at System.Windows.Forms.TreeNodeCollection.AddInternal(TreeNode node, Int32 delta)
       at FTP_ProgressBar.Form1.backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in c:\ftp_progressbar\FTP_ProgressBar\Form1.cs:line 419
       at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
  InnerException: 

Line 419 is: treeView1.Nodes.Add(CreateDirectoryNode(root, "root"));

Upvotes: 0

Views: 2592

Answers (2)

Grant Winney
Grant Winney

Reputation: 66439

You can't directly modify UI elements from a non-UI thread. When using a BackgroundWorker, the best place to modify the UI thread is from the ProgressChanged or RunWorkerCompleted events.

First, pass the values from the UI via the RunWorkerAsync() method:

backgroundWorker1.RunWorkerAsync(txtHost.Text);

Only do non-UI work in the DoWork event, then pass the results to the RunWorkerCompleted event:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{            
    var root = Convert.ToString(e.Argument);  // txtHost.Text;

    var dirNode = CreateDirectoryNode(root, "root");

    e.Result = dirNode;
}

Subscribe to the RunWorkerCompleted event to update the UI:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    var dirNode = (TreeNode)e.Result;

    treeView1.Nodes.Clear(); 

    treeView1.Nodes.Add(dirNode);
}

The app may freeze momentarily while the TreeView is being updated in the RunWorkerCompleted event, but you won't get that particular exception.

Upvotes: 4

Servy
Servy

Reputation: 203821

The DoWork event runs in a background thread. It cannot interact with UI elements. You should be creating some result; something that is not itself a UI element, setting it to the BGW's Result property, and then updating the UI based on that result in the RunWorkerCompleted even handler, which can access the result set in DoWork.

Upvotes: 0

Related Questions