Może Application.DoEvents()
być używany w C #?
Czy ta funkcja pozwala GUI dogonić resztę aplikacji, podobnie jak VB6 DoEvents
?
Może Application.DoEvents()
być używany w C #?
Czy ta funkcja pozwala GUI dogonić resztę aplikacji, podobnie jak VB6 DoEvents
?
Odpowiedzi:
Hmya, trwała mistyka DoEvent (). Nastąpił przeciwko temu olbrzymi luz, ale nikt tak naprawdę nie wyjaśnia, dlaczego jest „zły”. Ten sam rodzaj mądrości co „nie mutuj struktury”. Eee, dlaczego środowisko uruchomieniowe i język obsługują mutowanie struktury, jeśli jest tak źle? Z tego samego powodu: strzelasz sobie w stopę, jeśli nie zrobisz tego dobrze. Z łatwością. A robienie tego poprawnie wymaga dokładnej znajomości tego , co robi, co w przypadku DoEvent () zdecydowanie nie jest łatwe do zrozumienia.
Od samego początku: prawie każdy program Windows Forms faktycznie zawiera wywołanie DoEvents (). Jest sprytnie zamaskowany, ale pod inną nazwą: ShowDialog (). Jest to DoEvents (), który umożliwia modalne okno dialogowe, bez zawieszania reszty okien w aplikacji.
Większość programistów chce używać DoEvents, aby zatrzymać zamrażanie interfejsu użytkownika podczas pisania własnej pętli modalnej. Z pewnością to robi; wysyła wiadomości Windows i odbiera wszystkie żądania malowania. Problem polega jednak na tym, że nie jest selektywny. Nie tylko wysyła wiadomości o farbie, ale także dostarcza wszystko inne.
I jest zestaw powiadomień, które powodują problemy. Pochodzą z około 3 stóp przed monitorem. Użytkownik może na przykład zamknąć okno główne, gdy uruchomiona jest pętla wywołująca DoEvents (). Działa, interfejs użytkownika zniknął. Ale twój kod się nie zatrzymał, nadal wykonuje pętlę. To źle. Bardzo bardzo źle.
Jest więcej: użytkownik może kliknąć ten sam element menu lub przycisk, który powoduje uruchomienie tej samej pętli. Teraz masz dwie zagnieżdżone pętle wykonujące DoEvents (), poprzednia pętla została zawieszona, a nowa pętla zaczyna się od zera. To może zadziałać, ale chłopcze szanse są niewielkie. Zwłaszcza, gdy kończy się zagnieżdżona pętla i wznawia się zawieszoną, próbując zakończyć zadanie, które zostało już ukończone. Jeśli to nie bombarduje wyjątkiem, to na pewno dane zostaną zaszyfrowane do piekła.
Powrót do ShowDialog (). Wykonuje DoEvents (), ale pamiętaj, że robi coś innego. To wyłącza wszystkie okna w aplikacji , innych niż okna. Teraz, gdy problem 3 stóp został rozwiązany, użytkownik nie może nic zrobić, aby popsuć logikę. Rozwiązano zarówno tryby awarii zamykania okna, jak i ponownego uruchamiania zadania. Innymi słowy, użytkownik nie może ustawić kodu uruchamiającego program w innej kolejności. Będzie działał przewidywalnie, tak jak podczas testowania kodu. To sprawia, że dialogi są wyjątkowo denerwujące; kto nie nienawidzi aktywnego okna dialogowego i niemożności kopiowania i wklejania czegoś z innego okna? Ale to jest cena.
Właśnie to wymaga bezpiecznego korzystania z DoEvent w twoim kodzie. Ustawienie właściwości Enabled wszystkich formularzy na false to szybki i skuteczny sposób na uniknięcie problemów. Oczywiście żaden programista nigdy nie lubi tego robić. I nie ma. Dlatego nie powinieneś używać DoEvents (). Powinieneś używać wątków. Mimo że dają ci pełny arsenał sposobów strzelania stopą w kolorowe i nieprzeniknione sposoby. Ale z tą zaletą, że strzelasz tylko własną stopą; nie pozwala (zwykle) jej strzelać.
Kolejne wersje C # i VB.NET zapewnią inną broń dzięki nowym słowom kluczowym wyczekiwania i asynchronizacji. Zainspirowany w części kłopotami spowodowanymi przez DoEvent i wątki, ale w dużej mierze przez interfejs API WinRT, który wymaga aktualizacji interfejsu użytkownika podczas operacji asynchronicznej. Jak czytanie z pliku.
BackgroundWorker
komponent zarządza wątkami za Ciebie, unikając większości kolorowych efektów strzelania do stóp i nie wymaga najnowocześniejszych wersji językowych C #.
Może być, ale to hack.
Zobacz Czy DoEvents Evil? .
Bezpośrednio ze strony MSDN , do której odwoływało się urządzenie:
Wywołanie tej metody powoduje zawieszenie bieżącego wątku podczas przetwarzania wszystkich komunikatów w oknie oczekiwania. Jeśli komunikat powoduje wyzwolenie zdarzenia, mogą zostać wykonane inne obszary kodu aplikacji. Może to spowodować, że aplikacja będzie wykazywać nieoczekiwane zachowania, które są trudne do debugowania. Jeśli wykonujesz operacje lub obliczenia, które zajmują dużo czasu, często lepiej jest wykonać te operacje w nowym wątku. Aby uzyskać więcej informacji na temat programowania asynchronicznego, zobacz Omówienie programowania asynchronicznego.
Microsoft ostrzega więc przed jego użyciem.
Ponadto uważam, że jest to hack, ponieważ jego zachowanie jest nieprzewidywalne i podatne na skutki uboczne (wynika to z doświadczenia w próbie użycia DoEvent zamiast rozwijania nowego wątku lub korzystania z pracownika w tle).
Nie ma tutaj machismo - gdyby działało jako solidne rozwiązanie, to bym to zrobił. Jednak próba użycia DoEvents w .NET spowodowała tylko ból.
BackgroundWorker
pomógł uprościć „prawidłowy” sposób.
Tak, istnieje statyczna metoda DoEvents w klasie Application w przestrzeni nazw System.Windows.Forms. System.Windows.Forms.Application.DoEvents () można wykorzystać do przetwarzania komunikatów oczekujących w kolejce w wątku interfejsu użytkownika podczas wykonywania długotrwałego zadania w wątku interfejsu użytkownika. Ma to tę zaletę, że interfejs użytkownika wydaje się bardziej responsywny i nie „blokowany” podczas wykonywania długiego zadania. Jednak prawie zawsze NIE jest to najlepszy sposób na robienie rzeczy. Według firmy Microsoft wywołującej DoEvents „... powoduje zawieszenie bieżącego wątku podczas przetwarzania wszystkich komunikatów w oknie oczekiwania”. Wyzwolenie zdarzenia może spowodować nieoczekiwane i sporadyczne błędy, które są trudne do wyśledzenia. Jeśli masz rozległe zadanie, zdecydowanie lepiej jest to zrobić w osobnym wątku. Uruchamianie długich zadań w osobnym wątku pozwala na ich przetwarzanie bez zakłócania płynnego działania interfejsu użytkownika. Popatrztutaj po więcej szczegółów.
Oto przykład korzystania z DoEvent; zwróć uwagę, że firma Microsoft ostrzega również przed korzystaniem z niej.
Z mojego doświadczenia radziłbym wielką ostrożność przy korzystaniu z DoEvent w .NET. Doświadczyłem bardzo dziwnych wyników podczas używania DoEvent w TabControl zawierającym DataGridViews. Z drugiej strony, jeśli masz do czynienia tylko z małą postacią z paskiem postępu, może być w porządku.
Najważniejsze jest to: jeśli zamierzasz używać DoEvents, musisz dokładnie go przetestować przed wdrożeniem aplikacji.
Tak.
Jeśli jednak musisz użyć Application.DoEvents
, jest to głównie oznaka złego projektu aplikacji. Może zamiast tego chciałbyś popracować w osobnym wątku?
Widziałem powyższy komentarz Jheriko i początkowo zgodziłem się, że nie mogę znaleźć sposobu na uniknięcie korzystania z DoEvent, jeśli w końcu zakręcisz główny wątek interfejsu użytkownika, czekając na długo działający asynchroniczny fragment kodu w innym wątku. Ale z odpowiedzi Matthiasa proste odświeżenie małego panelu w moim interfejsie użytkownika może zastąpić DoEvent (i uniknąć nieprzyjemnego efektu ubocznego).
Więcej szczegółów na temat mojej sprawy ...
Robiłem następujące (zgodnie z sugestią tutaj ), aby upewnić się, że ekran powitalny typu paska postępu ( Jak wyświetlić nakładkę „ładującą” ... ) został zaktualizowany podczas długotrwałego polecenia SQL:
IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted) //UI thread needs to Wait for Async SQL command to return
{
System.Threading.Thread.Sleep(10);
Application.DoEvents(); //to make the UI responsive
}
Złe: dla mnie wywołanie DoEvent oznaczało, że kliknięcia myszy czasami strzelały do formularzy za moim ekranem powitalnym, nawet jeśli zrobiłem to TopMost.
Dobrą / odpowiedź: Zamień linię DoEvents z prostym Refresh wywołania małego panelu w centrum mojego ekranu powitalnego, FormSplash.Panel1.Refresh()
. Interfejs użytkownika ładnie się aktualizuje, a dziwność DoEvent, o której inni ostrzegali, zniknęła.
Widziałem wiele aplikacji komercyjnych, wykorzystujących „DoEvents-Hack”. Zwłaszcza gdy renderowanie wchodzi w grę, często widzę to:
while(running)
{
Render();
Application.DoEvents();
}
Wszyscy wiedzą o złu tej metody. Jednak używają hacka, ponieważ nie znają żadnego innego rozwiązania. Oto niektóre podejścia zaczerpnięte z postu na blogu autorstwa Toma Millera :
- Ustaw formularz tak, aby wszystkie rysunki występowały w WmPaint i wykonaj tam rendering. Przed końcem metody OnPaint upewnij się, że wykonujesz this.Invalidate (); Spowoduje to natychmiastowe uruchomienie metody OnPaint.
- P / Invoke do Win32 API i wywołaj PeekMessage / TranslateMessage / DispatchMessage. (Doevents faktycznie robi coś podobnego, ale możesz to zrobić bez dodatkowych przydziałów).
- Napisz własną klasę formularzy, która jest małym opakowaniem wokół CreateWindowEx, i daj sobie pełną kontrolę nad pętlą komunikatów. - Zdecyduj, że metoda DoEvents działa dobrze i trzymaj się jej.
Sprawdź dokumentację MSDN dla tej Application.DoEvents
metody.
DoEvents pozwala użytkownikowi klikać lub wpisywać i wyzwalać inne zdarzenia, a wątki w tle są lepszym podejściem.
Nadal jednak zdarzają się przypadki, w których mogą wystąpić problemy wymagające opróżnienia komunikatów o zdarzeniach. Wystąpił problem polegający na tym, że formant RichTextBox ignorował metodę ScrollToCaret (), gdy formant miał w kolejce komunikaty do przetworzenia.
Poniższy kod blokuje wszystkie dane wejściowe użytkownika podczas wykonywania DoEvents:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Integrative.Desktop.Common
{
static class NativeMethods
{
#region Block input
[DllImport("user32.dll", EntryPoint = "BlockInput")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
public static void HoldUser()
{
BlockInput(true);
}
public static void ReleaseUser()
{
BlockInput(false);
}
public static void DoEventsBlockingInput()
{
HoldUser();
Application.DoEvents();
ReleaseUser();
}
#endregion
}
}
Application.DoEvents może powodować problemy, jeśli w kolejce komunikatów zostanie umieszczone coś innego niż przetwarzanie grafiki.
Może to być przydatne do aktualizowania pasków postępu i powiadamiania użytkownika o postępach w budowaniu i ładowaniu MainForm, jeśli zajmie to trochę czasu.
W mojej niedawno utworzonej aplikacji użyłem DoEvents do aktualizacji niektórych etykiet na ekranie ładowania za każdym razem, gdy blok kodu jest wykonywany w konstruktorze mojego MainForm. Wątek interfejsu użytkownika był w tym przypadku zajęty wysyłaniem wiadomości e-mail na serwerze SMTP, który nie obsługiwał wywołań SendAsync (). Prawdopodobnie mógłbym utworzyć inny wątek za pomocą metod Begin () i End () i wywołać Send () z nich, ale ta metoda jest podatna na błędy i wolałbym, aby główna forma mojej aplikacji nie zgłaszała wyjątków podczas budowy.
DoEvents
jest częścią Windows Forms, a nie językiem C #. Jako taki może być używany z dowolnego języka .NET. Nie należy go jednak używać z żadnego języka .NET.