Windows 8–Search Suggestions

I recently started a series of postings from my tour of presentations in EuropeSearchResults and the UK.  Today I’d like to return to Searching (which I started to cover here), and this time take a look at what it takes to have your application offer Search suggestions as the user types into the Search box. 

To examine this, we’ll create a Windows 8 application with the suggestions hard coded, but you can just as easily obtain the suggestions from a web service, or database, etc.

Once again what I’m going to do is strip down one of the Microsoft SDK examples and then build it up from scratch.  You can find the original sample by clicking on Help->Samples in Visual Studio.  The one you want is Search Contract.

To begin, create a new Blank application in Visual Studio and call it SearchQueryList.  The very first thing to do is to double-click on PackageAppxManifest and then click on the Declarations tab.  Choose Search from the available declarations and add it to the project.

Open App.xaml.cs and override OnSearchActivated as follows,

   1: async protected override void OnSearchActivated(SearchActivatedEventArgs args)

   2: {

   3:     await EnsureMainPageActivatedAsync(args);

   4:     if (args.QueryText == "")

   5:     {

   6:         // navigate to landing page.

   7:     }

   8:     else

   9:     {

  10:         MainPage.Current.ProcessQueryText(args.QueryText);

  11:     }

  12: }

This makes a call to EnsureMainPageActivatedAsync, so let’s add that as well, while we are here,

   1: async private Task EnsureMainPageActivatedAsync(IActivatedEventArgs args)

   2: {

   3:     if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)

   4:     {

   5:         // Do an asynchronous restore

   6:  

   7:     }

   8:  

   9:     if (Window.Current.Content == null)

  10:     {

  11:         var rootFrame = new Frame();

  12:         rootFrame.Navigate(typeof(MainPage));

  13:         Window.Current.Content = rootFrame;

  14:     }

  15:  

  16:     Window.Current.Activate();

  17: }

Let’s turn to MainPage.xaml where our UI is dead-simple, just a text block to display the user’s query selection,

   1: <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

   2:     <TextBlock x:Name="StatusBlock"

   3:                Margin="0,0,0,5"

   4:                Text="Ready..."

   5:                FontSize="42"/>

   6: </Grid>

All the interesting work happens in the code behind for MainPage. 

We begin by defining a static reference to MainPage itself, along with a private member of type SearchPane. 

   1: public static MainPage Current;

   2: private SearchPane searchPane;

These are initialized in the constructor,

   1: public MainPage()

   2: {

   3:     this.InitializeComponent();

   4:     Current = this;

   5:     searchPane = SearchPane.GetForCurrentView();

   6: }

In addition, at the top of the file, we create a constant and, more important, we initialize a static array of strings that will serve for our suggestion list,

   1: internal const int SearchPaneMaxSuggestions = 5;

   2:  

   3: private static readonly string[] suggestionList =

   4:     {

   5:         "Shanghai", "Istanbul", "Karachi", "Delhi", "Mumbai", "Moscow", "São Paulo", "Seoul", "Beijing", "Jakarta",

   6:         "Tokyo", "Mexico City", "Kinshasa", "New York City", "Lagos", "London", "Lima", "Bogota", "Tehran", "Ho Chi Minh City",

   7:         "Hong Kong", "Bangkok", "Dhaka", "Cairo", "Hanoi", "Rio de Janeiro", "Lahore", "Chonquing", "Bangalore", "Tianjin",

   8:         "Baghdad", "Riyadh", "Singapore", "Santiago", "Saint Petersburg", "Surat", "Chennai", "Kolkata", "Yangon", "Guangzhou",

   9:         "Alexandria", "Shenyang", "Hyderabad", "Ahmedabad", "Ankara", "Johannesburg", "Wuhan", "Los Angeles", "Yokohama",

  10:         "Abidjan", "Busan", "Cape Town", "Durban", "Pune", "Jeddah", "Berlin", "Pyongyang", "Kanpur", "Madrid", "Jaipur",

  11:         "Nairobi", "Chicago", "Houston", "Philadelphia", "Phoenix", "San Antonio", "San Diego", "Dallas", "San Jose",

  12:         "Jacksonville", "Indianapolis", "San Francisco", "Austin", "Columbus", "Fort Worth", "Charlotte", "Detroit",

  13:         "El Paso", "Memphis", "Baltimore", "Boston", "Seattle Washington", "Nashville", "Denver", "Louisville", "Milwaukee",

  14:         "Portland", "Las Vegas", "Oklahoma City", "Albuquerque", "Tucson", "Fresno", "Sacramento", "Long Beach", "Kansas City",

  15:         "Mesa", "Virginia Beach", "Atlanta", "Colorado Springs", "Omaha", "Raleigh", "Miami", "Cleveland", "Tulsa", "Oakland",

  16:         "Minneapolis", "Wichita", "Arlington", " Bakersfield", "New Orleans", "Honolulu", "Anaheim", "Tampa", "Aurora",

  17:         "Santa Ana", "St. Louis", "Pittsburgh", "Corpus Christi", "Riverside", "Cincinnati", "Lexington", "Anchorage",

  18:         "Stockton", "Toledo", "St. Paul", "Newark", "Greensboro", "Buffalo", "Plano", "Lincoln", "Henderson", "Fort Wayne",

  19:         "Jersey City", "St. Petersburg", "Chula Vista", "Norfolk", "Orlando", "Chandler", "Laredo", "Madison", "Winston-Salem",

  20:         "Lubbock", "Baton Rouge", "Durham", "Garland", "Glendale", "Reno", "Hialeah", "Chesapeake", "Scottsdale",

  21:         "North Las Vegas", "Irving", "Fremont", "Irvine", "Birmingham", "Rochester", "San Bernardino", "Spokane",

  22:         "Toronto", "Montreal", "Vancouver", "Ottawa-Gatineau", "Calgary", "Edmonton", "Quebec City", "Winnipeg", "Hamilton"

  23:     };

In the OnNavigatedTo method we register to be notified of three events:

  • Query suggestions are requested
  • A Query suggestion is chosen
  • The Query is submitted
   1: protected override void OnNavigatedTo(NavigationEventArgs e)

   2: {

   3:     SearchPane.GetForCurrentView().QuerySubmitted += 

   4:         new TypedEventHandler<SearchPane, SearchPaneQuerySubmittedEventArgs>

   5:             (OnQuerySubmitted);

   6:  

   7:     searchPane.SuggestionsRequested += 

   8:         new TypedEventHandler<SearchPane, SearchPaneSuggestionsRequestedEventArgs>

   9:             (OnSearchPaneSuggestionsRequested);

  10:  

  11:     searchPane.ResultSuggestionChosen += OnSearchPaneResultSuggestionChosen;

  12: }

Key to this discussion is the implementation of OnSearchPaneSuggestionsRequested – the method that is called as each letter is typed into the query pane,

Let’s take this method apart, step by step.

We begin by extracting the query string and making sure it isn’t empty,

   1: private void OnSearchPaneSuggestionsRequested(

   2:     SearchPane sender, SearchPaneSuggestionsRequestedEventArgs e)

   3: {

   4:     var queryText = e.QueryText;

   5:     if (string.IsNullOrEmpty(queryText))

   6:     {

   7:        NotifyUser("Use the search pane to submit a query");

   8:     }

   9:     else

  10:     {

We pull out the request which will hold our suggestions (unless we have an exact match), initialize a local boolean to false to keep track of whether we have found a recommendation, and begin iterating through each string in our suggestion list, so that we can compare it with the query string,

   1: var request = e.Request;

   2: bool isRecommendationFound = false;

   3: foreach (string suggestion in suggestionList)

   4: {

The first thing we check for is an exact match,

   1: if (suggestion.CompareTo(queryText) == 0)

   2: {

If we get an exact match we’re going to show the match prepended by an image that we’ve placed in the Assets folder,

   1: RandomAccessStreamReference image = 

   2:     RandomAccessStreamReference.CreateFromUri(new Uri("ms-appx:///Assets/windows-sdk.png"));

   3:  

   4: request.SearchSuggestionCollection.AppendResultSuggestion(

   5:     suggestion,              // text to display 

   6:     "found " + suggestion,   // details

   7:     "tag " + suggestion,     // tags usable when called back by ResultSuggestionChosen event

   8:     image,                   // image if any

   9:     "image of " + suggestion // image alternate text

  10:     );

  11: isRecommendationFound = true;

Notice that in this case the string we’re assembling (with the image and the matching city name) is added by calling the AppendResultsSuggestion method on the SearchSuggestionCollection obtained from the request object we extracted earlier.  See the figure below for what this looks like,

SingleSearchResult

If we don’t get an exact match we look for a partial match and if we get one, we add the string to the suggestion list,

   1: else  // otherwise, this is a suggestion

   2:     if (suggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase))

   3:     {

   4:         // Add suggestion to Search Pane

   5:         request.SearchSuggestionCollection.AppendQuerySuggestion(suggestion);

   6:     }

We then add some code to break the search off if we have 5 or more hits,

   1: if (request.SearchSuggestionCollection.Size >= MainPage.SearchPaneMaxSuggestions)

   2: {

   3:     break;

   4: }

You can see what this looks like in the figure at the top of this article.

Finally, we update the TextBlock depending on whether we found any matches and if we found an exact match,

   1: if (request.SearchSuggestionCollection.Size > 0)

   2: {

   3:     // Demo: show if it is a recommendation or just a partial suggestion

   4:     string prefix = isRecommendationFound ? "* " : "";

   5:     NotifyUser(prefix + "Suggestions provided for query: " + queryText);

   6: }

   7: else

   8: {

   9:     NotifyUser("No suggestions provided for query: " + queryText);

  10: }

Once again, Windows is doing all the heavy lifting of obtaining the query and creating the suggestion list; all we have to do is to find the matching values and add them to the SearchSuggestionCollection in response to the SuggestionsRequested event.

You can find the complete source code for this project here

Share

About Jesse Liberty

Jesse Liberty is a Master Consultant for Falafel Software, and has three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Technical Evangelist for Telerik and for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS.
This entry was posted in Data, Essentials, Metro, Mini-Tutorial, Windows 8, WinRT and tagged . Bookmark the permalink.

8 Responses to Windows 8–Search Suggestions

  1. Raghu says:

    Nice explanation sir,
    Thanks for clearing doubts!!

  2. Charissa says:

    Hello, its good piece of writing regarding media print, we all
    know media is a enormous source of data.

  3. Tremendous issues here. I’m very happy to peer your article. Thanks so much and I am taking a look forward to touch you. Will you kindly drop me a e-mail?

  4. Hello, i believe that i saw you visited my blog thus i came to return the prefer?
    .I am attempting to in finding things to enhance my site!
    I guess its adequate to use a few of your ideas!!

  5. nails shop says:

    you’re really a just right webmaster. The website loading speed is amazing. It seems that you’re doing any unique trick.
    Also, The contents are masterwork. you’ve performed a great process in this matter!

  6. This paragraph will assist the internet visitors for creating new weblog or even
    a weblog from start to end.

  7. Nice post. I used to be checking constantly this blog and I am impressed!
    Extremely helpful information particularly the last section :) I deal with such information a lot.
    I used to be seeking this particular information for a long time.
    Thank you and good luck.

  8. Pingback: Windows 8 Developer Links – 2012-09-24 | Dan Rigby

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>