从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
我无法理解这一丑陋实现背后的微软逻辑,但你必须有两个功能:
void setEnableLoginButton()
{
if (InvokeRequired)
{
// btn_login can be any conroller, (label, button textbox ..etc.)
btn_login.Invoke(new MethodInvoker(setEnable));
// OR
//Invoke(new MethodInvoker(setEnable));
}
else {
setEnable();
}
}
void setEnable()
{
btn_login.Enabled = isLoginBtnEnabled;
}
这些片段对我有用,所以我可以在另一个线程上执行一些操作,然后更新GUI:
Task.Factory.StartNew(()=>
{
// THIS IS NOT GUI
Thread.Sleep(5000);
// HERE IS INVOKING GUI
btn_login.Invoke(new Action(() => DoSomethingOnGUI()));
});
private void DoSomethingOnGUI()
{
// GUI
MessageBox.Show("message", "title", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
更简单的是:
btn_login.Invoke(new Action(()=>{ /* HERE YOU ARE ON GUI */ }));
其他回答
出于许多目的,它非常简单:
public delegate void serviceGUIDelegate();
private void updateGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}
“serviceGUI()”是表单(this)中的GUI级方法,可以根据需要更改任意数量的控件。从另一个线程调用“updateGUI()”。可以添加参数来传递值,或者(可能更快)根据需要使用带有锁的类范围变量,如果访问这些变量的线程之间可能发生冲突,从而导致不稳定。如果非GUI线程是时间关键的(请记住BrianGideon的警告),请使用BeginInvoke而不是Invoke。
致敬!在搜索了这个问题之后,我发现FrankG和Oregon Ghost的答案对我来说是最简单、最有用的;所以我不太确定结果如何。
我有一个名为form_Diagnostics的对话框,它有一个称为updateDiagWindow的富文本框,我正在使用它作为一种日志显示。我需要能够从所有线程更新它的文本。额外的行允许窗口自动滚动到最新的行。
因此,我现在可以从整个程序的任何地方,以您认为无需任何线程的方式,用一行代码更新显示:
form_Diagnostics.updateDiagWindow(whatmessage);
主代码(将其放入表单的类代码中):
#region "---------Update Diag Window Text------------------------------------"
// This sub allows the diag window to be updated by all threads
public void updateDiagWindow(string whatmessage)
{
var _with1 = diagwindow;
if (_with1.InvokeRequired) {
_with1.Invoke(new UpdateDiagDelegate(UpdateDiag), whatmessage);
} else {
UpdateDiag(whatmessage);
}
}
// This next line makes the private UpdateDiagWindow available to all threads
private delegate void UpdateDiagDelegate(string whatmessage);
private void UpdateDiag(string whatmessage)
{
var _with2 = diagwindow;
_with2.appendtext(whatmessage);
_with2.SelectionStart = _with2.Text.Length;
_with2.ScrollToCaret();
}
#endregion
我想添加一个警告,因为我注意到一些简单的解决方案忽略了InvokeRequired检查。
我注意到,如果您的代码在创建控件的窗口句柄之前(例如,在显示表单之前)执行,Invoke会抛出异常。因此,我建议在调用Invoke或BeginInvoke之前始终检查InvokeRequired。
您可以使用现有的委托操作:
private void UpdateMethod()
{
if (InvokeRequired)
{
Invoke(new Action(UpdateMethod));
}
}
这一解决方案类似于上面使用.NET Framework 3.0的解决方案,但它解决了编译时安全支持的问题。
public static class ControlExtension
{
delegate void SetPropertyValueHandler<TResult>(Control souce, Expression<Func<Control, TResult>> selector, TResult value);
public static void SetPropertyValue<TResult>(this Control source, Expression<Func<Control, TResult>> selector, TResult value)
{
if (source.InvokeRequired)
{
var del = new SetPropertyValueHandler<TResult>(SetPropertyValue);
source.Invoke(del, new object[]{ source, selector, value});
}
else
{
var propInfo = ((MemberExpression)selector.Body).Member as PropertyInfo;
propInfo.SetValue(source, value, null);
}
}
}
要使用:
this.lblTimeDisplay.SetPropertyValue(a => a.Text, "some string");
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, false);
如果用户传递错误的数据类型,编译器将失败。
this.lblTimeDisplay.SetPropertyValue(a => a.Visible, "sometext");