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:
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 :)
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;
}
}
Comments
Anonymous
December 06, 2013
The problem with that solution is that you don't get notified when the user dismiss the flyout when tapping outside. Since SettingsFlyout does not inherit from FlyoutBase (go figure), the solution I found is a little bit tricky. Basically you have to retrieve the parent of the SettingsFlyout (which is a Popup) and subscribe to its Closed event. var parent = Parent as Popup; if(parent != null) parent.Closed += ... Using that solution you're notified when the user clicks the back button and when he dismisses the Flyout. -BenjaminAnonymous
December 06, 2013
Thx Benjamin for your comment. You are so right ! I will update the sample with your solution.