Какой самый простой способ обновить Label
из другого Thread
?
У меня
Form
работаетthread1
, и с этого я запускаю другой поток (thread2
).В то время
thread2
как обрабатывает некоторые файлы, я хотел бы обновитьLabel
наForm
текущий статусthread2
работы.
Как я мог это сделать?
c#
.net
multithreading
winforms
user-interface
CruelIO
источник
источник
Ответы:
Для .NET 2.0, вот хороший фрагмент кода, который я написал, который делает именно то, что вы хотите, и работает для любого свойства в
Control
:Назовите это так:
Если вы используете .NET 3.0 или выше, вы можете переписать указанный выше метод как метод расширения
Control
класса, что упростит вызов:ОБНОВЛЕНИЕ 05/10/2010:
Для .NET 3.0 вы должны использовать этот код:
который использует LINQ и лямбда-выражения, чтобы обеспечить более чистый, простой и безопасный синтаксис:
Теперь не только имя свойства проверяется во время компиляции, но и тип свойства, поэтому невозможно (например) присвоить строковое значение логическому свойству и, следовательно, вызвать исключение времени выполнения.
К сожалению, это не мешает никому делать глупости, такие как передача чужого
Control
свойства и значения, поэтому с радостью скомпилируем следующее:Поэтому я добавил проверки во время выполнения, чтобы убедиться, что переданное свойство действительно принадлежит тому,
Control
который вызывается методом. Не идеально, но все же намного лучше, чем версия .NET 2.0.Если у кого-то есть какие-либо дальнейшие предложения о том, как улучшить этот код для безопасности во время компиляции, пожалуйста, прокомментируйте!
источник
SetControlPropertyThreadSafe(myLabel, "Text", status)
можно вызвать из другого модуля, класса или формыСамый простой способ - это анонимный метод, переданный в
Label.Invoke
:Обратите внимание, что
Invoke
блокирует выполнение до его завершения - это синхронный код. Вопрос не касается асинхронного кода, но в Stack Overflow есть много информации о написании асинхронного кода, когда вы хотите узнать о нем.источник
Обработка долгой работы
Начиная с .NET 4.5 и C # 5.0, вы должны использовать основанный на задачах асинхронный шаблон (TAP) вместе с асинхронными - ожидать ключевые слова во всех областях (включая GUI):
вместо модели асинхронного программирования (APM) и асинхронного шаблона на основе событий (EAP) (последний включает класс BackgroundWorker ).
Тогда рекомендуемое решение для новой разработки:
Асинхронная реализация обработчика событий (да, вот и все):
Реализация второго потока, который уведомляет поток пользовательского интерфейса:
Обратите внимание на следующее:
Для более подробного примера смотрите: Будущее C #: Хорошие вещи приходят к тем , кто «Await» от Джозефа Albahari .
Смотрите также о концепции UI Threading Model .
Обработка исключений
Приведенный ниже фрагмент кода является примером того, как обрабатывать исключения и
Enabled
свойство кнопки-переключателя, чтобы предотвратить многократные щелчки во время фонового выполнения.источник
SecondThreadConcern.LongWork()
выдается исключение, может ли оно быть перехвачено потоком пользовательского интерфейса? Это отличный пост, кстати.Task.Delay(500).Wait()
? Какой смысл создавать Задачу, чтобы просто заблокировать текущий поток? Вы никогда не должны блокировать поток пула потоков!Вариация простейшего решения Марка Гравелла для .NET 4:
Или используйте вместо этого делегат Action:
Смотрите здесь для сравнения двух: MethodInvoker vs Action for Control.BeginInvoke
источник
this.refresh()
принудительно сделать недействительным и перекрасить GUI .. если это будет полезно ..Запустите и забудьте метод расширения для .NET 3.5+
Это можно вызвать с помощью следующей строки кода:
источник
@this
просто имя переменной, в данном случае ссылка на текущий элемент управления, вызывающий расширение. Вы можете переименовать его в источник, или что-то еще, что плавает на вашей лодке. Я использую@this
, потому что он ссылается на «этот элемент управления», который вызывает расширение и соответствует (по крайней мере, в моей голове) использованию ключевого слова «это» в обычном (не являющемся расширением) коде.OnUIThread
а неUIThread
.RunOnUiThread
. Но это только личный вкус.Это классический способ сделать это:
В вашем рабочем потоке есть событие. Ваш поток пользовательского интерфейса запускает другой поток для выполнения работы и подключает это рабочее событие, чтобы вы могли отобразить состояние рабочего потока.
Затем в пользовательском интерфейсе вам нужно пересечь потоки, чтобы изменить фактический элемент управления ... как метку или индикатор выполнения.
источник
Простое решение заключается в использовании
Control.Invoke
.источник
Потоковый код часто глючит и всегда трудно проверить. Вам не нужно писать многопоточный код для обновления пользовательского интерфейса из фоновой задачи. Просто используйте класс BackgroundWorker для запуска задачи и его метод ReportProgress для обновления пользовательского интерфейса. Обычно вы просто сообщаете, что процент выполнения завершен, но есть еще одна перегрузка, которая включает объект состояния. Вот пример, который просто сообщает о строковом объекте:
Это хорошо, если вы всегда хотите обновить одно и то же поле. Если вам нужно сделать более сложные обновления, вы можете определить класс для представления состояния пользовательского интерфейса и передать его в метод ReportProgress.
И последнее: обязательно установите
WorkerReportsProgress
флаг, иначеReportProgress
метод будет полностью проигнорирован.источник
backgroundWorker1_RunWorkerCompleted
.Подавляющее большинство ответов используют условия гонки, ожидающие
Control.Invoke
своего появления . Например, рассмотрим принятый ответ:Если пользователь закрывает форму непосредственно перед
this.Invoke
вызовом (помните,this
является лиForm
объект),ObjectDisposedException
скорее всего, будет запущен.Решение заключается в использовании
SynchronizationContext
, в частности,SynchronizationContext.Current
как предлагает hamilton.danielb (другие ответы зависят от конкретныхSynchronizationContext
реализаций, что совершенно не нужно). Я бы немного изменил его код для использования,SynchronizationContext.Post
а не для этогоSynchronizationContext.Send
(поскольку обычно рабочему потоку не нужно ждать):Обратите внимание, что в .NET 4.0 и выше вы действительно должны использовать задачи для асинхронных операций. Смотрите ответ n-san для эквивалентного подхода на основе задач (используя
TaskScheduler.FromCurrentSynchronizationContext
).Наконец, в .NET 4.5 и более поздних версиях вы также можете использовать
Progress<T>
(что в основном фиксируетсяSynchronizationContext.Current
при его создании), как продемонстрировал Рышард Деган (Ryszard Dżegan), для случаев, когда при длительной операции необходимо запускать код пользовательского интерфейса во время работы.источник
Вы должны убедиться, что обновление происходит в правильном потоке; поток пользовательского интерфейса.
Для этого вам придется вызывать обработчик событий, а не вызывать его напрямую.
Вы можете сделать это, подняв ваше событие следующим образом:
(Код набран здесь из моей головы, поэтому я не проверял правильный синтаксис и т. Д., Но он должен помочь вам.)
Обратите внимание, что приведенный выше код не будет работать в проектах WPF, поскольку элементы управления WPF не реализуют
ISynchronizeInvoke
интерфейс.Для того , чтобы убедиться , что код выше работы с Windows Forms и WPF, а также всех других платформ, вы можете взглянуть на
AsyncOperation
,AsyncOperationManager
иSynchronizationContext
классы.Чтобы легко вызывать события таким образом, я создал метод расширения, который позволяет мне упростить вызов события, просто вызвав:
Конечно, вы также можете использовать класс BackGroundWorker, который будет абстрагироваться от вас.
источник
Вам нужно будет вызвать метод в потоке GUI. Вы можете сделать это, вызвав Control.Invoke.
Например:
источник
Из-за тривиальности сценария у меня фактически был бы опрос потока пользовательского интерфейса для статуса. Я думаю, вы найдете, что это может быть довольно элегантно.
Такой подход позволяет избежать операции маршалинга , необходимые при использовании
ISynchronizeInvoke.Invoke
иISynchronizeInvoke.BeginInvoke
методов. Нет ничего плохого в использовании техники маршалинга, но есть несколько предостережений, о которых нужно знать.BeginInvoke
слишком часто, иначе это может привести к переполнению сообщения.Invoke
в рабочем потоке является блокирующим вызовом. Это временно остановит работу, выполняемую в этой теме.Стратегия, которую я предлагаю в этом ответе, меняет коммуникационные роли потоков. Вместо того, чтобы рабочий поток выдвигал данные, поток пользовательского интерфейса опрашивает их. Это общий шаблон, используемый во многих сценариях. Поскольку все, что вы хотите сделать, это отображать информацию о ходе выполнения из рабочего потока, я думаю, вы обнаружите, что это решение является отличной альтернативой маршалинг-решению. Это имеет следующие преимущества.
Control.Invoke
илиControl.BeginInvoke
подход, который тесно связывает их.источник
Elapsed
события, вы используете метод member, чтобы вы могли удалить таймер при удалении формы ...System.Timers.ElapsedEventHandler handler = (s, a) => { MyProgressLabel.Text = m_Text; };
и назначить его черезm_Timer.Elapsed += handler;
, позже в контексте утилизации, делаюm_Timer.Elapsed -= handler;
я правильно? И для утилизации / закрытия, следуя советам, которые обсуждались здесь .В предыдущих ответах не требуется ничего из Invoke.
Вам нужно взглянуть на WindowsFormsSynchronizationContext:
источник
Это похоже на решение выше с использованием .NET Framework 3.0, но оно решило проблему обеспечения безопасности во время компиляции .
Использовать:
Компилятор потерпит неудачу, если пользователь передаст неверный тип данных.
источник
Salvete! Поискав этот вопрос, я обнаружил, что ответы FrankG и Oregon Ghost были самыми простыми и полезными для меня. Теперь я кодирую в Visual Basic и запускаю этот фрагмент через конвертер; так что я не совсем уверен, как это получается.
У меня есть диалоговая форма,
form_Diagnostics,
которая называется richtext box,updateDiagWindow,
который я использую для отображения журнала. Мне нужно было иметь возможность обновить его текст из всех тем. Дополнительные строки позволяют окну автоматически прокручиваться до самых новых строк.Итак, теперь я могу обновить отображение одной строкой из любой точки всей программы так, как вы думаете, она будет работать без каких-либо потоков:
Основной код (поместите его в код класса вашей формы):
источник
Для многих целей это так просто:
«serviceGUI ()» - это метод уровня GUI в форме (this), который может изменять столько элементов управления, сколько вы хотите. Вызовите updateGUI () из другого потока. Параметры могут быть добавлены для передачи значений или (возможно, быстрее) использовать переменные области видимости класса с блокировками для них по мере необходимости, если есть какая-либо вероятность столкновения между потоками, обращающимися к ним, что может вызвать нестабильность. Используйте BeginInvoke вместо Invoke, если не-GUI-поток критичен ко времени (учитывая предупреждение Брайана Гидеона).
источник
Это в моем C # 3.0 варианте решения Яна Кемпа:
Вы называете это так:
В противном случае оригинал - очень хорошее решение.
источник
Обратите внимание, что
BeginInvoke()
это предпочтительнее,Invoke()
потому что это менее вероятно, чтобы вызвать взаимоблокировки (однако, это не проблема здесь, просто назначая текст метке):При использовании
Invoke()
вы ждете возврата метода. Теперь может случиться так, что вы делаете что-то в вызываемом коде, который должен будет ожидать поток, что может быть неочевидно, если он скрыт в некоторых вызываемых вами функциях, что само по себе может происходить косвенно через обработчики событий. Таким образом, вы будете ждать нити, нить будет ждать вас, и вы зашли в тупик.Это фактически привело к зависанию некоторых из наших выпущенных программ. Это было достаточно легко исправить, заменив
Invoke()
наBeginInvoke()
. Если вам не нужна синхронная работа, которая может иметь место, если вам нужно возвращаемое значение, используйтеBeginInvoke()
.источник
Когда я столкнулся с той же проблемой, я обратился за помощью к Google, но вместо того, чтобы дать мне простое решение, он смутил меня больше, приводя примеры
MethodInvoker
и бла-бла-бла. Поэтому я решил решить это самостоятельно. Вот мое решение:Сделайте делегата следующим образом:
Вы можете вызвать эту функцию в новом потоке, как это
Не путайся с
Thread(() => .....)
. Я использую анонимную функцию или лямбда-выражение, когда я работаю над потоком. Чтобы уменьшить количество строк кода, вы также можете использоватьThreadStart(..)
метод, который я не должен здесь объяснять.источник
Просто используйте что-то вроде этого:
источник
e.ProgressPercentage
, не вы уже в потоке пользовательского интерфейса от метода, который вы вызываете это?Вы можете использовать уже существующий делегат
Action
:источник
Моя версия - вставить одну строку рекурсивной «мантры»:
Без аргументов:
Для функции, которая имеет аргументы:
ЭТО ЭТО .
Некоторые аргументы . Обычно читаемость кода плохо ставить {} после
if ()
оператора в одну строку. Но в данном случае это обычная все та же «мантра». Это не нарушает читабельность кода, если этот метод согласован в проекте. И это спасает ваш код от мусора (одна строка кода вместо пяти).Как видите,
if(InvokeRequired) {something long}
вы просто знаете, что «эту функцию безопасно вызывать из другого потока».источник
Попробуйте обновить этикетку, используя это
источник
Создайте переменную класса:
Установите его в конструкторе, который создает ваш пользовательский интерфейс:
Когда вы хотите обновить ярлык:
источник
Вы должны использовать invoke и делегировать
источник
Большинство других ответов немного сложны для меня в этом вопросе (я новичок в C #), поэтому я пишу свои:
У меня есть приложение WPF, и я определил работника, как показано ниже:
Выпуск:
Решение:
Мне еще предстоит выяснить, что означает приведенная выше строка, но она работает.
Для WinForms :
Решение:
источник
Самый простой способ, я думаю:
источник
Например, получить доступ к элементу управления, кроме текущего потока:
Это
lblThreshold
метка иSpeed_Threshold
глобальная переменная.источник
Когда вы находитесь в потоке пользовательского интерфейса, вы можете запросить у него планировщик задач контекста синхронизации. Это даст вам TaskScheduler, который планирует все в потоке пользовательского интерфейса.
Затем вы можете связать свои задачи так, чтобы, когда результат был готов, другая задача (которая запланирована в потоке пользовательского интерфейса) выбрала ее и присвоила метке.
Это работает для задач (не потоков), которые сейчас являются предпочтительным способом написания параллельного кода .
источник
Task.Start
как правило , не является хорошей практикой blogs.msdn.com/b/pfxteam/archive/2012/01/14/10256832.aspxЯ только что прочитал ответы, и это, кажется, очень горячая тема. В настоящее время я использую .NET 3.5 SP1 и Windows Forms.
Хорошо известная формула, в значительной степени описанная в предыдущих ответах, которая использует свойство InvokeRequired, охватывает большинство случаев, но не весь пул.
Что, если дескриптор еще не создан?
Свойство InvokeRequired , как описано здесь (ссылка на свойство Control.InvokeRequired для MSDN), возвращает true, если вызов был сделан из потока, который не является потоком GUI, или false, если вызов был сделан из потока GUI, или если Handle был еще не создано.
Вы можете встретить исключение, если хотите, чтобы модальная форма отображалась и обновлялась другим потоком. Поскольку вы хотите, чтобы эта форма отображалась модально, вы можете сделать следующее:
И делегат может обновить метку в графическом интерфейсе:
Это может привести к InvalidOperationException , если операции перед обновлением лейбла «занимает меньше времени» (читать и интерпретировать его как упрощением) , чем время, необходимое для GUI потока , чтобы создать форму «s Handle . Это происходит внутри метода ShowDialog () .
Вы также должны проверить дескриптор следующим образом:
Вы можете обрабатывать выполняемую операцию , если ручка не была создана еще: Вы можете просто игнорировать обновление GUI (как показано в коде выше) , или вы можете ждать (более рискованными). Это должно ответить на вопрос.
Необязательный материал: лично я придумал кодировать следующее:
Я передаю свои формы, которые обновляются другим потоком, экземпляром этого ThreadSafeGuiCommand , и я определяю методы, которые обновляют графический интерфейс (в моей форме) следующим образом:
Таким образом, я совершенно уверен, что мой GUI будет обновлен независимо от того, какой поток будет выполнять вызов, при желании в течение определенного времени (тайм-аута).
источник