Niedawno zacząłem programować w WPF i napotkałem następujący problem. Nie rozumiem, jak używać tej Dispatcher.Invoke()
metody. Mam doświadczenie w tworzeniu wątków i stworzyłem kilka prostych programów Windows Forms, w których właśnie użyłem
Control.CheckForIllegalCrossThreadCalls = false;
Tak, wiem, że to dość kiepskie, ale były to proste aplikacje monitorujące.
Faktem jest, że teraz tworzę aplikację WPF, która pobiera dane w tle, zaczynam nowy wątek, aby wykonać wywołanie w celu pobrania danych (z serwera internetowego), teraz chcę wyświetlić je w moim formularzu WPF. Rzecz w tym, że nie mogę ustawić żadnej kontroli z tego wątku. Ani etykiety, ani nic. Jak można to rozwiązać?
Komentarze do odpowiedzi:
@Jalfp:
Więc używam tej metody Dispatchera w „nowym bieżniku”, kiedy otrzymuję dane? A może powinienem zmusić pracownika działającego w tle do pobrania danych, umieszczenia ich w polu i rozpoczęcia nowego wątku, który czeka, aż to pole zostanie wypełnione, i zadzwonić do dyspozytora, aby pokazał pobrane dane w kontrolkach?
źródło
Odpowiedzi:
Pierwszą rzeczą jest zrozumienie, że Dispatcher nie jest przeznaczony do wykonywania długich operacji blokowania (takich jak pobieranie danych z serwera internetowego ...). Możesz użyć Dispatchera, gdy chcesz uruchomić operację, która zostanie wykonana w wątku interfejsu użytkownika (na przykład zaktualizowanie wartości paska postępu).
Możesz pobrać dane w tle procesu roboczego i użyć metody ReportProgress do propagowania zmian w wątku interfejsu użytkownika.
Jeśli naprawdę potrzebujesz bezpośrednio skorzystać z Dispatchera, jest to całkiem proste:
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => this.progressBar.Value = 50));
źródło
new Action(...)
.BeginInvoke
a zamiast tego pojawia się błąd kompilatora CS1660.japf odpowiedział poprawnie. Na wszelki wypadek, jeśli patrzysz na akcje wieloliniowe, możesz napisać jak poniżej.
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => { this.progressBar.Value = 50; }));
Informacje dla innych użytkowników, którzy chcą wiedzieć o wydajności:
Jeśli kod MUSI zostać napisany w celu uzyskania wysokiej wydajności, możesz najpierw sprawdzić, czy wywołanie jest wymagane, używając flagi CheckAccess.
if(Application.Current.Dispatcher.CheckAccess()) { this.progressBar.Value = 50; } else { Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => { this.progressBar.Value = 50; })); }
Zwróć uwagę, że metoda CheckAccess () jest ukryta w programie Visual Studio 2015, więc po prostu napisz ją, nie oczekując, że inteligencja ją pokaże. Zauważ, że CheckAccess ma narzut na wydajność (narzut w ciągu kilku nanosekund). Lepiej jest tylko wtedy, gdy chcesz zaoszczędzić tę mikrosekundę wymaganą do wykonania „wywołania” za wszelką cenę. Ponadto zawsze istnieje opcja utworzenia dwóch metod (włączonych z invoke i innych bez), gdy wywołanie metody jest pewne, czy jest w wątku interfejsu użytkownika, czy nie. To bardzo rzadki przypadek, kiedy powinieneś spojrzeć na ten aspekt dyspozytora.
źródło
Kiedy wykonywany jest wątek i chcesz wykonać główny wątek interfejsu użytkownika, który jest blokowany przez bieżący wątek, użyj poniższego:
aktualny wątek:
Dispatcher.CurrentDispatcher.Invoke(MethodName, new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Główny wątek interfejsu użytkownika:
Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, new Action(() => MethodName(parameter)));
źródło
Powyższa odpowiedź @japf działa dobrze, aw moim przypadku chciałem zmienić kursor myszy z obracającego się koła z powrotem na normalną strzałkę, gdy przeglądarka CEF zakończy ładowanie strony. W przypadku, gdy może to komuś pomóc, oto kod:
private void Browser_LoadingStateChanged(object sender, CefSharp.LoadingStateChangedEventArgs e) { if (!e.IsLoading) { // set the cursor back to arrow Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => Mouse.OverrideCursor = Cursors.Arrow)); } }
źródło