从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
这是您应该采用的经典方法:
using System;
using System.Windows.Forms;
using System.Threading;
namespace Test
{
public partial class UIThread : Form
{
Worker worker;
Thread workerThread;
public UIThread()
{
InitializeComponent();
worker = new Worker();
worker.ProgressChanged += new EventHandler<ProgressChangedArgs>(OnWorkerProgressChanged);
workerThread = new Thread(new ThreadStart(worker.StartWork));
workerThread.Start();
}
private void OnWorkerProgressChanged(object sender, ProgressChangedArgs e)
{
// Cross thread - so you don't get the cross-threading exception
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
OnWorkerProgressChanged(sender, e);
});
return;
}
// Change control
this.label1.Text = e.Progress;
}
}
public class Worker
{
public event EventHandler<ProgressChangedArgs> ProgressChanged;
protected void OnProgressChanged(ProgressChangedArgs e)
{
if(ProgressChanged!=null)
{
ProgressChanged(this,e);
}
}
public void StartWork()
{
Thread.Sleep(100);
OnProgressChanged(new ProgressChangedArgs("Progress Changed"));
Thread.Sleep(100);
}
}
public class ProgressChangedArgs : EventArgs
{
public string Progress {get;private set;}
public ProgressChangedArgs(string progress)
{
Progress = progress;
}
}
}
工作线程有一个事件。UI线程启动另一个线程来完成工作,并挂接该工作线程事件,以便显示工作线程的状态。
然后在UI中,您需要跨线程来更改实际控件。。。如标签或进度条。
其他回答
我更喜欢这个:
private void UpdateNowProcessing(string nowProcessing)
{
if (this.InvokeRequired)
{
Action<string> d = UpdateNowProcessing;
Invoke(d, nowProcessing);
}
else
{
this.progressDialog.Next(nowProcessing);
}
}
最简单的方法是将匿名方法传递给Label。Invoke:
// Running on the worker thread
string newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {
// Running on the UI thread
form.Label.Text = newText;
});
// Back on the worker thread
请注意,Invoke会阻止执行直到它完成——这是同步代码。这个问题不涉及异步代码,但在StackOverflow上有很多关于在您想了解异步代码时编写异步代码的内容。
出于许多目的,它非常简单:
public delegate void serviceGUIDelegate();
private void updateGUI()
{
this.Invoke(new serviceGUIDelegate(serviceGUI));
}
“serviceGUI()”是表单(this)中的GUI级方法,可以根据需要更改任意数量的控件。从另一个线程调用“updateGUI()”。可以添加参数来传递值,或者(可能更快)根据需要使用带有锁的类范围变量,如果访问这些变量的线程之间可能发生冲突,从而导致不稳定。如果非GUI线程是时间关键的(请记住BrianGideon的警告),请使用BeginInvoke而不是Invoke。
只使用ui的同步上下文
using System.Threading;
// ...
public partial class MyForm : Form
{
private readonly SynchronizationContext uiContext;
public MyForm()
{
InitializeComponent();
uiContext = SynchronizationContext.Current; // get ui thread context
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(() =>
{// set ui thread context to new thread context
// for operations with ui elements to be performed in proper thread
SynchronizationContext
.SetSynchronizationContext(uiContext);
label1.Text = "some text";
});
t.Start();
}
}
即使操作很耗时(在我的示例中为thread.sleep)-此代码不会锁定您的UI:
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(ThreadJob));
t.IsBackground = true;
t.Start();
}
private void ThreadJob()
{
string newValue= "Hi";
Thread.Sleep(2000);
this.Invoke((MethodInvoker)delegate
{
label1.Text = newValue;
});
}