As part of something a lot larger that I am currently working on I wanted to implement the Search charm logic in Windows 8.
Now being the model citizen that I am, I have read all the good technical bumpg around Windows 8, and I paid particular attention to the fact that we really need to keep the UI free, as anything more than 30/300ms (can’t recall which) on a touch system just feels broken.
So what does that mean? Well it means I have been a good chat and played nice and used Async/Await where needed. All good so far.
So now onto the Search charm. I obviously enabled this in the manifest file. Then I wrote the following code which I expected to work
sealed partial class App : Application { public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } protected override async void OnLaunched(LaunchActivatedEventArgs args) { .... .... .... .... SetupSearch(); } private async void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); await SuspensionManager.SaveAsync(); deferral.Complete(); } private void SetupSearch() { var searchPane = SearchPane.GetForCurrentView(); searchPane.SuggestionsRequested += searchPane_SuggestionsRequested; searchPane.ResultSuggestionChosen += searchPane_ResultSuggestionChosen; } void searchPane_ResultSuggestionChosen(SearchPane sender, SearchPaneResultSuggestionChosenEventArgs args) { MessageDialog dialog = new MessageDialog(string.Format("You picked {0}",args.Tag)); dialog.ShowAsync(); } async void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args) { var suggestions = Enumerable.Range(1, 2).Select(x => new { Desc = string.Format("{0}_{1}", args.QueryText, x.ToString()), Id = x }); //To simulate waiting on something more meaningful (such as a webservice etc etc) await Task.Delay(2000); var imageUri = new Uri("ms-appx:///Assets/search40.png"); var imageSource = Windows.Storage.Streams. RandomAccessStreamReference.CreateFromUri(imageUri); args.Request.SearchSuggestionCollection. AppendSearchSeparator("Demo Suggestions"); foreach (var suggestion in suggestions) { args.Request.SearchSuggestionCollection.AppendResultSuggestion( "Suggestion", suggestion.Desc, suggestion.Id.ToString(), imageSource, ""); } } }
That looked fair enough to me. But when I ran this code I got this Exception
Most strange.
Turns out this is an issue for which there is a solution, that is just rather poorly documented. In fact it borrows a lot from the jQuery Deferred pattern (which you can read more about here)
So what is the solution. Well as I say WinRT has certainly borrowed from the jQuery Deferral / Promise idea. Where we ask for a Deferrable object, do some work, and then complete the Deferrable object.
So here is the code above refactored to use a WinRT Deferrable object. This technique can be used with most of the charms.
sealed partial class App : Application { public App() { this.InitializeComponent(); this.Suspending += OnSuspending; } protected override async void OnLaunched(LaunchActivatedEventArgs args) { .... .... .... .... SetupSearch(); } private async void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); await SuspensionManager.SaveAsync(); deferral.Complete(); } private void SetupSearch() { var searchPane = SearchPane.GetForCurrentView(); searchPane.SuggestionsRequested += searchPane_SuggestionsRequested; searchPane.ResultSuggestionChosen += searchPane_ResultSuggestionChosen; } void searchPane_ResultSuggestionChosen(SearchPane sender, SearchPaneResultSuggestionChosenEventArgs args) { MessageDialog dialog = new MessageDialog(string.Format("You picked {0}",args.Tag)); dialog.ShowAsync(); } async void searchPane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args) { var suggestions = Enumerable.Range(1, 2).Select(x => new { Desc = string.Format("{0}_{1}", args.QueryText, x.ToString()), Id = x }); //FIXED CODE : Use args.Request.GetDeferral() / deferral.Complete var deferral = args.Request.GetDeferral(); await Task.Delay(2000); var imageUri = new Uri("ms-appx:///Assets/search40.png"); var imageSource = Windows.Storage.Streams. RandomAccessStreamReference.CreateFromUri(imageUri); args.Request.SearchSuggestionCollection. AppendSearchSeparator("Demo Suggestions"); foreach (var suggestion in suggestions) { args.Request.SearchSuggestionCollection.AppendResultSuggestion( "Suggestion", suggestion.Desc, suggestion.Id.ToString(), imageSource, ""); } deferral.Complete(); } }
As always here is a small demo project (requires Windows 8 / Visual Studio 2012) : AsyncSearchSuggestionDemo.zip
Thank you, that helped me!
Cool