Charm Up your Metro App (Part II)

The Developer Preview version is here.

Disclaimer : These articles were written with the Developer Preview version. No guarantee it will work on other versions.

Charming Up Metro App (Part I) : Settings pane.
Charming Up Metro App (Part II) : Search Contract.
Charming Up Metro App (Part III) : Share Contract.
Charming Up Metro App (Part IV) : FilePicker Contract.

 

Contracts are special type of Charm,  a new feature introduced in Windows 8. It gives a standard way for applications to communicate. They don’t need to know each other, they just have to implement what Microsoft call Contract and the OS is doing the wiring.

There are different kinds of contracts (Search, Share and Pick). In this post I’ll talk about Search Contract.

I created a little Metro application called POTUS where you can search US Presidents, the source is at the end of the post. I’ll use it as an example on search contract.

Enabling Search Contract

First, you have to enable the Search Contract in your application. You have to modify the application manifest to do so.

Double click on the Package.appxmanifest to enter the manifest screen. There, add the Search declaration.

Next thing is to add a new Search Contract item. Add a new item to your project, and select Search Contract.

Now, VS has made lots of wiring for you (check in the App.xaml.cs, you’ll see the OnSearchActivated has been overridden) and created a basic search result screen (SearchResultPage.xaml).

Do the search

 

When you hit enter on the search textbox of the Search Pane, the Activate method of your search page is called.

It is in this method that you’ll do the search in your application and you’ll show the result. There is a SearchActivatedEventArgs parameter that contains the request.

Example in the POTUS application :

 public void Activate(SearchActivatedEventArgs args)
{
    var queryText = args.QueryText.ToLower();

    //Search the president list
    Results = ViewModelLocator.PresidentList.Where(d => d.FirstName.ToLower().Contains(queryText) || d.LastName.ToLower().Contains(queryText)).ToList();


    // Prepare bindable content
    var bindableProperties = new PropertySet();
    bindableProperties["QueryText"] = '"' + queryText + '"';
    bindableProperties["AllFilter"] = "All (" + Results.Count + ")";
    bindableProperties["Results"] = Results;
    this.DataContext = bindableProperties;

    // Display the results
    this.PreviousContent = Window.Current.Content;
    Window.Current.Content = this;
    Window.Current.Activate();
}

 

That’s all! Your search in implemented.

But…you can do way more.

Real-time results

If you want your result page to be updated in real-time, you have to hook to the QueryChanged event of the search pane :

 _searchpane = SearchPane.GetForCurrentView();

 _searchpane.QueryChanged += new TypedEventHandler(pane_QueryChanged);

Then, in your the method, you just do your search and show it, almost as in your Activate method (the main difference is you don’t put the focus on the result pane, as it will close the search pane)

 

void pane_QueryChanged(SearchPane sender, SearchPaneQueryChangedEventArgs args)
{
    var queryText = args.QueryText.ToLower();

    //Search the presidents list
    Results = ViewModelLocator.PresidentList.Where(d => d.FirstName.ToLower().Contains(queryText) || d.LastName.ToLower().Contains(queryText)).ToList();


    // Prepare bindable content
    var bindableProperties = new PropertySet();
    bindableProperties["QueryText"] = '"' + queryText + '"';
    bindableProperties["AllFilter"] = "All (" + Results.Count + ")";
    bindableProperties["Results"] = Results;
    this.DataContext = bindableProperties;

    // Display the results
    this.PreviousContent = Window.Current.Content;
    Window.Current.Content = this;
   
}

 

Les suggestions du Chef

You can also do something quite cool : suggestions. You can suggest keywords as the user is typing its request.

Again, very simple. Register to the event SuggestionsRequested :

 _searchpane = SearchPane.GetForCurrentView();

_searchpane.SuggestionsRequested += new TypedEventHandler(pane_SuggestionsRequested);

In the method, you get the suggestions from the user query, and you add them to the SearchSuggestionCollection using AppendQuerySuggestions. You can add a nice separator. The Metro guideline suggests (no pun) that you show a maximum of 10 keywords.

void pane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
    var suggestions = ViewModelLocator.PresidentList.Where(d => d.LastName.ToLower().Contains(args.QueryText.ToLower())).Select(d => d.LastName).OrderBy(d => d).ToList().Take(5);

    args.Request.SearchSuggestionCollection.AppendSearchSeparator("Suggestions");

    args.Request.SearchSuggestionCollection.AppendQuerySuggestions(suggestions);
}

 

Exact matches

You can have another kind of suggestion. More advanced. The Metro guideline suggests it is when you have an exact match. And suggest you show a maximum of 5. You can have an image, and two lines of text. It is in the same SuggestionRequested method, you also add it to the SearchSuggestionCollection but this time using AppendResultSuggestion.

Example in POTUS, with normal and exact suggestions :

void pane_SuggestionsRequested(SearchPane sender, SearchPaneSuggestionsRequestedEventArgs args)
{
    var suggestions = ViewModelLocator.PresidentList.Where(d => d.LastName.ToLower().Contains(args.QueryText.ToLower())).Select(d => d.LastName).OrderBy(d => d).ToList().Take(5);

    args.Request.SearchSuggestionCollection.AppendSearchSeparator("Suggestions");

    args.Request.SearchSuggestionCollection.AppendQuerySuggestions(suggestions);

    var exactmatches = ViewModelLocator.PresidentList.Where(d => d.LastName.ToLower() == args.QueryText.ToLower()).OrderBy(d => d.LastName).ToList().Take(5);

    foreach (var exactmatch in exactmatches)
    {
        var fullname = exactmatch.LastName + " " + exactmatch.FirstName;
        var terms = exactmatch.TermBeginString + "->" + exactmatch.TermEndString;
        args.Request.SearchSuggestionCollection.AppendResultSuggestion(fullname, terms, exactmatch.ID, StreamReference.CreateFromUri(new Uri("ms-resource://charmup2/Files/Images/" + exactmatch.Picture)), "no image");
    }

}

 

The third parameter of AppendResultSuggestion is a tag. The tag is useful because when the user select an exact match, a ResultSuggestionChosen event is launched. You can register to it, and you can get that tag from the method parameters. You can then make your app navigate directly to the information.

 void _searchpane_ResultSuggestionChosen(SearchPane sender, SearchPaneResultSuggestionChosenEventArgs args)
{
    var selectedpresident = ViewModelLocator.PresidentList.Where(d => d.ID == args.Tag).FirstOrDefault();

    if (selectedpresident == null)
        return;

    var queryText = selectedpresident.FirstName + " " + selectedpresident.LastName;

    // Prepare bindable content
    var bindableProperties = new PropertySet();
    bindableProperties["QueryText"] = '"' + queryText + '"';
    bindableProperties["AllFilter"] = "All (1)";
    bindableProperties["Results"] = new List{selectedpresident};
    this.DataContext = bindableProperties;

    // Display the results
    this.PreviousContent = Window.Current.Content;
    Window.Current.Content = this;
    Window.Current.Activate();
}

In the example here I just show the exact match in the search result page because it is not a full application, just a simple demo.

Conclusion

As you see, very simple. But can have neat results !

Sources POTUS

Next contract : Share

The following two tabs change content below.
Olivier

Olivier

Mobile Engineer at Arcana Studio
Freelance developer. Passionate for mobile Development and IoT. Expert in WinRT and Xamarin. MVP Windows Platform Development Nokia Developer Champion