Wymuś aktualizację GUI z wątku UI

W WinForms, jak wymusić natychmiastową aktualizację interfejsu użytkownika z wątku interfejsu?

To co robię to mniej więcej:

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

Tekst etykiety nie jest ustawiony na " proszę czekać..."przed operacją.

Rozwiązałem to za pomocą innego wątku do operacji, ale robi się Owłosione i chciałbym uprościć kod.

Author: Bobby, 2009-09-01

10 answers

Na początku zastanawiałam się, dlaczego OP nie zaznaczyła jeszcze jednej odpowiedzi jako odpowiedzi, ale po wypróbowaniu jej sam i nadal nie działa, pogrzebałam trochę głębiej i odkryłam, że jest o wiele więcej w tej kwestii, niż najpierw przypuszczałam.

Lepsze zrozumienie można uzyskać, czytając z podobnego pytania: Dlaczego nie kontroluje aktualizacji / odświeżania w połowie procesu

Na koniec, dla przypomnienia, udało mi się zaktualizować moją wytwórnię, wykonując "po": {]}
private void SetStatus(string status) 
{
    lblStatus.Text = status;
    lblStatus.Invalidate();
    lblStatus.Update();
    lblStatus.Refresh();
    Application.DoEvents();
}
Choć z tego, co rozumiem, jest to dalekie od eleganckiego i poprawnego podejścia do tego. Jest to hack, który może lub nie może działać w zależności od tego, jak zajęty jest wątek.
 90
Author: Jagd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:44

Wywołaj Application.DoEvents() po ustawieniu etykiety, ale całą pracę powinieneś wykonać w osobnym wątku, aby użytkownik mógł zamknąć okno.

 12
Author: Scoregraphic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-01 07:07:29

WywoĹ 'anie label.Invalidate a nastÄ ™ pnie label.Update() - Zwykle aktualizacja dzieje siÄ ™ dopiero po zakoĹ" czeniu aktualnej funkcji, ale wywoĹ ' anie Update wymusza jej aktualizację w tym konkretnym miejscu w kodzie. From MSDN :

Metoda Invalidate reguluje to, co zostanie pomalowane lub przemalowane. Metoda aktualizacji reguluje czas malowania lub przemalowania. Jeśli używasz metod Invalidate i Update razem, zamiast wywoływać Refresh, to co zostanie przemalowane zależy od tego, jakiego przeciążenia używasz Invalidate. Na Metoda Update wymusza natychmiastowe malowanie kontrolki, ale metoda Invalidate reguluje to, co jest malowane po wywołaniu metody Update.

 12
Author: Dror Helper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-01 07:07:42

Właśnie natknąłem się na ten sam problem i znalazłem kilka ciekawych informacji i chciałem umieścić moje dwa grosze i dodać je tutaj.

Po pierwsze, jak inni już wspominali, długotrwałe operacje powinny być wykonywane przez wątek, który może być robotnikiem w tle, jawnym wątkiem, wątkiem z threadpool lub (od. Net 4.0) zadaniem: Stackoverflow 570537: update-label-while-processing-in-windows-forms , aby interfejs użytkownika był responsywny.

Ale dla krótkie zadania nie ma potrzeby gwintowania, chociaż oczywiście nie boli.

Stworzyłem winform z jednym przyciskiem i jedną etykietą, aby przeanalizować ten problem:

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

Moją analizą było przekroczenie kodu (używając F10) i zobaczenie, co się stało. Po przeczytaniu tego artykułu wielowątkowość w WinForms znalazłem coś ciekawego. Artykuł mówi na dole pierwszej strony, że wątek UI nie może przemalować interfejsu użytkownika do aktualnie wykonanego funkcja kończy się i okno jest oznaczane przez Windows jako "nie odpowiada" zamiast po chwili. Zauważyłem to również na mojej aplikacji testowej z góry, przechodząc przez nią, ale tylko w niektórych przypadkach.

(dla poniższego testu ważne jest, aby nie mieć Visual Studio ustawiony na Pełny ekran, musisz być w stanie zobaczyć swoje małe okno aplikacji w tym samym czasie obok niego, nie musisz przełączać się między oknem Visual Studio do debugowania i okna aplikacji do zobaczymy, co się stanie. Uruchom aplikację, ustaw punkt przerwania na label1->Text ..., Umieść okno aplikacji obok okna VS i umieść kursor myszy nad oknem VS.)

  1. Kiedy klikam raz na VS po uruchomieniu aplikacji (aby umieścić tam ogniska i włączyć stepping) i przejść przez to bez poruszania myszką, nowy tekst jest ustawiony, a etykieta jest aktualizowana w funkcji update (). oznacza to, że interfejs użytkownika jest oczywiście przemalowany.

  2. Kiedy przekroczę pierwszy linia, następnie przesuń kursor myszy i kliknij gdzieś, a następnie krok dalej, nowy tekst jest prawdopodobnie ustawiony i funkcja update() jest wywoływana, ale interfejs nie jest aktualizowany/przemalowywany i stary tekst pozostaje tam, dopóki funkcja button1_click () nie zakończy się. Zamiast malowania okno jest oznaczone jako "nie reaguje"! Nie pomaga również dodanie this->Update(); do aktualizacji całego formularza.

  3. Dodanie Application::DoEvents(); daje możliwość aktualizacji/przemalowania interfejsu użytkownika. W każdym razie musisz zadbać o to, aby użytkownik nie może naciskać przycisków ani wykonywać innych operacji na interfejsie użytkownika, które są niedozwolone!! Dlatego: staraj się unikać DoEvents()!Nie jest to jednak żaden problem, ponieważ nie jest to możliwe.]} Ale (@Jagd, Apr 2 '10 at 19: 25) można pominąć .refresh() i .invalidate().

Moje wyjaśnienia są następujące: AFAIK winform nadal używa funkcji WINAPI. Również artykuł MSDN o systemie.Okna.Kontrola Form.Metoda Update odnosi się do funkcji WinAPI WM_PAINT. Artykuł MSDN o WM_PAINT stwierdza w pierwszym zdaniu, że polecenie WM_PAINT jest wysyłane przez system tylko wtedy, gdy kolejka komunikatów jest pusta. Ale ponieważ kolejka komunikatów jest już wypełniona w drugim przypadku, nie jest wysyłana, a zatem etykieta i formularz wniosku nie są przemalowywane.

Joke> wniosek: więc po prostu trzeba trzymać użytkownika z dala od korzystania z myszy ; -) /joke>

 4
Author: Tobias Knauss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:27

Możesz spróbować tego

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

MethodInvoker updateIt = delegate
                {
                    this.label1.Text = "Started...";
                };
this.label1.BeginInvoke(updateIt);
Zobacz, czy działa.
 3
Author: Rick2047,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-04 07:03:35

Po zaktualizowaniu interfejsu użytkownika Uruchom zadanie do wykonania z długotrwałą operacją:

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());
To działa w. NET 3.5 i nowszych wersjach.
 2
Author: pixelgrease,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-16 06:32:39

To bardzo kuszące, aby chcieć to "naprawić" i wymusić aktualizację interfejsu, ale najlepszą poprawką jest to, aby zrobić to w wątku tła i nie związać wątku interfejsu, aby nadal mógł reagować na zdarzenia.

 1
Author: Mike Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-01 07:08:18
 1
Author: Chetan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-01 07:09:22

Myślę, że mam odpowiedź, z powyższego i trochę eksperymentów.

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

Próbowałem zmniejszyć wartość i Ekran został zaktualizowany nawet w trybie debugowania, ale to nie zadziała przy ustawieniu progressBar.Value na progressBar.Maximum, ponieważ nie można ustawić wartości paska postępu powyżej maksimum, więc najpierw ustawiłem progressBar.Value na progressBar.Maximum -1, a następnie ustawić progressBar.Maxiumum na równe progressBar.Value. mówią, że istnieje więcej niż jeden sposób na zabicie kota. Czasami chciałbym zabić Billa Gatesa albo kogokolwiek innego. : o).

Z tego wyniku, nawet nie wydaje mi się, aby trzeba Invalidate(), Refresh(), Update(), lub zrobić cokolwiek z paskiem postępu, jego kontenerem panelu lub formularzem nadrzędnym.

 1
Author: Graham Bennett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-04 16:04:36

Miałem ten sam problem z właściwością Enabled i odkryłem first chance exception podniesiony, ponieważ jest nie bezpieczny dla wątków . Znalazłem rozwiązanie " jak zaktualizować GUI z innego wątku w C#?"tutaj https://stackoverflow.com/a/661706/1529139 i to działa !

 0
Author: 56ka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:06