2014-07-03 90 views
1

我的表单应用程序中有服务调用。当我的按钮被点击时,我打电话给我的服务方法。这是我的代码块:BackgroundWorker with ProgressBar - 跨线程操作无效

void listBox_SelectedValueChanged(object sender, EventArgs e) 
    { 
     if (listBox.SelectedItem.Equals("Demo")) 
     { 
      progressBar1.Visible = true; 
      backgroundWorker1.RunWorkerAsync(); 
     } 
    } 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     ObservableCollection<FilterInfo> filterInfos = new ObservableCollection<FilterInfo>(); 
     FilterInfo myFilterObj = new FilterInfo("SUPERVISOR", userName); 
     filterInfos.Add(myFilterObj); 
     ObservableCollection<DEMOBec> demos = demoSvc.GetDemoByFilter(filterInfos, false); 
     dt = new Converter<DEMOBec>().ConvertDataTable(demos.ToList()); 
    } 

在调用服务之前,我让ProgressBar(Style = Marquee)可见。但是由于Cross Thread问题,我无法在完成的方法中将其隐藏起来。

当我试图在UI线程做一些与BGWorker的完成情况,

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     progressBar1.Visible = false; 
    } 

我得到一个例外:控制“Form1的”访问:无效

跨线程操作从一个线程而不是它创建的线程。

我该如何处理这个问题?

+0

如果您正确使用它,那是不可能的。 RunWorkerCompleted事件在UI线程上执行,所以你不应该看到这个异常。你是否正在抛出异常的代码行?仔细检查堆栈跟踪。 –

+0

“DoWork”事件更可能发生。 ObservableCollection是否以任何方式绑定到UI?修改'DoWork'事件中的那些_might_可能导致异常......不确定。 –

+0

您的变量'dt'似乎是全局变量,容易受到跨线程问题的影响。 – Srikanth

回答

4

您需要使用invoke()方法用于调用

private delegate void ToDoDelegate(); 
    void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     Invoke(new ToDoDelegate(() => progressBar1.Visible = false)); 
    } 

更多信息() http://msdn.microsoft.com/de-de/library/System.Windows.Forms.Control.Invoke(v=vs.110).aspx

+0

不需要再次调用()其已在UI上运行。检查[链接](http://stackoverflow.com/questions/2806814/backgroundworker-runworkercompleted-事件) – Srikanth

+0

感谢您的答复,这是有帮助的。通过此操作(Invoke())我创建一个新的线程来做我的外部服务呼叫的作品? – mekafe

0

修改进度的可见性在UI线程上。

Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action) 
        delegate() 
        { 
         progressBar1.Visible = false; 
        }); 
+0

解释一下,代码应该做什么! –

-1

我认为在RunWorkerCompleted事件中不会引发异常(如在注释中所述)。我的建议是,您尝试访问DoWork事件中的UI组件的行触发了它。

尝试将您需要的数据放入DoWork事件到DoWorkEventArgs e。大量的例子如何做到这一点,就像this discussion一样提供。

当工作完成后,提供“主UI线程”使用事件参数,也修改后的/新的数据,这样的:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { 
    // do some work 
    e.Result = ... ; // modified/new data 
} 

,并以下列方式进行检索:

void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { 
    var newData = (CastToWhatever) e.Result; 
} 

希望它可以帮助=)

编辑

您在backgroundWorker1_RunWorkerCompleted()中的代码肯定不是异常的来源。它内置一个小而简单的例子来说明我的argment:

private void button1_Click(object sender, EventArgs e) 
{ 
    backgroundWorker1.RunWorkerAsync(textBox1.Text); 
} 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    var num = Int32.Parse(e.Argument.ToString()) + 1; 
    backgroundWorker1.ReportProgress(num); 
    e.Result = num; 
} 

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    progressBar1.Value = (int)e.ProgressPercentage; 
} 

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    textBox1.Text = e.Result.ToString(); 
} 

它能做什么?在WinForm上有一个TextBox和一个数字,一个ProgressBar和一个Button来启动一个BackGroundWorker

工人得到来自TextBox(UI线程)DoWork增加它由1数量并保存为结果(新线)。此外,它报告增加的号码为ProgressBar(跨线程)

最后RunWorkerCompleted读出结果并将新值存储在TextBox(UI线程)中。

所以,请重新检查你的代码;-)

+0

我将我的服务结果返回给e.Result,但我在progressBar.Visibility = false中得到了同样的错误;在BGWorker的Completed事件? – mekafe

1

这里是小有一点的代码块,我总是爱用,我不记得我是从我把它放在我的Dropbox的一个很久以前。

​​

使用

this.InvokeEx(x => Progressbar1.Visible = false); //Note 'x' could be any variable 

这样你就不必每次都输入了这一切,和它是如此简单易用。只需将该权限添加到表单类的最后(在最后一个括号关闭后)。您可以使用它来安全地执行任何交叉线程操作。

+0

感谢您的关注。本课非常有用。最好的问候。 – mekafe