Awaiting XNauka „async i await” się nie kończy. Co można jeszcze zrobić przy pomocy tych słów kluczowych. W WPF w prosty sposób możemy zwolnić wątek UI. W WCF możemy łatwo przejąć kontrolę nad wiadomościami zwrotnymi usługi. To jednak nie koniec.
Dobrym źródłem wiedzy na temat async i await jest blog Luciana Wischik.
Jest on obecnie kuratorem języka Visual Basic. W sumie jego wpisy i prezentacje są bardzo użyteczne. Istnieje jednak jeden problem przykłady są w języku Visual Basic.
(Biorąc pod uwagę ,że jest on kuratorem tego języka nie powinno to nikogo dziwić )
Co trochę boli, ponieważ Lucian napisał kod, który serializuję Taski i w teorii je hibernuje w VB ,ale nie w C#. Przetłumaczenie kod z VB na C# nie zawsze jest takie łatwe.
Tak czy siak, jego blog posiada cykl „How to await »x«”. Czyli co można zrobić przy użyciu metod rozszerzeniowych, task-ów i słów kluczowych „async i await”.
Przykładowo możemy uczynić przycisk awaitable.
Co to znaczy?
Oznacza to ,że możemy wykonać i zatrzymać jakieś zadanie, dopóki dany przycisk nie zostanie kliknięty.
Przykładowo możemy wyświetlić jakiś komunikat i go zmienić, dopiero gdy przycisk zostanie kliknięty. Możemy także użyć metody „ContinuedWith<T>”, czyli wykonać jakąś metodę, gdy zdarzenia kliknięcia się skończą.
Cała magia zaczyna się od tej metody rozszerzeniowej. Przetłumaczyłem ją z VB na C#.
public static class StaticExtensionAwaitClick
{
public static Task WhenClicked(this Button btn)
{
var tcs = new TaskCompletionSource<object>();
RoutedEventHandler lambda = null;
lambda = (s, e) =>
{
btn.Click -= lambda;
tcs.SetResult(null);
};
btn.Click += lambda;
return tcs.Task;
}
}
Ten kod podpina się pod zdarzenie kliknięcia i zwróci „Task-a”. Gdy wszystkie inne zdarzenia zapisane pod kliknięciem się skończą zwrócony zadanie „Task” zostanie zakończone.
Tak jak pisałem wcześniej możemy przykładowo użyć teraz metody „ContinueWith<T>”. Chociaż nie będę ukrywał nie bardzo ma to sens.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
btnTest.WhenClicked().ContinueWith(
k => { MessageBox.Show("Po klinieciu"); });
}
private void btnTest_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("W trakcie kliknięcia");
}
}
A o to przykład z komunikatem, który zostanie zmieniony dopiero po kliknięciu. Metoda ta będzie czekać na wykonanie zadania „WhenClicked()”.
public async Task StopAfterButton()
{
tb_wait.Text = "Nacisnij przycisk aby przejść dalej ";
await btnTest.WhenClicked();
tb_wait.Text = "Dzięki";
}
Metoda „StopAfterButton()” jest uruchamiana wraz z aplikacją. Swoją drogą ta metoda powinna nazywać StopBeforeButton
public MainWindow()
{
InitializeComponent();
btnTest.WhenClicked().ContinueWith(
k => { MessageBox.Show("Po klinieciu"); });
StopAfterButton();
}
Oto rezultaty:
Mniej więcej to się dzieje w metodzie „StopAfterButton()”.
Dla zabawy przerobiłem trochę metodę rozszerzeniową tak, aby stworzyć jednorazową akcję dla kliknięcia bez podpinania się do zdarzenia bezpośrednio.
public static Task WhenClicked(this Button btn,Action action)
{
var tcs = new TaskCompletionSource<object>();
RoutedEventHandler lambda = null;
lambda = (s, e) =>
{
btn.Click -= lambda;
action.Invoke();
tcs.SetResult(null);
};
btn.Click += lambda;
return tcs.Task;
}
btnNoClickEvent.WhenClicked(
() =>
MessageBox.Show("Działa tylko raz"));
Ten kod jest jednak bezużyteczny, ponieważ to samo można osiągnąć przy użyciu metody „ContinuedWith<T>” . Czyli metoda „ContinuedWith<T>” ma sens, gdybyśmy chcieli wykonać jakąś akcję dla przycisku tylko raz dla każdego przypisania. Przy dziwnych działaniach asynchronicznych może i miałoby to zastosowanie.
Ma to jakieś jeszcze inne zastosowania?
Nie bardzo. Jest to odrobinę ciekawe, gdyż ten sam mechanizm można stworzyć dla każdego zdarzenia, jakie istnieje w frameworku .NET.
Nie zmienia to jednak faktu ,że takie działanie ma więcej sensu, gdy chcemy przykładowo z-awaitować animację.
Edit z 2022:
Kod jest na GitHubie : PanNiebieski/WPFAwaitableClick (github.com)