解决WinForms中的跨线程exception
目前我正在使用WinForms(在C#中),我必须在后台运行该应用程序。 为此,我使用异步。 当我运行应用程序时,它显示了一个例外
“跨线程操作无效:控制”从其创建的线程以外的线程访问。”
我该如何解决这个错误?
在对控件进行方法调用时,如果调用者与创建控件的线程不在同一个线程上,则需要使用Control.Invoke进行调用。 这是一个代码示例:
// you can define a delegate with the signature you want public delegate void UpdateControlsDelegate(); public void SomeMethod() { //this method is executed by the background worker InvokeUpdateControls(); } public void InvokeUpdateControls() { if (this.InvokeRequired) { this.Invoke(new UpdateControlsDelegate(UpdateControls)); } else { UpdateControls(); } } private void UpdateControls() { // update your controls here }
希望能帮助到你。
大多数情况下,使用WinForms执行此类操作的最佳方法是使用BackgroundWorker ,它将在后台线程上运行您的工作,但为您提供了一种将状态报告回UI的简洁方法。
在很多日常的.NET编程中,显式创建线程或调用.Invoke表明你没有充分利用框架(当然,有很多合理的理由去做低级的东西,它是只是他们不太常见,人们有时会意识到)。
您需要检查您尝试更新的控件是否需要调用。 像这样的东西:
Action setterCallback = (toSet, text) => toSet.Text = text; void SetControlText(Control toSet, string text) { if (this.InvokeRequired) { this.Invoke(setterCallback, toSet, text); } else { setterCallback(toSet, text); } }
您可能会发现有用的模式是在与GUI交互的函数顶部进行检查,以查看您是否在正确的线程上运行,并在需要时让函数自行调用。 像这样:
public delegate void InvocationDelegate(); public void DoGuiStuff(){ if (someControl.InvokeRequired){ someControl.Invoke(InvocationDelegate(DoGuiStuff)); return; } //GUI manipulation here }
使用这种模式 – 如果在调用方法时你在正确的线程上它不会自己调用,但是如果你在不同的线程上它将自己调用然后返回(所以GUI操作逻辑只被调用过一次办法)。
从Invoke更新以开始Invoke
// you can define a delegate with the signature you want public delegate void UpdateControlsDelegate(); public void SomeMethod() { //this method is executed by the background worker InvokeUpdateControls(); } public void InvokeUpdateControls() { if (this.InvokeRequired) { this.BeginInvoke(new UpdateControlsDelegate(UpdateControls)); } else { UpdateControls(); } } private void UpdateControls() { // update your controls here }
可以使用Control.Invoke()方法完成UI更改,可以使用下面的代码片段解决此跨线程exception。
void UpdateWorker() { //Here ddUser is the user control //Action to be performed should be called within { } as like below code if (this.ddUser.InvokeRequired) ddUser.Invoke(new MethodInvoker(() => { ddUser.Size = new Size(100, 100); })); }