Loupe

#Windows8.1 Récupérer une valeur depuis un SettingsFlyout avec un TaskCompletionSource

Lorsqu’on développe des applications Windows 8.1, il arrive souvent qu’on utilise le SettingsFlyout comme un formulaire, et dans ce cas, il est intéressant de récupérer directement l’objet qui en résulte. Cela n’est pas possible avec la méthode ShowIndependant, c’est là que le TaskCompletionSource intervient.

Créer le SettingsFlyout

Pour créer le SettingsFlyout, il suffit de… le créer :

image

 

On obtient la même structure qu’un UserControl standard, la même chose en code-behind.

public sealed partial class ContactFlyout : SettingsFlyout
{
    public ContactFlyout()
    {
        this.InitializeComponent();
    }
}

TaskCompletionSource

Pour pouvoir récupérer de façon asynchrone le résultat de notre ContactFlyout, on va utiliser un TaskCompletionSource. Les TaskCompletionSource vont permettre de créer une Task esclave que vous dirigez manuellement. Concrètement, tout en bénéficiant des avantages de l’asynchronisme (ne pas bloquer le thread courant durant l’exécution de l’opération, etc…), il est possible d’indiquer soi-même lorsque la Task est finie, annulée ou est en état d’erreur.

Pour l’utiliser, on instancie un TaskCompletionSource qu’on garde en propriété dans notre code-behind du SettingsFlyout pour pouvoir le manipuler plus tard, en lui spécifiant son type de retour. Dans cet exemple je ne récupère qu’un string, mais vous pouvez bien entendu manipuler des classes créées par vos soins.

public sealed partial class ContactFlyout : SettingsFlyout
{
    private TaskCompletionSource<string> _tcs;

    public ContactFlyout()
    {
        this.InitializeComponent();
    }

    public async Task<string> ShowAsync()
    {
        _tcs = new TaskCompletionSource<string>();
        this.ShowIndependent();
        return await _tcs.Task;
    }
}

Ce qui va se passer, c’est que le ContactFlyout va apparaître avec le ShowIndependant, et que le return attend que la Task soit signalé, c’est à dire que celle-ci passe en RanToCompletion, Faulted ou Canceled, qui sont des valeurs de l’énumération TaskStatus.

Ensuite, il est possible d’appeler les méthodes suivantes pour mettre la Task dans l’état associé, le lien entre les états et les méthodes étant assez explicite.

 

image

 

Les méthodes SetResult, SetException et SetCanceled doivent être appelées une seule et unique fois, au second appel, une InvalidOperationException est retournée. Tandis que les méthodes équivalentes avec le Try quant à elle renverront False. Ceci est du au fait que la Task du TaskCompletionSource est déjà dans un état, et qu’on essaye de la mettre de nouveau dans cet état.

Ensuite, il ne reste plus qu’à placer notre SetResult sur un click de bouton, un SetCanceled dans un autre pour permettre à l’utilisateur d’annuler, et bien entendu de gérer de la même manière le bouton back du ContactFlyout.

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    _tcs.SetResult(ContactName);
}

private void Cancel_OnClick(object sender, RoutedEventArgs e)
{
    _tcs.SetCanceled();
}

private void ContactFlyout_OnBackClick(object sender, BackClickEventArgs e)
{
    _tcs.SetCanceled();
}

 

Comment on l’utilise, notre ContactFlyout?

Une fois que toute la logique du ContactFlyout a été mise en place, nous pouvons l’utiliser.

public async Task AskContactTask()
{
    string contact = null;
    try
    {
        var flyout = new ContactFlyout();
        contact = await flyout.ShowAsync();
    }
    catch (TaskCanceledException)
    {
        // Il ne se passe rien, l'utilisateur a annulé son opération
    }

    var dialog = new MessageDialog(contact ?? "L'utilisateur n'a rien choisi !");
    await dialog.ShowAsync();
}

C’est plutôt simple, on créé notre string puis on instancie notre ContactFlyout. Une fois instancié, on peut utiliser notre méthode ShowAsync fraichement créée et on l’encapsule dans un try/catch dans lequel on va catcher l’exception TaskCanceledException, ce qui correspond aux annulations de l’utilisateur. Libre à vous de catcher les autres exceptions qui pourraient arriver en fonction de votre logique métier. Dans notre cas, on pourrait imaginer catcher les InvalidOperationException pour plus de sécurité.

Il ne reste plus qu’à utiliser votre variable récupérée et voilà ! Récemment, j’ai du développer plusieurs SettingsFlyout du genre qui servait de formulaire, et le comportement final de ceux-ci intégrés à l’application est plutôt satisfaisant. C’est pourquoi je vous conseille vivement d’utiliser ce contrôle, mais faites tout de même attention à ne pas en abuser !

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus