Скорее всего, вы неправильно используете длинную операцию в основном потоке пользовательского интерфейса, которая предотвращает обновление метки. Вы можете «исправить» это по телефону DoEvents():
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
Application.DoEvents();
}
Но это просто лейкопластырь на вершине плохой дизайн. Вы должны правильно переместить этот код в фоновый поток и использовать делегат/Invoke() для обновления метки.
Edit: (отвечая на вопрос последующую)
По умолчанию приложение выполняется в одном потоке. Это включает в себя код, который вы добавляете для управления событиями, а также код, который вы не видите, который работает за кулисами, чтобы ваше приложение реагировало так, как вы ожидали. Такие вещи, как взаимодействие с пользователем (щелчки мыши, нажатия клавиш и т. Д.) И письма с картинками (когда элементы управления изменены, ваше окно скрыто) помещаются в очередь. Эти ожидающие сообщения в очереди обрабатываются только после прекращения работы вашего кода. Если у вас длинный фрагмент кода, например длинный, то эти сообщения просто сидят в очереди, ожидая обработки. Таким образом, обновление метки не происходит до тех пор, пока не будет выполнен ваш цикл. Что делает DoEvents(), это приложение сообщает об обработке этих ожидающих сообщений в очереди прямо сейчас, а затем возвращается к исполняемому в данный момент коду. Это позволяет обновлять ярлык в режиме реального времени, как вы ожидаете.
Когда вы сталкиваетесь с ситуациями, которые «фиксируются» с помощью DoEvents(), это просто означает, что вы пытаетесь запустить слишком много кода в основном потоке пользовательского интерфейса. Предполагается, что основной поток пользовательского интерфейса будет сфокусирован на реагировании на взаимодействие пользователя и обновлении дисплея. Код в обработчиках контрольных событий должен быть коротким и сладким, так что основной поток пользовательского интерфейса может вернуться к выполнению основной работы.
Правильное исправление заключается в том, чтобы переместить этот длинный код в другой поток, тем самым позволяя основному потоку пользовательского интерфейса реагировать и постоянно обновляться. Для многих сценариев самый простой подход - разместить в вашей форме элемент управления BackgroundWorker() и подключить события DoWork(), ProgressChanged() и RunWorkerCompleted(). * Вы должны установить для свойства WorkerReportsProgress() значение true, однако, для обработки события ProgressChanged(). Последние два события уже привязаны к основному потоку пользовательского интерфейса для вас, поэтому вам не нужно беспокоиться о перекрестных потоках. Из обработчика DoWork() вы вызываете ReportProgress() и передаете процентное значение прогресса и необязательный другой объект (это может быть что угодно). Эти значения можно получить в событии ProgressChanged() и использовать для обновления GUI. Событие RunWorkerCompleted() запускается, когда все работы в обработчике DoWork() завершены.
В вашем случае у вас есть отдельный класс, который выполняет работу. Вы можете отразить то, что делает BackgroundWorker, вручную создав собственный поток в этом классе для выполнения этой работы. Если вы хотите обновить прогресс, сделайте свой класс поднимите Custom Event
, на который подписана основная форма.Однако, когда это событие получено, оно будет работать в контексте отдельного потока. Поэтому необходимо «маршалить» вызов по границам потока, чтобы код работал в основном потоке пользовательского интерфейса, прежде чем обновлять элементы управления. Это достигается с помощью делегатов («указатели» на методы) и метода Invoke(). * Существуют и другие методы для выполнения этой задачи, такие как SynchronizationContext.
См. here для некоторых примеров этих подходов.
Наконец, вот это супер простой пример класса, который вызывает пользовательские события из отдельного потока:
public partial class Form1 : Form
{
private Clock Clk;
public Form1()
{
InitializeComponent();
Clk = new Clock();
Clk.CurrentTime += new Clock.TimeHack(Clk_CurrentTime);
}
private void Clk_CurrentTime(string hack)
{
if (label1.InvokeRequired)
{
Clock.TimeHack t = new Clock.TimeHack(Clk_CurrentTime);
label1.Invoke(t, new object[] { hack });
}
else
{
label1.Text = hack;
}
}
}
public class Clock
{
public delegate void TimeHack(string hack);
public event TimeHack CurrentTime;
private Thread t;
private bool stopThread = false;
public Clock()
{
t = new Thread(new ThreadStart(ThreadLoop));
t.IsBackground = true; // allow it to be shutdown automatically when the application exits
t.Start();
}
private void ThreadLoop()
{
while (!stopThread)
{
if (CurrentTime != null)
{
CurrentTime(DateTime.Now.ToString());
}
System.Threading.Thread.Sleep(1000);
}
}
public void Stop()
{
stopThread = true;
}
}
Его в другом потоке. –
@NipunAmbastha: Я так не думаю, если бы это было так, 'UpdateLabel()' -Method будет бросать «CrossThreadMessagingException», потому что нет 'Invoke()'; – jAC
Возможно, 'UpdateLabelClass' должен запускать события при достижении прогресса, а форма подписаться на эти события. – ja72