2013-05-17 3 views
-1

У меня есть класс, который обновляет графический пользовательский элементНе обновляя GUI времени

public class UpdateLabelClass 
{ 
    static MainGUI theForm = (MainGUI)Application.OpenForms[0]; 
    Label lblCurProgress = theForm.curProgress; 

    public ProgressBarUpdate() 
    { 

    } 
    public void UpdateLabel(String newLabel) 
    { 
     lblCurProgress.Text = newLabel; 
    } 
} 

И в других классах, я делаю экземпляр класса и вызвать UpdateLabel (SomeString);

Теперь проблема в том, что она пропускает операцию по обновлению метки, поэтому я подумал: «Может быть, она даже не дошла до кода», поэтому я сразу же после этого поместил MessageBox.Show() и обновил метка.

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

+0

Его в другом потоке. –

+1

@NipunAmbastha: Я так не думаю, если бы это было так, 'UpdateLabel()' -Method будет бросать «CrossThreadMessagingException», потому что нет 'Invoke()'; – jAC

+0

Возможно, 'UpdateLabelClass' должен запускать события при достижении прогресса, а форма подписаться на эти события. – ja72

ответ

2

Скорее всего, вы неправильно используете длинную операцию в основном потоке пользовательского интерфейса, которая предотвращает обновление метки. Вы можете «исправить» это по телефону 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; 
    } 

} 
+1

Это решение действительно сработало. Но вы можете объяснить, почему? – Alexey

+0

См. Отредактированный ответ выше ... –

0
public void UpdateLabel(String newLabel) 
{ 
    lblCurProgress.Text = newLabel; 
    lblCurProgress.Refresh(); 
} 
+0

Я не думаю, что обновление необходимо, но давайте посмотрим. – jAC

+0

Я использую метод, достаточно близкий к описанному выше, в заставке для приложения с большой нагрузкой (около 13 секунд). Хорошо работает и дает пользователям обратную связь, поэтому они знают, что это что-то делает. – PLED

+0

Не обновляется. Как я уже указал, по какой-то причине он обновляется только после вызова MessageBox.Show() сразу после вызова. – Alexey

Смежные вопросы