[英]BackgroundWorker with ProgressBar - Cross-thread operation not valid
我的表格申请中有服务电话。 单击我的按钮后,我将调用我的服务方法。 这是我的代码块:
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(样式=选取框)可见。 但是由于跨线程问题,我无法使其在完整的方法中不可见。
当我尝试在BGWorker的Completed事件中的UI线程中执行某些操作时,
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressBar1.Visible = false;
}
我有一个例外:
跨线程操作无效:控件“ Form1”从创建该线程的线程之外的线程访问。
我该如何解决这个问题?
您需要使用Invoke()方法
private delegate void ToDoDelegate();
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Invoke(new ToDoDelegate(() => progressBar1.Visible = false));
}
Invoke()的详细信息http://msdn.microsoft.com/de-de/library/System.Windows.Forms.Control.Invoke(v=vs.110).aspx
这是我一直喜欢使用的小段代码,我不记得很久以前将其放在Dropbox中的位置。
public static class ControlExtensions
{
public static TResult InvokeEx<TControl, TResult>(this TControl control,
Func<TControl, TResult> func)
where TControl : Control
{
return control.InvokeRequired
? (TResult)control.Invoke(func, control)
: func(control);
}
public static void InvokeEx<TControl>(this TControl control,
Action<TControl> func)
where TControl : Control
{
control.InvokeEx(c => { func(c); return c; });
}
public static void InvokeEx<TControl>(this TControl control, Action action)
where TControl : Control
{
control.InvokeEx(c => action());
}
}
用法
this.InvokeEx( x => Progressbar1.Visible = false); //Note 'x' could be any variable
这样,您不必每次都键入所有内容,并且使用非常简单。 只需将此权利添加到表单类的末尾(在最后一个括号关闭之后)。 您可以使用它来安全地执行任何跨线程操作。
修改UI线程上ProgressBar的可见性。
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)
delegate()
{
progressBar1.Visible = false;
});
我认为在RunWorkerCompleted
事件中不会引发异常(如评论中所述)。 我的建议是,您尝试在DoWork
事件中访问UI cmponent的行会触发它。
尝试将DoWork
事件中所需的数据放入DoWorkEventArgs e
。 像这样的讨论在SO上提供了大量示例。
工作完成后,也可以使用事件args向“主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()
代码绝对不是异常的来源。 它构建了一个简单的示例来说明我的观点:
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
和一个用于启动BackGroundWorker
的Button
。
worker从TextBox
(UI线程)获取该数字, DoWork
将其增加1
并将其保存为result (新线程) 。 另外,它将增加的数字报告给ProgressBar
(跨线程) 。
最后, RunWorkerCompleted
读取结果并将新值存储在TextBox
(UI线程)中 。
因此,请重新检查您的代码;-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.