从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
从另一个线程更新标签的最简单方法是什么?
我在thread1上运行了一个Form,然后我开始另一个线程(thread2)。当thread2处理一些文件时,我想用thread2工作的当前状态更新表单上的标签。
我怎么能这样做?
当前回答
将一些公共变量放在一个单独的类中以保存值。
例子:
public class data_holder_for_controls
{
// It will hold the value for your label
public string status = string.Empty;
}
class Demo
{
public static data_holder_for_controls d1 = new data_holder_for_controls();
static void Main(string[] args)
{
ThreadStart ts = new ThreadStart(perform_logic);
Thread t1 = new Thread(ts);
t1.Start();
t1.Join();
//your_label.Text=d1.status; --- can access it from any thread
}
public static void perform_logic()
{
// Put some code here in this function
for (int i = 0; i < 10; i++)
{
// Statements here
}
// Set the result in the status variable
d1.status = "Task done";
}
}
其他回答
这是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”的结果中。它提高了静态类型的安全性。
否则,原始版本是一个非常好的解决方案。
即使操作很耗时(在我的示例中为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;
});
}
您需要在GUI线程上调用该方法。您可以通过调用Control.Invoke来实现这一点。
例如:
delegate void UpdateLabelDelegate (string message);
void UpdateLabel (string message)
{
if (InvokeRequired)
{
Invoke (new UpdateLabelDelegate (UpdateLabel), message);
return;
}
MyLabelControl.Text = message;
}
例如,访问当前线程以外的控件:
Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
lblThreshold.Text = Speed_Threshold.ToString();
}));
lblThreshold是一个标签,Speed_Threshold则是一个全局变量。
在这个问题上,大多数其他答案对我来说都有点复杂(我是C#新手),所以我写了我的答案:
我有一个WPF应用程序,并定义了如下工作人员:
问题:
BackgroundWorker workerAllocator;
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1) {
// This is my DoWork function.
// It is given as an anonymous function, instead of a separate DoWork function
// I need to update a message to textbox (txtLog) from this thread function
// Want to write below line, to update UI
txt.Text = "my message"
// But it fails with:
// 'System.InvalidOperationException':
// "The calling thread cannot access this object because a different thread owns it"
}
解决方案:
workerAllocator.DoWork += delegate (object sender1, DoWorkEventArgs e1)
{
// The below single line works
txtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}
我还没有弄清楚上面这句话的意思,但它奏效了。
对于WinForms:
解决方案:
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.Text = "my message";
});