从另一个线程更新Label的最简单方法是什么?

我在thread1上有一个Formthread1开始,我开始了另一个线程( thread2 )。 当thread2处理某些文件时,我想使用thread2的当前状态更新Form上的Label

我怎样才能做到这一点?


#1楼

Label lblText; //initialized elsewherevoid AssignLabel(string text)
{if (InvokeRequired){BeginInvoke((Action<string>)AssignLabel, text);return;}lblText.Text = text;
}

请注意, BeginInvoke()Invoke() BeginInvoke()更可取,因为它不太可能导致死锁(但是,将文本分配给标签时,这并不是问题):

使用Invoke()您正在等待方法返回。 现在,可能是您在被调用的代码中执行了一些操作,该操作需要等待线程,如果该线程被埋在您正在调用的某些函数中,则该事件可能不会立即显现出来,而这本身可以通过事件处理程序间接发生。 因此,您将等待线程,线程将等待您,并且您陷入僵局。

这实际上导致我们发布的某些软件挂起。 通过用BeginInvoke()替换Invoke()来修复它很容易。 除非需要同步操作(如果需要返回值),请使用BeginInvoke()


#2楼

Marc Gravell针对.NET 4 的最简单解决方案的变体:

control.Invoke((MethodInvoker) (() => control.Text = "new text"));

或改用Action委托:

control.Invoke(new Action(() => control.Text = "new text"));

参见此处,对两者进行比较: MethodInvoker与Action for Control.BeginInvoke


#3楼

您可以使用已经存在的委托Action

private void UpdateMethod()
{if (InvokeRequired){Invoke(new Action(UpdateMethod));}
}

#4楼

当我遇到相同的问题时,我寻求Google的帮助,但没有给我简单的解决方案,而是举了MethodInvoker和blah blah之类的例子,这使我更加困惑。 所以我决定自己解决。 这是我的解决方案:

像这样委托:

Public delegate void LabelDelegate(string s);void Updatelabel(string text)
{if (label.InvokeRequired){LabelDelegate LDEL = new LabelDelegate(Updatelabel);label.Invoke(LDEL, text);}elselabel.Text = text
}

您可以像这样在新线程中调用此函数

Thread th = new Thread(() => Updatelabel("Hello World"));
th.start();

不要与Thread(() => .....)混淆。 在线程上工作时,我使用匿名函数或lambda表达式。 为了减少代码行,您也可以使用ThreadStart(..)方法,在此不做解释。


#5楼

先前答案中的“调用”东西都不是必需的。

您需要查看WindowsFormsSynchronizationContext:

// 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
}

#6楼

尝试使用此刷新标签

public static class ExtensionMethods
{private static Action EmptyDelegate = delegate() { };public static void Refresh(this UIElement uiElement){uiElement.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);}
}

#7楼

处理长时间的工作

从.NET 4.5和C#5.0开始,您应该在所有区域 (包括GUI)中使用基于任务的异步模式(TAP)以及async - await关键字:

TAP是新开发的推荐异步设计模式

而不是异步编程模型(APM)和基于事件的异步模式(EAP) (后者包括BackgroundWorker类 )。

然后,针对新开发的推荐解决方案是:

  1. 事件处理程序的异步实现(是的,仅此而已):

     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"; } 
  2. 通知UI线程的第二个线程的实现:

     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()); } } } 

请注意以下几点:

  1. 以顺序方式编写的简洁代码,没有回调和显式线程。
  2. 任务而不是Thread 。
  3. async关键字,该关键字允许使用await ,从而防止事件处理程序到达完成状态,直到任务完成为止,同时不会阻塞UI线程。
  4. Progress类(请参阅IProgress接口 ),它支持关注点分离(SoC)设计原理,并且不需要显式的调度程序和调用。 它从创建位置(此处为UI线程)使用当前的SynchronizationContext 。
  5. TaskCreationOptions.LongRunning提示不要将任务排队到ThreadPool中 。

有关更详细的示例,请参见: C#的未来: 约瑟夫·阿尔巴哈里 ( Joseph Albahari )“等待”的人们会感到高兴 。

另请参阅关于UI线程模型概念。

处理异常

下面的代码段示例说明了如何处理异常以及切换按钮的Enabled属性以防止在后台执行过程中多次单击。

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...");}
}

#8楼

您必须使用invoke和委托

private delegate void MyLabelDelegate();
label1.Invoke( new MyLabelDelegate(){ label1.Text += 1; });

#9楼

我认为最简单的方法是:

   void Update(){BeginInvoke((Action)delegate(){//do your update});}

#10楼

创建一个类变量:

SynchronizationContext _context;

在创建您的UI的构造函数中进行设置:

var _context = SynchronizationContext.Current;

当您要更新标签时:

_context.Send(status =>{// UPDATE LABEL
}, null);

#11楼

例如,访问当前线程以外的控件:

Speed_Threshold = 30;
textOutput.Invoke(new EventHandler(delegate
{lblThreshold.Text = Speed_Threshold.ToString();
}));

那里lblThreshold是一个Label,而Speed_Threshold是一个全局变量。


#12楼

当您进入UI线程时,可以要求其提供同步上下文任务计划程序。 这将为您提供一个TaskScheduler ,以调度UI线程上的所有内容。

然后,您可以链接任务,以便在结果准备好后,再由另一个任务(在UI线程上安排)将其选中并将其分配给标签。

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;}
}

这适用于任务(不是线程),这是现在编写并发代码的首选方式 。


#13楼

绝大多数答案使用Control.Invoke ,这是一种等待发生的竞赛条件 。 例如,考虑接受的答案:

string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate { someLabel.Text = newText; // runs on UI thread
});

如果用户在此之前关闭窗体。调用this.Invoke (请记住, thisForm对象),则可能会引发ObjectDisposedException

解决方案是使用SynchronizationContext ,特别是使用hamilton.danielb建议的SynchronizationContext.Current (其他答案依赖于完全不需要的特定SynchronizationContext实现)。 我会稍微修改一下他的代码以使用SynchronizationContext.Post而不是SynchronizationContext.Send (因为通常不需要辅助线程等待):

public partial class MyForm : Form
{private readonly SynchronizationContext _context;public MyForm(){_context = SynchronizationContext.Current...}private MethodOnOtherThread(){..._context.Post(status => someLabel.Text = newText,null);}
}

请注意,在.NET 4.0及更高版本上,您实际上应该将任务用于异步操作。 请参阅n-san的答案以获取基于任务的等效方法(使用TaskScheduler.FromCurrentSynchronizationContext )。

最终,在.NET 4.5及更高版本上,您还可以使用Progress<T> (基本上捕获SynchronizationContext.Current在创建时),这由RyszardDżegan演示,用于长时间运行的操作需要在运行时仍运行UI代码的情况。


#14楼

我刚刚阅读了答案,这似乎是一个非常热门的话题。 我目前正在使用.NET 3.5 SP1和Windows窗体。

在前面的答案中大大描述了一个众所周知的公式,该公式利用了InvokeRequired属性,它涵盖了大多数情况,但没有涵盖整个情况。

如果尚未创建句柄怎么办?

如果调用是从不是GUI线程的线程进行的,则此处描述的InvokeRequired属性(对MSDN的Control.InvokeRequired属性引用)将返回true;如果调用是从GUI线程进行的,或者如果Handle是尚未创建。

如果您想让一个模式窗体显示并由另一个线程更新,则可能会遇到异常。 因为您希望该表格以模态显示,所以您可以执行以下操作:

private MyForm _gui;public void StartToDoThings()
{_gui = new MyForm();Thread thread = new Thread(SomeDelegate);thread.Start();_gui.ShowDialog();
}

委托可以在GUI上更新Label:

private void SomeDelegate()
{// Operations that can take a variable amount of time, even no time//... then you update the GUIif(_gui.InvokeRequired)_gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });else_gui.Label1.Text = "Done!";
}

如果标签更新之前的操作“花费更少的时间”(读取并解释为简化操作)比GUI线程创建FormHandle花费的时间少,则可能导致InvalidOperationException 。 这发生在ShowDialog()方法中。

您还应该像这样检查句柄

private void SomeDelegate()
{// Operations that can take a variable amount of time, even no time//... then you update the GUIif(_gui.IsHandleCreated)  //  <---- ADDEDif(_gui.InvokeRequired)_gui.Invoke((Action)delegate { _gui.Label1.Text = "Done!"; });else_gui.Label1.Text = "Done!";
}

如果尚未创建Handle,则可以处理要执行的操作:您可以忽略GUI更新(如上面的代码所示),也可以等待(风险更大)。 这应该可以回答这个问题。

可选内容:我个人想出了以下代码:

public class ThreadSafeGuiCommand
{private const int SLEEPING_STEP = 100;private readonly int _totalTimeout;private int _timeout;public ThreadSafeGuiCommand(int totalTimeout){_totalTimeout = totalTimeout;}public void Execute(Form form, Action guiCommand){_timeout = _totalTimeout;while (!form.IsHandleCreated){if (_timeout <= 0) return;Thread.Sleep(SLEEPING_STEP);_timeout -= SLEEPING_STEP;}if (form.InvokeRequired)form.Invoke(guiCommand);elseguiCommand();}
}

我使用此ThreadSafeGuiCommand的实例来提供由另一个线程更新的表单,并定义如下方法来更新GUI(在我的Form中):

public void SetLabeTextTo(string value)
{_threadSafeGuiCommand.Execute(this, delegate { Label1.Text = value; });
}

通过这种方式,我非常确定我将使GUI进行更新,无论发出呼叫的线程是什么,可以选择等待明确定义的时间(超时)。


#15楼

只需使用如下代码:

 this.Invoke((MethodInvoker)delegate{progressBar1.Value = e.ProgressPercentage; // runs on UI thread});

#16楼

出于许多目的,它就像这样简单:

public delegate void serviceGUIDelegate();
private void updateGUI()
{this.Invoke(new serviceGUIDelegate(serviceGUI));
}

“ serviceGUI()”是表单(this)中的GUI级别方法,可以根据需要更改任意数量的控件。 从另一个线程调用“ updateGUI()”。 可以添加参数以传递值,或者(如果更快)在访问它们的线程之间可能发生冲突而导致不稳定的情况下,根据需要使用带有锁的类范围变量。 如果非GUI线程时间紧迫,请使用BeginInvoke而不是Invoke(牢记Brian Gideon的警告)。


#17楼

由于场景的琐碎性,我实际上将获得状态的UI线程轮询。 我认为您会发现它可能非常优雅。

public class MyForm : Form
{private volatile string m_Text = "";private System.Timers.Timer m_Timer;private MyForm(){m_Timer = new System.Timers.Timer();m_Timer.SynchronizingObject = this;m_Timer.Interval = 1000;m_Timer.Elapsed += (s, a) => { MyProgressLabel.Text = m_Text; };m_Timer.Start();var thread = new Thread(WorkerThread);thread.Start();}private void WorkerThread(){while (...){// Periodically publish progress information.m_Text = "Still working...";}}
}

该方法避免了使用ISynchronizeInvoke.InvokeISynchronizeInvoke.BeginInvoke方法时所需的编组操作。 使用封送处理技术没有错,但是您需要注意一些警告。

  • 确保您不要太频繁地调用BeginInvoke否则它可能会超出消息泵。
  • 在辅助线程上调用Invoke是阻塞调用。 它将暂时中止该线程中正在进行的工作。

我在这个答案中提出的策略颠倒了线程的通信角色。 UI线程会轮询它,而不是辅助线程推送数据。 这是在许多情况下使用的常见模式。 因为您要做的只是显示工作线程中的进度信息,所以我认为您会发现该解决方案是封送处理解决方案的绝佳替代方案。 具有以下优点。

  • UI和工作线程保持松散耦合,而不是将它们紧密耦合的Control.InvokeControl.BeginInvoke方法。
  • UI线程不会妨碍工作线程的进度。
  • 工作线程无法控制UI线程更新所花费的时间。
  • UI和辅助线程执行操作的时间间隔可以保持独立。
  • 工作线程无法超出UI线程的消息泵。
  • UI线程决定了UI更新的时间和频率。

#18楼

线程代码通常有漏洞,并且总是很难测试。 您无需编写线程代码即可从后台任务更新用户界面。 只需使用BackgroundWorker类来运行任务及其ReportProgress方法即可更新用户界面。 通常,您只报告完成百分比,但是还有一个包含状态对象的重载。 这是一个仅报告字符串对象的示例:

    private void button1_Click(object sender, EventArgs e){backgroundWorker1.WorkerReportsProgress = true;backgroundWorker1.RunWorkerAsync();}private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e){Thread.Sleep(5000);backgroundWorker1.ReportProgress(0, "A");Thread.Sleep(5000);backgroundWorker1.ReportProgress(0, "B");Thread.Sleep(5000);backgroundWorker1.ReportProgress(0, "C");}private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e){label1.Text = e.UserState.ToString();}

如果您始终想更新相同的字段,那很好。 如果要进行更复杂的更新,则可以定义一个类来表示UI状态并将其传递给ReportProgress方法。

最后一件事,请确保设置WorkerReportsProgress标志,否则ReportProgress方法将被完全忽略。


#19楼

.NET 3.5及以上版本的一劳永逸扩展方法

using System;
using System.Windows.Forms;public static class ControlExtensions
{/// <summary>/// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread./// </summary>/// <param name="control"></param>/// <param name="code"></param>public static void UIThread(this Control @this, Action code){if (@this.InvokeRequired){@this.BeginInvoke(code);}else{code.Invoke();}}
}

可以使用以下代码行来调用它:

this.UIThread(() => this.myLabel.Text = "Text Goes Here");

#20楼

在这个问题上,大多数其他答案对我来说有点复杂(我是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 UItxt.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 workstxtLog.Dispatcher.BeginInvoke((Action)(() => txtLog.Text = "my message"));
}

我还没有找出上面这行的意思,但是它有效。

对于WinForms

解:

txtLog.Invoke((MethodInvoker)delegate
{txtLog.Text = "my message";
});

#21楼

这个类似于上面使用.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");

#22楼

您必须确保更新发生在正确的线程上。 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控件未实现ISynchronizeInvoke接口,因此上面的代码不适用于WPF项目。

为了确保上面的代码可与Windows Forms和WPF以及所有其他平台一起使用,可以查看AsyncOperationAsyncOperationManagerSynchronizationContext类。

为了以这种方式轻松引发事件,我创建了一个扩展方法,该方法允许我通过调用以下方法简化引发事件:

MyEvent.Raise(this, EventArgs.Empty);

当然,您也可以使用BackGroundWorker类,该类将为您抽象此问题。


#23楼

简单的解决方案是使用Control.Invoke

void DoSomething()
{if (InvokeRequired) {Invoke(new MethodInvoker(updateGUI));} else {// Do SomethingupdateGUI();}
}void updateGUI() {// update gui here
}

#24楼

您需要在GUI线程上调用该方法。 您可以通过调用Control.Invoke来实现。

例如:

delegate void UpdateLabelDelegate (string message);void UpdateLabel (string message)
{if (InvokeRequired){Invoke (new UpdateLabelDelegate (UpdateLabel), message);return;}MyLabelControl.Text = message;
}

#25楼

最简单的方法是将匿名方法传递给Label.Invoke

// Running on the worker thread
string newText = "abc";
form.Label.Invoke((MethodInvoker)delegate {// Running on the UI threadform.Label.Text = newText;
});
// Back on the worker thread

请注意, Invoke阻止执行直到完成为止-这是同步代码。 这个问题并没有询问异步代码,但是您想了解异步代码时,Stack Overflow上有很多内容涉及编写异步代码。


#26楼

这是您应该执行的经典方式:

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 exceptionif (this.InvokeRequired){this.BeginInvoke((MethodInvoker)delegate{OnWorkerProgressChanged(sender, e);});return;}// Change controlthis.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线程从另一个线程开始进行工作,并连接该工作程序事件,以便您可以显示工作程序线程的状态。

然后,在用户界面中,您需要跨线程来更改实际控件……例如标签或进度条。


#27楼

对于.NET 2.0,这是我编写的大量代码,完全可以满足您的要求,并且可以用于Control上的任何属性:

private delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue);public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{if (control.InvokeRequired){control.Invoke(new SetControlPropertyThreadSafeDelegate               (SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });}else{control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });}
}

这样称呼它:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

如果您使用的是.NET 3.0或更高版本,则可以将上述方法重写为Control类的扩展方法,从而将调用简化为:

myLabel.SetPropertyThreadSafe("Text", status);

2010年5月10日更新:

对于.NET 3.0,您应该使用以下代码:

private delegate void SetPropertyThreadSafeDelegate<TResult>(Control @this, Expression<Func<TResult>> property, TResult value);public static void SetPropertyThreadSafe<TResult>(this Control @this, Expression<Func<TResult>> property, TResult value)
{var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;if (propertyInfo == null ||!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||@this.GetType().GetProperty(propertyInfo.Name, propertyInfo.PropertyType) == null){throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");}if (@this.InvokeRequired){@this.Invoke(new SetPropertyThreadSafeDelegate<TResult> (SetPropertyThreadSafe), new object[] { @this, property, value });}else{@this.GetType().InvokeMember(propertyInfo.Name, BindingFlags.SetProperty, null, @this, new object[] { value });}
}

它使用LINQ和lambda表达式允许更简洁,更简单和更安全的语法:

myLabel.SetPropertyThreadSafe(() => myLabel.Text, status); // status has to be a string or this will fail to compile

现在不仅在编译时检查属性名称,而且属性的类型也是如此,因此不可能(例如)为布尔型属性分配字符串值,从而导致运行时异常。

不幸的是,这不会阻止任何人做一些愚蠢的事情,例如传递另一个Control的属性和值,因此可以很高兴地编译以下内容:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

因此,我添加了运行时检查,以确保传递的属性确实属于调用该方法的Control 。 虽然不完美,但仍比.NET 2.0版本好很多。

如果有人对如何提高此代码的编译时安全性有任何进一步的建议,请发表评论!


#28楼

这是我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 });elsepropertyInfo.SetValue(control, value, null);
}

您这样称呼它:

myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!")
  1. 它将空检查添加到“作为MemberExpression”的结果中。
  2. 它提高了静态类型安全性。

否则,原始版本是一个非常好的解决方案。


#29楼

Salvete! 搜索了这个问题之后,我发现FrankGOregon Ghost的答案对我来说是最简单,最有用的。 现在,我用Visual Basic编写代码,并通过转换器运行此代码段。 所以我不确定结果如何。

我有一个名为form_Diagnostics,的对话框form_Diagnostics,其中有一个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

#30楼

我的版本是插入一行递归“咒语”:

没有参数:

    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!}

那就是它


一些争论 :通常,将代码{}放​​在if ()语句后一行不利于代码的可读性。 但是在这种情况下,这是常规的“咒语”。 如果此方法在项目中一致,则不会破坏代码的可读性。 而且可以节省乱码(一行而不是五行)。

正如您看到if(InvokeRequired) {something long}您只知道“从另一个线程调用该函数很安全”。

如何从另一个线程更新GUI?相关推荐

  1. .NET一个线程更新另一个线程的UI(两种实现方法及若干简化)

    本片博文接上一篇:.NET多线程执行函数,给出实现一个线程更新另一个线程UI的两种方法. Winform中的控件是绑定到特定的线程的(一般是主线程),这意味着从另一个线程更新主线程的控件不能直接调用该 ...

  2. android listview多线程刷新各自的进度,从另一个线程更新主线程中的ListView

    Yar.. 12 我个人使用自定义线程(扩展线程的类),但通过Message发送响应给UI线程.所以在线程的run()函数中有: Message msg; msg = Message.obtain() ...

  3. SWT中非UI线程更新UI的方法

    有时用swt会需要在费时的操作后更新ui,如果直接在回调函数里更新,界面会卡死,另起一个线程更新,会出现异常.这时就可以用Display类中的asyncExec或者syncExec方法. public ...

  4. wxpython 多线程_在wxPython中使用线程连续更新GUI的好方法?

    我正在开发一个使用pythonv2.7和wxpythonv3.0的GUI应用程序. 我必须不断更新我的图形用户界面,其中包含许多面板.每个面板包含一个wx.StaticText.我必须不断更新这些wx ...

  5. 为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区...

    开门见山: 这个误区是:子线程不能更新 UI ,其应该分类讨论,而不是绝对的. 半小时前,我的 XRecyclerView 群里面,一位群友私聊我,问题是: 为什么我的子线程更新了 UI 没报错? 我 ...

  6. wxpython后台如何更新界面信息_wxpython后台线程更新界面控件方法

    在大多数的GUI应用程序中,在应用程序的后台中长期执行一个处理过程而不干涉用户与应用程序的其它部分的交互是有好处的.允许后台处理的机制通常是产生一个线程并在该线程中长期执行一个处理过程.对于wxPyt ...

  7. 同步上下文(SynchronizationContext) 和 C#中跨线程更新UI的方法总结

    同步上下文(SynchronizationContext) 和 C#中跨线程更新UI的方法总结 在C#编程环境中,不允许在工作线程中直接对主线程(UI线程)中的控件进行更新操作.因此,稍微复杂一点的程 ...

  8. wxpython后台线程更新界面控件方法

    http://blog.sina.com.cn/s/blog_56146dc501009ea9.html http://www.th7.cn/Program/Python/201303/128088. ...

  9. 一个线程罢工的诡异事件

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 背景 事情(事故)是这样的,突然收到报警,线上某个应用里业务逻辑没 ...

最新文章

  1. 链表的基本操作 java_JAVA实现单链表的基本操作
  2. JoinGroup的过程
  3. 逆推继承看原型 函数的角色 函数声明和函数表达式的区别 函数中this指向的问题
  4. 使用 SAP Cloud SDK 开发应用时,如何通过环境变量的配置避免硬编码
  5. Codeforces 722C. Destroying Array
  6. 软件:分享9款实用电脑软件,值得看一看
  7. webRTC——浏览器里的音视频通话 1
  8. erlang-17版本的编码方式
  9. 设置和清除LD_LIBRARY_PATH
  10. Leetcode 109.有序链表转换二叉搜索树
  11. 【IT之路】LoadRunner系列-Loadrunner 11破解汉化
  12. windows搭建共享文件服务器,利用Windows共享搭建文件服务器
  13. python中读取列表所有元素_python获取所有子元素
  14. Python dataframe绘制饼图_基于Python的图表绘图系统matplotlib,“饼图“”你真了解吗?...
  15. 全球及中国双向可控硅输出光耦行业发展现状及项目战略规划分析报告2021-2027年版
  16. Re:if java for
  17. Deeplab V1
  18. HyperLPR车牌识别库代码分析总结(15)
  19. w ndows7中桌面图标,七仔教你学习Windows 7:桌面初体验
  20. 信息技术教案计算机知识产权,[中学联盟]云南省罗平县第一中学高一《信息技术基础》参考教案:信息技术对人类社会的影响及知识产权 (2份打包)...

热门文章

  1. Android webview 加载https网页显示空白
  2. ScrollView嵌套ListView处理事件冲突
  3. LeetCode 2 两数相加
  4. webpack的使用、安装和配置打包的入口和出口
  5. python 提交form-data之坑
  6. Swift5.1 语言参考(六) 声明
  7. ubuntu——安装和NS3
  8. Two Sum [easy] (Python)
  9. 如何阻止子元素触发父元素的事件
  10. k8s系列---部署集群