从BackgroundWorker线程访问UI控件
我的窗体上有一个调用RunWorkerAsync()方法的button,然后执行一个动作,然后在同一个窗体上更新一个ListBox。
在DoWork事件完成后,我为事件(这是一个列表)分配结果,我处理RunWorkerCompleted()事件,然后执行下面的代码来更新我的列表框
这称之为:
(道歉,代码格式不起作用)
现在,当我运行应用程序并按下刷新button时,出现以下exception:
我将如何解决这个问题?
编辑:
在下面的语句中抛出exception,这发生在DoWork方法中,我清除内容以保持列表最新;
listBoxServers.Items.Clear();
这里有一个我觉得非常好用的片段:
public static void ThreadSafe(Action action) { Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new MethodInvoker(action)); }
你可以把它传递给Action
types的任何委托或简单地像这样的lambda:
ThreadSafe(() => { [your code here] });
要么
ThreadSafe(listBoxServers.Items.Clear);
您不能在列表框上调用Invoke
,而是在窗体上Invoke
。 对于WinForms应用程序,我使用类似于:
... this.Invoke((MethodInvoker)delegate() { // Do stuff on ANY control on the form. }); ...
根据.NET版本,您可能必须自己声明一个MethodInvoker
的委托
public delegate void MethodInvoker();
不过,您也可以考虑使用Background Worker的ReportProgress
function。 应该在表单线程的上下文中调用相应的事件处理程序。
每当你需要在线程中运行一些东西时,我所做的就是这样的:
listBoxServers.BeginInvoke( (Action) (() => listBoxServers.Items.Clear()));
后台线程不允许更新Windows应用程序中的用户界面,因此您必须将控制权还原回UI线程才能进行实际更新。
创build一个将在主线程上调用UpdateServerDetails的方法,如下所示:
private void DispatchServerDetails(List<ServerDetails> details) { Action<List<ServerDetails>> action = UpdateServerDetails; Dispatcher.Invoke(action) }
然后调用DispatchServerDetails
而不是UpdateServerDetails
。
一些注意事项:
– 这在WPF应用程序中效果最佳,对于WinForms,您需要跳过一些圈,或者您可以使用InvokeRequired
UI更新仍然是同步的,所以如果UpdateServerDetails做了很多工作,它会阻塞UI线程(不是你的情况,只是为了安全起见)。
在Windows窗体项目中使用调用可能有点棘手,有一些陷阱,有logging,但容易错过。 我build议你在这个问题中使用类似的东西:
是否适当扩展控制提供一贯安全的调用/ BeginInvokefunction?
它处理不需要调用的情况,从不同的线程调用,处理是或不被创build,etcetcetc。 如果你不是bool参数的粉丝,可以很容易地修改为SafeInvoke()
和SafeBeginInvoke()
。
(这里包括为了您的方便:
/// Usage: this.lblTimeDisplay.SafeInvoke(() => this.lblTimeDisplay.Text = this.task.Duration.ToString(), false); // or string taskName = string.Empty; this.txtTaskName.SafeInvoke(() => taskName = this.txtTaskName.Text, true); /// <summary> /// Execute a method on the control's owning thread. /// </summary> /// <param name="uiElement">The control that is being updated.</param> /// <param name="updater">The method that updates uiElement.</param> /// <param name="forceSynchronous">True to force synchronous execution of /// updater. False to allow asynchronous execution if the call is marshalled /// from a non-GUI thread. If the method is called on the GUI thread, /// execution is always synchronous.</param> public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous) { if (uiElement == null) { throw new ArgumentNullException("uiElement"); } if (uiElement.InvokeRequired) { if (forceSynchronous) { uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } else { uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); }); } } else { if (!uiElement.IsHandleCreated) { // Do nothing if the handle isn't created already. The user's responsible // for ensuring that the handle they give us exists. return; } if (uiElement.IsDisposed) { throw new ObjectDisposedException("Control is already disposed."); } updater(); } }
我只是想出了一个更简单的方法,而不使用Invoke:
int fakepercentage = -1; //some loop here......if no loop exists, just change the value to something else if (fakepercentage == -1) { fakepercentage = -2; } else { fakepercentage = -1; } backgroundworker1.ReportProgress(fakepercentage);
然后在backgroundworker1_ProgressChanged(对象发件人,ProgressChangedEventArgs e)中:
if (e.ProgressPercentage < 0) { //access your ui control safely here }
- 使用SqlCommandasynchronous方法时性能糟糕
- asynchronous与multithreading – 有没有区别?
- 来自.Net 4.5的asynchronousHttpClient是密集加载应用程序的不好select吗?
- Node.js – 使用async lib – async.foreach和object
- socket.shutdown与socket.close
- .NETasynchronousstream读/写
- 在WebApi中使用HttpContext.Current是非常危险的,因为是asynchronous的
- asynchronous文件复制/在C#中移动
- ASP.NET MVC中的asynchronous操作是否使用.NET 4上的ThreadPool的线程