Как обновить графический интерфейс из другого streamа?

Каков самый простой способ обновить Label из другого streamа?

У меня есть Form on thread1 , и из этого я thread2 другой stream ( thread2 ). Хотя thread2 обрабатывает некоторые файлы, я хотел бы обновить Label в Form с текущим статусом работы thread2 .

Как я могу это сделать?

Для .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 или выше, вы можете переписать вышеуказанный метод как метод расширения classа Control , который затем упростит вызов:

 myLabel.SetPropertyThreadSafe("Text", status); 

ОБНОВЛЕНИЕ 05/10/2010:

Для .NET 3.0 вы должны использовать этот код:

 private delegate void SetPropertyThreadSafeDelegate( Control @this, Expression> property, TResult value); public static void SetPropertyThreadSafe( this Control @this, Expression> property, TResult value) { var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo; if (propertyInfo == null || [email protected]().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 (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); 

Поэтому я добавил проверки времени выполнения, чтобы убедиться, что свойство pass-in действительно принадлежит элементу Control , вызываемому методом. Не идеально, но все же намного лучше, чем версия .NET 2.0.

Если у кого-то есть дополнительные предложения по улучшению этого кода для безопасности во время компиляции, пожалуйста, прокомментируйте!

Самый простой способ – анонимный метод, переданный в 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 блокирует выполнение до его завершения – это синхронный код. Вопрос не спрашивает об асинхронном коде, но есть много контента в Stack Overflow о написании асинхронного кода, когда вы хотите узнать об этом.

Обработка длительной работы

Начиная с .NET 4.5 и C # 5.0 вы должны использовать Асинхронный шаблон на основе задач (TAP) вместе с ключевыми словами async- ожидания во всех областях (включая графический интерфейс):

TAP – рекомендуемый asynchronous шаблон проектирования для новой разработки

вместо асинхронной модели программирования (APM) и асинхронного шаблона на основе событий (EAP) (последний включает в себя class BackgroundWorker ).

Тогда рекомендуемое решение для новой разработки:

  1. Асинхронная реализация обработчика событий (да, это все):

     private async void Button_Clicked(object sender, EventArgs e) { var progress = new Progress(s => label.Text = s); await Task.Factory.StartNew(() => SecondThreadConcern.LongWork(progress), TaskCreationOptions.LongRunning); label.Text = "completed"; } 
  2. Реализация второго streamа, который уведомляет stream пользовательского интерфейса:

     class SecondThreadConcern { public static void LongWork(IProgress progress) { // Perform a long running work... for (var i = 0; i < 10; i++) { Task.Delay(500).Wait(); progress.Report(i.ToString()); } } } 

Обратите внимание на следующее:

  1. Короткий и чистый код, написанный последовательным образом без обратных вызовов и явных streamов.
  2. Задача вместо Thread .
  3. async , что позволяет использовать ожидание, что, в свою очередь, не позволяет обработчику события достичь состояния завершения до завершения задачи и тем временем не блокирует stream пользовательского интерфейса.
  4. Прогресс (см. Интерфейс IProgress ), который поддерживает принцип рассоединения проблем (SoC) и не требует явного диспетчера и вызова. Он использует текущий SynchronizationContext из своего места создания (здесь stream пользовательского интерфейса).
  5. TaskCreationOptions.LongRunning, который намекает, чтобы не ставить очередь задачи в ThreadPool .

Более подробные примеры см .: «Будущее C #: хорошие вещи приходят к тем, кто« ждет » Джозефа Альбахари .

См. Также о концепции модели Threading UI .

Обработка исключений

Нижеприведенный fragment представляет собой пример того, как обрабатывать исключения и включить свойство Enabled чтобы предотвратить множественные щелчки во время выполнения фона.

 private async void Button_Click(object sender, EventArgs e) { button.Enabled = false; try { var progress = new Progress(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 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..."); } } 

Изменение простейшего решения Marc Gravell для .NET 4:

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

Вместо этого используйте делегат Action:

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

См. Здесь для сравнения двух: MethodInvoker vs Action for Control.BeginInvoke

Fire и забыть метод расширения для .NET 3.5+

 using System; using System.Windows.Forms; public static class ControlExtensions { ///  /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread. ///  ///  ///  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"); 

Это classический способ сделать это:

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

У вашего рабочего streamа есть событие. Ваш stream пользовательского интерфейса запускает другой stream для выполнения этой работы и подключает это рабочее событие, чтобы вы могли отображать состояние рабочего streamа.

Затем в пользовательском интерфейсе вам нужно пересечь streamи, чтобы изменить фактический элемент управления … как метку или индикатор выполнения.

Простое решение – использовать Control.Invoke .

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

Код Threading часто глючит и всегда трудно тестировать. Вам не нужно писать код streamа для обновления пользовательского интерфейса из фоновой задачи. Просто используйте class 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(); } 

Это нормально, если вы всегда хотите обновить одно и то же поле. Если у вас есть более сложные обновления, вы можете определить class для представления состояния пользовательского интерфейса и передать его методу ReportProgress.

WorkerReportsProgress , обязательно установите флаг WorkerReportsProgress , ReportProgress метод ReportProgress будет полностью проигнорирован.

Подавляющее большинство ответов использует Control.Invoke который является ожиданием гонки . Например, рассмотрим принятый ответ:

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

Если пользователь закрывает форму непосредственно перед this.Invoke вызывается (помните, что this объект Form ), вероятно, будет this.Invoke .

Решение заключается в использовании SynchronizationContext , в частности SynchronizationContext.Current поскольку hamilton.danielb предлагает (другие ответы полагаются на конкретные реализации SynchronizationContext которые совершенно не нужны). Я бы немного изменил его код, чтобы использовать SynchronizationContext.Post а не SynchronizationContext.Send хотя (так как обычно нет необходимости ждать рабочего streamа):

 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 (который в основном захватывает SynchronizationContext.Current после его создания), как продемонстрировал Ryszard Dżegan для случаев, когда долговременная операция должна запускать код пользовательского интерфейса во время работы.

Вы должны убедиться, что обновление происходит в правильном streamе; stream пользовательского интерфейса.

Для этого вам придется вызывать обработчик событий вместо прямого вызова.

Вы можете сделать это, подняв ваше событие следующим образом:

(Код напечатан здесь из моей головы, поэтому я не проверял правильность синтаксиса и т. Д., Но он должен вас поймать.)

 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 Forms и WPF и всеми другими платформами, вы можете посмотреть AsyncOperation , AsyncOperationManager и SynchronizationContext .

Чтобы легко поднять события таким образом, я создал метод расширения, который позволяет мне упростить создание события, просто позвонив:

 MyEvent.Raise(this, EventArgs.Empty); 

Конечно, вы также можете использовать class BackGroundWorker, который абстрагирует этот вопрос для вас.

Вам нужно вызвать метод в streamе графического интерфейса. Вы можете сделать это, вызвав Control.Invoke.

Например:

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

Из-за тривиальности сценария у меня действительно был бы опрос по пользовательскому интерфейсу для статуса. Я думаю, вы обнаружите, что он может быть довольно элегантным.

 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.Invoke и ISynchronizeInvoke.BeginInvoke . Нет ничего плохого в использовании техники маршалинга, но есть пара предостережений, о которых вам нужно знать.

  • Убедитесь, что вы не вызываете BeginInvoke слишком часто, или он может переполнять насос сообщений.
  • Вызов Invoke в рабочем streamе является блокирующим вызовом. Он временно остановит работу, выполняемую в этом streamе.

Страtagsя, которую я предлагаю в этом ответе, отменяет роли связи streamов. Вместо того, чтобы рабочий stream толкал данные, опрос пользовательского интерфейса. Это общий шаблон, используемый во многих сценариях. Поскольку все, что вы хотите сделать, это отобразить информацию о ходе работы из рабочего streamа, тогда я думаю, вы обнаружите, что это решение является отличной альтернативой решению маршалинга. Он имеет следующие преимущества.

  • Пользовательский интерфейс и рабочие streamи остаются слабо связанными, в отличие от подхода Control.Invoke или Control.BeginInvoke который плотно соединяет их.
  • Нить пользовательского интерфейса не будет препятствовать прогрессу рабочего streamа.
  • Рабочий stream не может доминировать во время обновления streamа пользовательского интерфейса.
  • Интервалы, на которых выполняются операции пользовательского интерфейса и рабочего streamа, могут оставаться независимыми.
  • Рабочий stream не может переполнять stream сообщений streamа пользовательского интерфейса.
  • Поток пользовательского интерфейса определяет, когда и как часто обновляется пользовательский интерфейс.

Ни один из элементов Invoke в предыдущих ответах не требуется.

Вам нужно посмотреть на 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 } 

Для многих целей это так просто:

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

«serviceGUI ()» – это метод уровня GUI в форме (это), который может изменять как можно большее количество элементов управления. Вызовите «updateGUI ()» из другого streamа. Параметры могут быть добавлены для передачи значений или (возможно, быстрее) использования переменных classа classа с блокировками на них по мере необходимости, если есть какая-либо вероятность столкновения между streamами, обращающимися к ним, что может вызвать нестабильность. Используйте BeginInvoke вместо Invoke, если stream без GUI является критическим по времени (учитывая предупреждение Брайана Гидеона).

Это в моей версии C # 3.0 решения Яна Кемпа:

 public static void SetPropertyInGuiThread(this C control, Expression> 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>, V>)SetPropertyInGuiThread, new object[] { control, property, value } ); else propertyInfo.SetValue(control, value, null); } 

Вы называете это следующим образом:

 myButton.SetPropertyInGuiThread(b => b.Text, "Click Me!") 
  1. Он добавляет нулевую проверку к результату «как выражение MemberExpression».
  2. Это улучшает статическую безопасность.

В противном случае оригинал – очень приятное решение.

Это похоже на решение выше, использующее .NET Framework 3.0, но оно решило проблему обеспечения безопасности во время компиляции .

 public static class ControlExtension { delegate void SetPropertyValueHandler(Control souce, Expression> selector, TResult value); public static void SetPropertyValue(this Control source, Expression> selector, TResult value) { if (source.InvokeRequired) { var del = new SetPropertyValueHandler(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"); 

Salvete! Искав этот вопрос, я нашел ответы FrankG и Oregon Ghost самыми легкими для меня самыми полезными. Теперь я кодирую Visual Basic и запускаю этот fragment через конвертер; поэтому я не совсем уверен, как это получается.

У меня есть диалоговая форма, называемая form_Diagnostics, которая имеет поле richtext, называемое updateDiagWindow, которое я использую в качестве своего рода журнала. Мне нужно было обновить его текст из всех streamов. Дополнительные строки позволяют окну автоматически прокручиваться до новейших строк.

Итак, теперь я могу обновить дисплей одной строкой из любой точки всей программы так, как вы думаете, она будет работать без каких-либо streamов:

  form_Diagnostics.updateDiagWindow(whatmessage); 

Основной код (поместите это внутри кода classа вашей формы):

 #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 
 Label lblText; //initialized elsewhere void AssignLabel(string text) { if (InvokeRequired) { BeginInvoke((Action)AssignLabel, text); return; } lblText.Text = text; } 

Обратите внимание, что BeginInvoke() является предпочтительным по сравнению с Invoke() поскольку он менее правдоподобен для возникновения взаимоблокировок (однако это не проблема при просто назначении текста ярлыку):

При использовании Invoke() вы ожидаете возвращения метода. Теперь может быть, что вы что-то делаете в вызываемом коде, который должен будет ждать streamа, что может быть не сразу очевидным, если оно захоронено в некоторых функциях, которые вы вызываете, что само по себе может происходить косвенно через обработчики событий. Таким образом, вы будете ждать streamа, stream будет ждать вас, и вы зашли в тупик.

Это фактически привело к зависанию некоторых из наших выпущенных программ. Это было довольно легко исправить, заменив Invoke() на BeginInvoke() . Если вам не нужна синхронная операция, которая может иметь место, если вам нужно вернуть значение, используйте BeginInvoke() .

When I encountered the same issue I sought help from Google, but rather than give me a simple solution it confused me more by giving examples of MethodInvoker and blah blah blah. So I decided to solve it on my own. Вот мое решение:

Make a delegate like this:

 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 } 

You can call this function in a new thread like this

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

Don’t be confused with Thread(() => .....) . I use an anonymous function or lambda expression when I work on a thread. To reduce the lines of code you can use the ThreadStart(..) method too which I am not supposed to explain here.

Simply use something like this:

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

You may use the already-existing delegate Action :

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

My version is to insert one line of recursive “mantra”:

For no arguments:

  void Aaaaaaa() { if (InvokeRequired) { Invoke(new Action(Aaaaaaa)); return; } //1 line of mantra // Your code! } 

For a function that has arguments:

  void Bbb(int x, string text) { if (InvokeRequired) { Invoke(new Action(Bbb), new[] { x, text }); return; } // Your code! } 

THAT is IT .


Some argumentation : Usually it is bad for code readability to put {} after an if () statement in one line. But in this case it is routine all-the-same “mantra”. It doesn’t break code readability if this method is consistent over the project. And it saves your code from littering (one line of code instead of five).

As you see if(InvokeRequired) {something long} you just know “this function is safe to call from another thread”.

You must use invoke and delegate

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

Try to refresh the label using this

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

Create a class variable:

 SynchronizationContext _context; 

Set it in the constructor that creates your UI:

 var _context = SynchronizationContext.Current; 

When you want to update the label:

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

The easiest way I think:

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

For example, access a control other than in the current thread:

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

There the lblThreshold is a Label and Speed_Threshold is a global variable.

When you’re in the UI thread you could ask it for its synchronization context task scheduler. It would give you a TaskScheduler that schedules everything on the UI thread.

Then you can chain your tasks so that when the result is ready then another task (which is scheduled on the UI thread) picks it and assigns it to a label.

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

This works for tasks (not threads) which are the preferred way of writing concurrent code now .

I just read the answers and this appears to be a very hot topic. I’m currently using .NET 3.5 SP1 and Windows Forms.

The well-known formula greatly described in the previous answers that makes use of the InvokeRequired property covers most of the cases, but not the entire pool.

What if the Handle has not been created yet?

The InvokeRequired property, as described here (Control.InvokeRequired Property reference to MSDN) returns true if the call was made from a thread that is not the GUI thread, false either if the call was made from the GUI thread, or if the Handle was not created yet.

You can come across an exception if you want to have a modal form shown and updated by another thread. Because you want that form shown modally, you could do the following:

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

And the delegate can update a Label on the GUI:

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

This can cause an InvalidOperationException if the operations before the label’s update “take less time” (read it and interpret it as a simplification) than the time it takes for the GUI thread to create the Form ‘s Handle . This happens within the ShowDialog() method.

You should also check for the Handle like this:

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

You can handle the operation to perform if the Handle has not been created yet: You can just ignore the GUI update (like shown in the code above) or you can wait (more risky). This should answer the question.

Optional stuff: Personally I came up coding the following:

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

I feed my forms that get updated by another thread with an instance of this ThreadSafeGuiCommand , and I define methods that update the GUI (in my Form) like this:

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

In this way I'm quite sure that I will have my GUI updated whatever thread will make the call, optionally waiting for a well-defined amount of time (the timeout).

I wanted to add a warning because I noticed that some of the simple solutions omit the InvokeRequired check.

I noticed that if your code executes before the window handle of the control has been created (eg before the form is shown), Invoke throws an exception. So I recommend always checking on InvokeRequired before calling Invoke or BeginInvoke .

  • Android ViewPager с нижними точками
  • В WPF, как я могу определить, является ли элемент управления видимым для пользователя?
  • SwingWorker, Thread.sleep () или javax.swing.timer? Мне нужно «вставить паузу»,
  • Java swing JComponent "размер"
  • Пользовательская строка заголовка без дополнения (Android)
  • Создание пользовательской кнопки в Java с помощью JButton
  • Repeater, ListView, DataList, DataGrid, GridView ... Что выбрать?
  • Как установить фоновое изображение в JPanel
  • Почему мои вещи не отображаются в JFrame?
  • Как определить несколько действий JButton из другого classа
  • Почему мы должны использовать sp для размеров шрифтов в Android?
  • Давайте будем гением компьютера.