从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
这是Ian Kemp解决方案的C#3.0变体:
public static void SetPropertyInGuiThread<C,V>(this C control, Expression<Func<C, V>> property, V value) where C : Control
{
var memberExpression = property.Body as MemberExpression;
if (memberExpression == null)
throw new ArgumentException("The 'property' expression must specify a property on the control.");
var propertyInfo = memberExpression.Member as PropertyInfo;
if (propertyInfo == null)
throw new ArgumentException("The 'property' expression must specify a property on the control.");
if (control.InvokeRequired)
control.Invoke(
(Action<C, Expression<Func<C, V>>, V>)SetPropertyInGuiThread,
new object[] { control, property, value }
);
else
propertyInfo.SetValue(control, value, null);
}
你这样称呼它:
myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
它将空检查添加到“as MemberExpression”的结果中。它提高了静态类型的安全性。
否则,原始版本是一个非常好的解决方案。
其他回答
您必须确保更新发生在正确的线程上;UI线程。
为了做到这一点,您必须调用事件处理程序,而不是直接调用它。
您可以通过以下方式发起活动:
(代码是在我的脑海中打出来的,所以我没有检查语法等是否正确,但它应该能让你继续。)
if( MyEvent != null )
{
Delegate[] eventHandlers = MyEvent.GetInvocationList();
foreach( Delegate d in eventHandlers )
{
// Check whether the target of the delegate implements
// ISynchronizeInvoke (Winforms controls do), and see
// if a context-switch is required.
ISynchronizeInvoke target = d.Target as ISynchronizeInvoke;
if( target != null && target.InvokeRequired )
{
target.Invoke (d, ... );
}
else
{
d.DynamicInvoke ( ... );
}
}
}
请注意,上面的代码在WPF项目上不起作用,因为WPF控件不实现ISynchronizeInvoke接口。
为了确保上面的代码适用于Windows窗体和WPF以及所有其他平台,您可以查看AsyncOperation、AsyncOperationManager和SynchronizationContext类。
为了以这种方式轻松地引发事件,我创建了一个扩展方法,它允许我通过调用以下命令来简化引发事件:
MyEvent.Raise(this, EventArgs.Empty);
当然,您也可以使用BackGroundWorker类,它将为您抽象这个问题。
也许有点过量,但这是我通常解决问题的方式:
由于同步,此处不需要调用。BasicClassThreadExample对我来说只是一种布局,因此请根据您的实际需要进行更改。
这很简单,因为您不需要处理UI线程中的内容!
public partial class Form1 : Form
{
BasicClassThreadExample _example;
public Form1()
{
InitializeComponent();
_example = new BasicClassThreadExample();
_example.MessageReceivedEvent += _example_MessageReceivedEvent;
}
void _example_MessageReceivedEvent(string command)
{
listBox1.Items.Add(command);
}
private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
_example.Start();
}
}
public class BasicClassThreadExample : IDisposable
{
public delegate void MessageReceivedHandler(string msg);
public event MessageReceivedHandler MessageReceivedEvent;
protected virtual void OnMessageReceivedEvent(string msg)
{
MessageReceivedHandler handler = MessageReceivedEvent;
if (handler != null)
{
handler(msg);
}
}
private System.Threading.SynchronizationContext _SynchronizationContext;
private System.Threading.Thread _doWorkThread;
private bool disposed = false;
public BasicClassThreadExample()
{
_SynchronizationContext = System.ComponentModel.AsyncOperationManager.SynchronizationContext;
}
public void Start()
{
_doWorkThread = _doWorkThread ?? new System.Threading.Thread(dowork);
if (!(_doWorkThread.IsAlive))
{
_doWorkThread = new System.Threading.Thread(dowork);
_doWorkThread.IsBackground = true;
_doWorkThread.Start();
}
}
public void dowork()
{
string[] retval = System.IO.Directory.GetFiles(@"C:\Windows\System32", "*.*", System.IO.SearchOption.TopDirectoryOnly);
foreach (var item in retval)
{
System.Threading.Thread.Sleep(25);
_SynchronizationContext.Post(new System.Threading.SendOrPostCallback(delegate(object obj)
{
OnMessageReceivedEvent(item);
}), null);
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_doWorkThread.Abort();
}
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~BasicClassThreadExample() { Dispose(false); }
}
必须使用invoke和delegate
private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });
简单的解决方案是使用Control.Invoke。
void DoSomething()
{
if (InvokeRequired) {
Invoke(new MethodInvoker(updateGUI));
} else {
// Do Something
updateGUI();
}
}
void updateGUI() {
// update gui here
}
WPF应用程序中最简单的方法是:
this.Dispatcher.Invoke((Action)(() =>
{
// This refers to a form in a WPF application
val1 = textBox.Text; // Access the UI
}));