C# 如何从另一个线程更新GUI?
从另一个C# 如何从另一个线程更新GUI?,c#,.net,multithreading,winforms,user-interface,C#,.net,Multithreading,Winforms,User Interface,从另一个线程更新标签的最简单方法是什么 我有一个表单在thread1上运行,从中我开始了另一个线程(thread2) 当thread2正在处理一些文件时,我想用thread2的当前工作状态更新表单上的标签 我该怎么做呢?您必须确保更新发生在正确的线程上;用户界面线程 为了做到这一点,您必须调用事件处理程序,而不是直接调用它 您可以通过以下方式提高您的活动: // thread-safe equivalent of // myLabel.Text = status; SetControlPro
线程更新标签
的最简单方法是什么
- 我有一个
表单
在thread1
上运行,从中我开始了另一个线程(thread2
)
- 当
thread2
正在处理一些文件时,我想用thread2
的当前工作状态更新表单上的标签
我该怎么做呢?您必须确保更新发生在正确的线程上;用户界面线程
为了做到这一点,您必须调用事件处理程序,而不是直接调用它
您可以通过以下方式提高您的活动:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
Public delegate void LabelDelegate(string s);
void Updatelabel(string text)
{
if (label.InvokeRequired)
{
LabelDelegate LDEL = new LabelDelegate(Updatelabel);
label.Invoke(LDEL, text);
}
else
label.Text = text
}
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = e.ProgressPercentage; // runs on UI thread
});
(这里输入的代码是我脑子里想出来的,所以我没有检查语法是否正确,等等,但它应该能让你继续使用。)
请注意,上面的代码在WPF项目上不起作用,因为WPF控件不实现ISynchronizeInvoke
接口
为了确保上述代码适用于Windows窗体和WPF以及所有其他平台,您可以查看AsyncOperation
、AsyncOperationManager
和SynchronizationContext
类
为了以这种方式轻松引发事件,我创建了一个扩展方法,它允许我通过调用以下命令简化引发事件:
MyEvent.Raise(this, EventArgs.Empty);
当然,您也可以使用BackGroundWorker类,它将为您抽象这个问题。您必须确保更新发生在正确的线程上;用户界面线程
为了做到这一点,您必须调用事件处理程序,而不是直接调用它
您可以通过以下方式提高您的活动:
// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);
myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
Public delegate void LabelDelegate(string s);
void Updatelabel(string text)
{
if (label.InvokeRequired)
{
LabelDelegate LDEL = new LabelDelegate(Updatelabel);
label.Invoke(LDEL, text);
}
else
label.Text = text
}
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = e.ProgressPercentage; // runs on UI thread
});
(这里输入的代码是我脑子里想出来的,所以我没有检查语法是否正确,等等,但它应该能让你继续使用。)
请注意,上面的代码在WPF项目上不起作用,因为WPF控件不实现ISynchronizeInvoke
接口
为了确保上述代码适用于Windows窗体和WPF以及所有其他平台,您可以查看AsyncOperation
、AsyncOperationManager
和SynchronizationContext
类
为了以这种方式轻松引发事件,我创建了一个扩展方法,它允许我通过调用以下命令简化引发事件:
MyEvent.Raise(this, EventArgs.Empty);
当然,您也可以使用BackGroundWorker类,它将为您抽象这个问题。简单的解决方案是使用控件。调用
void DoSomething()
{
if (InvokeRequired) {
Invoke(new MethodInvoker(updateGUI));
} else {
// Do Something
updateGUI();
}
}
void updateGUI() {
// update gui here
}
简单的解决方案是使用控件.Invoke
void DoSomething()
{
if (InvokeRequired) {
Invoke(new MethodInvoker(updateGUI));
} else {
// Do Something
updateGUI();
}
}
void updateGUI() {
// update gui here
}
您需要在GUI线程上调用该方法。您可以通过调用Control.Invoke来实现这一点
例如:
delegate void UpdateLabelDelegate (string message);
void UpdateLabel (string message)
{
if (InvokeRequired)
{
Invoke (new UpdateLabelDelegate (UpdateLabel), message);
return;
}
MyLabelControl.Text = message;
}
您需要在GUI线程上调用该方法。您可以通过调用Control.Invoke来实现这一点
例如:
delegate void UpdateLabelDelegate (string message);
void UpdateLabel (string message)
{
if (InvokeRequired)
{
Invoke (new UpdateLabelDelegate (UpdateLabel), message);
return;
}
MyLabelControl.Text = message;
}
最简单的方法是将匿名方法传递到:
请注意,Invoke
会阻止执行,直到它完成为止——这是同步代码。这个问题并不是问异步代码,但当您想了解异步代码时,有很多关于编写异步代码的问题。最简单的方法是将匿名方法传递到:
请注意,Invoke
会阻止执行,直到它完成为止——这是同步代码。这个问题不涉及异步代码,但当您想了解异步代码时,有很多关于编写异步代码的问题。这是您应该采用的经典方法:
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;
}
}
}
使用系统;
使用System.Windows.Forms;
使用系统线程;
名称空间测试
{
公共部分类UIThread:表单
{
工人;
螺纹加工螺纹;
公共UIThread()
{
初始化组件();
工人=新工人();
worker.ProgressChanged+=新事件处理程序(OnWorkerProgressChanged);
workerThread=新线程(新线程开始(worker.StartWork));
workerThread.Start();
}
WorkerProgressChanged上的私有void(对象发送方,ProgressChangedArgs e)
{
//交叉线程-因此不会出现交叉线程异常
if(this.invokererequired)
{
此.BeginInvoke((MethodInvoker)委托
{
OnWorkerProgressChanged(发送方,e);
});
返回;
}
//变更控制
this.label1.Text=e.进度;
}
}
公社工人
{
公共事件处理程序已更改;
ProgressChanged上的受保护无效(ProgressChangedArgs e)
{
if(ProgressChanged!=null)
{
(本,e);
}
}
公共网络
{
睡眠(100);
OnProgressChanged(新的ProgressChangedArgs(“进度变更”));
睡眠(100);
}
}
公共类ProgressChangedArgs:EventArgs
{
公共字符串进度{get;private set;}
公共进度变更码(字符串进度)
{
进步=进步;
}
}
}
您的工作线程有一个事件。您的UI线程启动另一个线程来执行工作,并连接该工作线程事件,以便您可以显示工作线程的状态
然后在UI中,您需要跨线程来更改实际控件。。。类似于标签或进度条。这是实现此目的的经典方法:
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;
}
}
}
使用系统;
使用System.Windows.Forms;
使用系统线程;
名称空间测试
{
公共部分类UIThread:表单
{
工人;
螺纹加工螺纹;
公共UIThread()
{
初始化组件();
工人=新工人();
worker.ProgressChanged+=新事件处理程序(O
this.UIThread(() => this.myLabel.Text = "Text Goes Here");
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");
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!")
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
void Aaaaaaa()
{
if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra
// Your code!
}
void Bbb(int x, string text)
{
if (InvokeRequired) { Invoke(new Action<int, string>(Bbb), new[] { x, text }); return; }
// Your code!
}
Label lblText; //initialized elsewhere
void AssignLabel(string text)
{
if (InvokeRequired)
{
BeginInvoke((Action<string>)AssignLabel, text);
return;
}
lblText.Text = text;
}
control.Invoke((MethodInvoker) (() => control.Text = "new text"));
control.Invoke(new Action(() => control.Text = "new text"));
private void UpdateMethod()
{
if (InvokeRequired)
{
Invoke(new Action(UpdateMethod));
}
}
Public delegate void LabelDelegate(string s);
void Updatelabel(string text)
{
if (label.InvokeRequired)
{
LabelDelegate LDEL = new LabelDelegate(Updatelabel);
label.Invoke(LDEL, text);
}
else
label.Text = text
}
Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();
// In the main thread
WindowsFormsSynchronizationContext mUiContext = new WindowsFormsSynchronizationContext();
...
// In some non-UI Thread
// Causes an update in the GUI thread.
mUiContext.Post(UpdateGUI, userData);
...
void UpdateGUI(object userData)
{
// Update your GUI controls here
}
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
}
}
private async void Button_Clicked(object sender, EventArgs e)
{
var progress = new Progress<string>(s => label.Text = s);
await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress),
TaskCreationOptions.LongRunning);
label.Text = "completed";
}
class SecondThreadConcern
{
public static void LongWork(IProgress<string> progress)
{
// Perform a long running work...
for (var i = 0; i < 10; i++)
{
Task.Delay(500).Wait();
progress.Report(i.ToString());
}
}
}
private async void Button_Click(object sender, EventArgs e)
{
button.Enabled = false;
try
{
var progress = new Progress<string>(s => button.Text = s);
await Task.Run(() => SecondThreadConcern.FailingWork(progress));
button.Text = "Completed";
}
catch(Exception exception)
{
button.Text = "Failed: " + exception.Message;
}
button.Enabled = true;
}
class SecondThreadConcern
{
public static void FailingWork(IProgress<string> progress)
{
progress.Report("I will fail in...");
Task.Delay(500).Wait();
for (var i = 0; i < 3; i++)
{
progress.Report((3 - i).ToString());
Task.Delay(500).Wait();
}
throw new Exception("Oops...");
}
}
private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });
SynchronizationContext _context;
var _context = SynchronizationContext.Current;
_context.Send(status =>{
// UPDATE LABEL
}, null);
Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{
lblThreshold.Text = Speed_Threshold.ToString();
}));
public partial class MyForm : Form
{
private readonly TaskScheduler _uiTaskScheduler;
public MyForm()
{
InitializeComponent();
_uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void buttonRunAsyncOperation_Click(object sender, EventArgs e)
{
RunAsyncOperation();
}
private void RunAsyncOperation()
{
var task = new Task<string>(LengthyComputation);
task.ContinueWith(antecedent =>
UpdateResultLabel(antecedent.Result), _uiTaskScheduler);
task.Start();
}
private string LengthyComputation()
{
Thread.Sleep(3000);
return "47";
}
private void UpdateResultLabel(string text)
{
labelResult.Text = text;
}
}
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
someLabel.Text = newText; // runs on UI thread
});
public partial class MyForm : Form
{
private readonly SynchronizationContext _context;
public MyForm()
{
_context = SynchronizationContext.Current
...
}
private MethodOnOtherThread()
{
...
_context.Post(status => someLabel.Text = newText,null);
}
}
this.Invoke((MethodInvoker)delegate
{
progressBar1.Value = e.ProgressPercentage; // runs on UI thread
});
this.Dispatcher.Invoke((Action)(() =>
{
// This refers to a form in a WPF application
val1 = textBox.Text; // Access the UI
}));
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"));
}
txtLog.Invoke((MethodInvoker)delegate
{
txtLog.Text = "my message";
});
public static void InvokeIfRequired<T>(this T c, Action<T> action) where T : Control
{
if (c.InvokeRequired)
{
c.Invoke(new Action(() => action(c)));
}
else
{
action(c);
}
}
object1.InvokeIfRequired(c => { c.Visible = true; });
object1.InvokeIfRequired(c => { c.Text = "ABC"; });
object1.InvokeIfRequired(c =>
{
c.Text = "ABC";
c.Visible = true;
}
);