SettingsFlyout awaitable

Hi;

Today just a quick tip to create a SettingsFlyout awaitable

SettingsFlyout is a new feature in Windows 8.1. To use it, just add a new item in your project, with the Setting Flyout Template:

image

For this sample, i create a simple setting flyout item called SettingFlyoutItem :)

You can launch the flyout with some methods (Show or ShowIndependant)  from the SettingsFlyout object, like this :

 SettingsFlyoutItem sf = new SettingsFlyoutItem();
sf.Width = 500;
sf.ShowIndependent();

Here is a screenshot of my SettingFlyoutItem where I can edit my customer service ticket :)

image

To close the flyout, just call the Hide() method, like this :

 private void OkButton_Click(object sender, RoutedEventArgs e)
{
    this.Hide();
}
 

This method is called from the SettingFlyoutItem class you have created before.

But you are not notified by the SettingsFlyout when the hide method is called, in your view model.

Here is the problem from my View Model (in an EditCommand) :

 if (editCommand != null) return editCommand;

editCommand = new RelayCommand(() =>
{
    SettingsFlyoutItem sf = new SettingsFlyoutItem();
    sf.Width = 500;
    sf.ShowIndependent();

    // I Want to be notified when the Flyout is closed to make a refresh

});

We can use a Messenger class (already part of the MVVM light for example) to resolve this problem.

We can rely on events too, why not…

But we can do much better, using the async / await pattern:

The idea is to create a new method, ShowIndependantAsync() and make it awaitable. Thanks to TaskCompletion<T> , we can rely the response of the task to an other method. In this case, the Ok Button Eventhandler, where we call the Hide() method.

Update : Thanks to Benjamin Roux who point me the fact that the SettingFlyout object is a Popup, and you can clic outside the setting flyout to close it. So you need to intercept the Closed event from the Popup parent object.

Here is the complete code of my SettingFlyoutItem class :

 public sealed partial class SettingsFlyoutItem : SettingsFlyout
{
    Popup parent;

    public SettingsFlyoutItem()
    {
        this.InitializeComponent();
        this.BackClick += SettingsFlyoutItem_BackClick;
        this.Loaded += SettingsFlyoutItem_Loaded;
    }

    void SettingsFlyoutItem_Loaded(object sender, RoutedEventArgs e)
    {
        this.parent = Parent as Popup;

        if (this.parent != null)
            this.parent.Closed += parent_Closed;

    }

    void parent_Closed(object sender, object e)
    {
        if (this.tcs.Task.Status != TaskStatus.RanToCompletion)
           this.tcs.SetResult(false);
    }

    private TaskCompletionSource<Boolean> tcs;

    public async Task<Boolean> ShowIndependentAsync()
    {
        this.ShowIndependent();

        this.tcs = new TaskCompletionSource<Boolean>();
            
        //Await the completion from any event handler
        await tcs.Task;
        this.Hide();        return tcs.Task.Result;
    }

        
    void SettingsFlyoutItem_BackClick(object sender, BackClickEventArgs e)
    {
        this.tcs.SetResult(false);
    }
    private void OkButton_Click(object sender, RoutedEventArgs e)
    {
        this.tcs.SetResult(true);
    }
    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        this.tcs.SetResult(false);
    }

}

Then, use it is pretty straightforward :

 public RelayCommand EditCommand
{
    get
    {
        if (editCommand != null) return editCommand;

        editCommand = new RelayCommand(async () =>
        {
            SettingsFlyoutItem sf = new SettingsFlyoutItem();
            sf.Width = 500;
            var isOk = await sf.ShowIndependentAsync();

            // Since the flyout is closed, i can continue
            if (isOk)
                this.refreshCommand.Execute(null);
        });

        return editCommand;
    }
}