我如何调用从backgroundworker dowork事件调用的方法?

时间:2014-12-03 16:31:59

标签: c# .net multithreading winforms

这是方法:

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;
        }

然后在后台工作:

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

进步改变了:

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

工作20秒后,它会在DoWork事件中抛出异常:

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

出现InvalidOperationException

正在从错误的线程调用正在对此控件执行的操作。使用Control.Invoke或Control.BeginInvoke对正确的线程进行Marshal来执行此操作

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: 

第419行是:treeView1.Nodes.Add(CreateDirectoryNode(root," root"));

2 个答案:

答案 0 :(得分:4)

您无法直接从非UI线程修改UI元素。使用BackgroundWorker时,修改UI线程的最佳位置来自ProgressChangedRunWorkerCompleted事件。

首先,通过RunWorkerAsync()方法从UI传递值:

backgroundWorker1.RunWorkerAsync(txtHost.Text);

仅在DoWork事件中执行非UI工作,然后将结果传递给RunWorkerCompleted事件:

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

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

    e.Result = dirNode;
}

订阅RunWorkerCompleted事件以更新用户界面:

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

    treeView1.Nodes.Clear(); 

    treeView1.Nodes.Add(dirNode);
}

RunWorkerCompleted事件中更新TreeView时,应用可能会暂时冻结,但您不会得到该特定异常。

答案 1 :(得分:0)

DoWork事件在后台线程中运行。它无法与UI元素交互。你应该创造一些结果;一些本身不是UI元素的东西,将其设置为BGW的Result属性,然后根据RunWorkerCompleted偶数处理程序中的结果更新UI,该处理程序可以访问{{1}中的结果集}}