C# 解决WinForms中的跨线程异常
目前我正在使用WinForms(在C#中),我必须在后台运行应用程序。为此,我使用异步。当我运行应用程序时,它会显示一个异常,如 跨线程操作无效:从创建控件“”的线程以外的线程访问该控件“”C# 解决WinForms中的跨线程异常,c#,winforms,multithreading,C#,Winforms,Multithreading,目前我正在使用WinForms(在C#中),我必须在后台运行应用程序。为此,我使用异步。当我运行应用程序时,它会显示一个异常,如 跨线程操作无效:从创建控件“”的线程以外的线程访问该控件“” 如何解决此错误?对控件进行方法调用时,如果调用方所在的线程与创建控件的线程不同,则需要使用调用。下面是一个代码示例: // you can define a delegate with the signature you want public delegate void UpdateControlsDel
如何解决此错误?对控件进行方法调用时,如果调用方所在的线程与创建控件的线程不同,则需要使用调用。下面是一个代码示例:
// 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执行此类操作的最佳方法是使用,它将在后台线程上运行您的工作,但为您提供一种很好的干净方法,将状态报告回UI
在许多日常的.NET编程中,显式创建线程或调用.Invoke是一种迹象,表明您没有充分利用框架(当然,也有很多正当的理由做低级的事情,只是人们有时意识到它们不太常见).您需要检查您试图更新的控件是否需要调用。大概是这样的:
Action<Control, string> setterCallback = (toSet, text) => toSet.Text = text;
void SetControlText(Control toSet, string text) {
if (this.InvokeRequired) {
this.Invoke(setterCallback, toSet, text);
}
else {
setterCallback(toSet, text);
}
}
Action setterCallback=(toSet,text)=>toSet.text=text;
void SetControlText(控件设置,字符串文本){
if(this.invokererequired){
调用(setterCallback、toSet、text);
}
否则{
setterCallback(设置,文本);
}
}
您可能会发现一种有用的模式是在与GUI交互的函数顶部进行检查,以查看您是否在正确的线程上运行,并在需要时让函数调用自身。像这样:
public delegate void InvocationDelegate();
public void DoGuiStuff(){
if (someControl.InvokeRequired){
someControl.Invoke(InvocationDelegate(DoGuiStuff));
return;
}
//GUI manipulation here
}
使用此模式-如果调用方法时处于正确的线程上,它不会调用自身,但如果处于不同的线程上,它将调用自身,然后返回(因此GUI操作逻辑无论哪种方式都只调用一次)。从调用更新为开始调用
// 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
}
UI更改可以通过Control.Invoke()方法完成,这个跨线程异常可以使用下面的代码片段解决
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); }));
}
我知道这个主题已经有10年历史了,但我想通过lambda选择器改进泛型的解决方案,而不是定义每种类型的setter
private void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value)
{
if (this.InvokeRequired)
this.Invoke(MyUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
感谢您的回复我正在我的应用程序中使用此(上面的链接)背景代码。但这显示了错误…@Victor-您必须使用.Invoke(参见其他人的示例)或使用BackgroundWorker.ReportProgress方法(以及它引发的ProgressChanged事件)访问表单。如果我使用BackgroundWorker,我总是喜欢ReportProgress而不是直接调用调用。Hi的可能重复Hi的可能重复感谢您的响应一旦我尝试了您提供的代码,实际上我调用了thred in button_click事件..但它总是转到其他部分..不进入此If(this.invokererequired){this.Invoke(new PerformSomeActionDelegate(MyAction));}@Victor:这意味着此时不需要调用。确保线程中执行的所有代码都执行“安全”(Invoke)调用以更新窗体控件。@Victor:恐怕这不可能Hi Pankaj allready我已经尝试过这段代码,但当它为真时,InvokerRequired始终为假…我在我的按钮单击事件中调用InvokerRequired…将其替换为buttonid并再次检查。我编写了这样的代码:private void btnsiteranks\u click\u 1(对象发送方,EventArgs e){InvokeUpdateControls();}public void InvokeUpdateControls(){if(this.InvokeRequired){this.BeginInvoke(new PerformSomeActionDelegate(Geturls));}else{Geturls();}}好了,现在工作正常了,只是我修改了代码,比如(!this.invokererequired)…但是我想在数据处理时显示标签文本如何…请帮助我
public static void SetControlSafety<C, V>(C control, Expression<Func<C, V>> selector, V value) where C : Control
{
if (control.InvokeRequired)
control.Invoke(DataCrawlerUtils.GetSetter(selector), control, value);
else
DataCrawlerUtils.GetSetter(selector)(control, value);
}
public static Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> pExpression
)
{
var parameter1 = Expression.Parameter(typeof(T));
var parameter2 = Expression.Parameter(typeof(TProperty));
// turning an expression body into a PropertyInfo is common enough
// that it's a good idea to extract this to a reusable method
var member = (MemberExpression)pExpression.Body;
var propertyInfo = (PropertyInfo)member.Member;
// use the PropertyInfo to make a property expression
// for the first parameter (the object)
var property = Expression.Property(parameter1, propertyInfo);
// assignment expression that assigns the second parameter (value) to the property
var assignment = Expression.Assign(property, parameter2);
// then just build the lambda, which takes 2 parameters, and has the assignment
// expression for its body
var setter = Expression.Lambda<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
SetControlSafety(txtStatus, x => x.Text, "Loading resources...");