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.

image

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 Uśmiech

public MainWindow()
{
    InitializeComponent();
    btnTest.WhenClicked().ContinueWith(
    k => { MessageBox.Show("Po klinieciu"); });
    StopAfterButton();
}

Oto rezultaty:

Async Button click

Mniej więcej to się dzieje w metodzie „StopAfterButton()”.

Await wyjaśnienie

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)