Принудительное обновление GUI из потока пользовательского интерфейса



в WinForms, как заставить немедленное обновление пользовательского интерфейса из потока пользовательского интерфейса?



то, что я делаю примерно:



label.Text = "Please Wait..."
try
{
SomewhatLongRunningOperation();
}
catch(Exception e)
{
label.Text = "Error: " + e.Message;
return;
}
label.Text = "Success!";


текст метки не устанавливается в "Пожалуйста, подождите..."перед операцией.



Я решил это, используя другой поток для операции, но он становится волосатым, и я хотел бы упростить код.

771   10  

10 ответов:

сначала я задавался вопросом, почему OP еще не отметил один из ответов как ответ, но после того, как попробовал его сам и все еще не работал, я копнул немного глубже и обнаружил, что в этой проблеме гораздо больше, чем я сначала предполагал.

лучшее понимание можно получить, прочитав аналогичный вопрос:Почему не будет контролировать обновление / Обновление в середине процесса

наконец, для записи я смог обновить свой лейбл, выполнив следующее:

private void SetStatus(string status) 
{
    lblStatus.Text = status;
    lblStatus.Invalidate();
    lblStatus.Update();
    lblStatus.Refresh();
    Application.DoEvents();
}

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

вызов Application.DoEvents() после установки метки, но вы должны сделать всю работу в отдельном потоке вместо этого, так что пользователь может закрыть окно.

вызов label.Invalidate а то label.Update() - обычно обновление происходит только после выхода из текущей функции, но вызов Update заставляет его обновляться в этом конкретном месте в коде. От MSDN:

о признании незаконным способом определяет, что красится и перекрашивается. Метод Update определяет, когда происходит покраска или перекраска. Если вы используете методы Invalidate и Update вместе, а не вызываете Refresh, что будет перекрашено, зависит от того, какая перегрузка Признать недействительными использовании. Метод Update просто заставляет элемент управления быть окрашенным немедленно, но метод Invalidate управляет тем, что окрашивается при вызове метода Update.

Я только что наткнулся на ту же проблему и нашел интересную информацию, и я хотел положить свои два цента и добавить его сюда.

прежде всего, как уже упоминалось, длительные операции должны выполняться потоком, который может быть фоновым работником, явным потоком, потоком из пула потоков или (начиная с .Net 4.0) задачей: Stackoverflow 570537: update-label-while-processing-in-windows-forms, так что пользовательский интерфейс продолжает реагировать.

но для коротких задач нет реальной необходимости в резьбе, хотя это, конечно, не больно.

Я создал winform с одной кнопкой и одной меткой для анализа этой проблемы:

System::Void button1_Click(System::Object^  sender, System::EventArgs^  e)
{
  label1->Text = "Start 1";
  label1->Update();
  System::Threading::Thread::Sleep(5000); // do other work
}

мой анализ перешагнул через код (используя F10) и увидел, что произошло. А после прочтения этой статьи многопоточность в WinForms я нашел кое-что интересное. В статье говорится в нижней части первой страницы, что поток пользовательского интерфейса может не перерисовывайте пользовательский интерфейс до тех пор, пока выполняемая в данный момент функция не завершится, и окно будет помечено Windows как "не отвечает" вместо этого через некоторое время. Я также заметил, что на моем тестовом приложении сверху при переходе через него, но только в некоторых случаях.

(для следующего теста важно не иметь Visual Studio в полноэкранном режиме, вы должны быть в состоянии видеть ваше маленькое окно приложения в то же время рядом с ним, вы не должны переключаться между визуальными Студия окна для отладки и окно вашего приложения, чтобы увидеть, что происходит. Запустите приложение, установите точку останова на label1->Text ..., поместите окно приложения рядом с окном VS и наведите курсор мыши на окно VS.)

  1. когда я нажимаю один раз на VS после запуска приложения (чтобы поместить фокусы там и включить шаг) и шаг через него без перемещения мыши, новый текст устанавливается и метка обновляется в функции update (). это означает, пользовательский интерфейс перекрашен очевидно.

  2. когда я перешагиваю через первую строку, затем перемещаю мышь вокруг много и нажимаю где-то, затем шаг дальше, новый текст, вероятно, установлен, и функция update() вызывается, но пользовательский интерфейс не обновляется/перекрашивается, и старый текст остается там до тех пор, пока функция button1_click() не завершится. Вместо перекраски, окно помечается как "не реагирует"! Это также не помогает добавить this->Update(); для обновления всей формы.

  3. добавлять Application::DoEvents(); пользовательский интерфейс дает возможность обновить/перекрасить. В любом случае вы должны позаботиться о том, чтобы пользователь не мог нажимать кнопки или выполнять другие операции над пользовательским интерфейсом, которые не разрешены!! Таким образом: старайтесь избегать событий ()!, лучше использовать резьбу (которая, я думаю, довольно проста в .Net).
    Но (@Jagd, 2 апреля ' 10 в 19: 25) можно опустить .refresh() и .invalidate().

мои объяснения таковы следующее: Насколько мне известно, winform по-прежнему использует по WinAPI функция. Также статья MSDN о системе.Окна.Формы Контроля.Метод обновления относится к функции WINAPI WM_PAINT. Элемент MSDN статья о WM_PAINT в первом предложении указывается, что команда WM_PAINT отправляется системой только тогда, когда очередь сообщений пуста. Но поскольку очередь сообщений уже заполнена во 2-м случае, она не отправляется и, следовательно, метка и форма заявки не являются перекрашенный.

шутка> вывод: так что вы просто должны держать пользователя от использования мыши ; -) / шутка>

вы можете попробовать это

using System.Windows.Forms; // u need this to include.

MethodInvoker updateIt = delegate
                {
                    this.label1.Text = "Started...";
                };
this.label1.BeginInvoke(updateIt);

смотрите, если это работает.

после обновления пользовательского интерфейса, запустите задачу для выполнения с длительной операции:

label.Text = "Please Wait...";

Task<string> task = Task<string>.Factory.StartNew(() =>
{
    try
    {
        SomewhatLongRunningOperation();
        return "Success!";
    }
    catch (Exception e)
    {
        return "Error: " + e.Message;
    }
});
Task UITask = task.ContinueWith((ret) =>
{
    label.Text = ret.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());

это работает в .NET 3.5 и более поздних версиях.

очень заманчиво хотеть "исправить" это и заставить обновление пользовательского интерфейса, но лучшее исправление-сделать это в фоновом потоке, а не связывать поток пользовательского интерфейса, чтобы он все еще мог реагировать на события.

попробуйте вызвать label.Признать недействительными()

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate(VS.80).aspx

думаю, у меня есть ответ, дистиллированный из вышесказанного и немного экспериментов.

progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;

я попытался уменьшить значение и экран обновляется даже в режиме отладки, но это не будет работать для настройки progressBar.Value to progressBar.Maximum, потому что вы не можете установить значение индикатора выполнения выше максимального, поэтому я сначала установил progressBar.Value to progressBar.Maximum -1, то набор progressBar.Maxiumum равной progressBar.ValuЭл. Они говорят, что есть больше чем один способ убить кошку. Иногда я хотел бы Убить Билла Гейтса или кто он теперь :о).

С этим результатом мне даже не нужно было Invalidate(),Refresh(),Update(), или сделать что-нибудь с индикатором выполнения или его контейнером панели или родительской формой.

у меня была такая же проблема с имуществом Enabled и я обнаружил first chance exception поднял из-за этого не потокобезопасна. Я нашел решение на тему "Как обновить GUI из другого потока в C#?"здесь https://stackoverflow.com/a/661706/1529139 и это работает !

Comments

    Ничего не найдено.