Partilhar via


Quickstart: Adding search to an app (XAML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Most users will rely on search to find what they're looking for. For example, if your app plays media files, users will expect to be able to search for a specific song or video; if your app is a cooking app, users will expect to search for specific recipes or ingredients.

With a little planning, it's not that difficult to add search to your app. Here's what you need:

  • A data source to search. You need some sort of catalog or inventory of items that users might want to search for. The more descriptive you can make this inventory, the better your search results will be.
  • A control for entering search queries. Windows provides a SearchBox control that your app can use. The SearchBox provides an input area for entering queries, a search button for executing the search, and events for handling search queries. It even provides some search suggestions automatically.
  • A page for displaying search results. Microsoft Visual Studio provides the Search Results Page template that creates a lot of the code you need to handle search queries and display results.

This quickstart tells you how to use these items to add search functionality to your app.

Prerequisites

Set up your data

When the user enters a search query, your app searches for items that user might be looking for. The data your app searches could take several forms: it might be an XML file, JavaScript Object Notation (JSON) data, a database, a web service, or files in the file system.

The examples in this quickstart use the sample data provided when you create a new project in Microsoft Visual Studio.

When you use Visual Studio to create a new Grid app, Hub app, or Split app, it creates a folder named DataModel that contains a JSON file that contains sample data and a code file that provides objects for accessing the JSON data.

The code files define three classes:

  • SampleDataItem: An individual data item.
  • SampleDataGroup: A group of SampleDataItem objects.
  • SampleDataSource: A collection of SampleDataGroup objects.

The data itself is defined in the JSON file and is organized into groups and items. Here's an example of what the data looks like:

{"Groups":[
  {
    "UniqueId": "Group-1",
    "Title": "Group Title: 1",
    "Subtitle": "Group subtitle: 1",
    "ImagePath": "Assets/DarkGray.png",
    "Description" : "Group Description: Lorem ipsum dolor sit amet...",
    "Items":
    [
      {
        "UniqueId": "Group-1-Item-1",
        "Title": "Item Title: 1",
        "Subtitle": "Item Subtitle: 1",
        "ImagePath": "Assets/LightGray.png",
        "Description" : "Item Description: Pellentesque porta, mauris... neque tortor ac erat.",
        "Content" : "Curabitur class..."
      },
      {
        "UniqueId": "Group-1-Item-2",
        "Title": "Item Title: 2",
        "Subtitle": "Item Subtitle: 2",
        "ImagePath": "Assets/DarkGray.png",
        "Description" : "Item Description: Pellentesque porta, mauris... neque tortor ac erat.",
        "Content" : "Curabitur class..."
      },

(For more info about working with data, see Adding data to a project template.)

Of course, you'll want to use your own data when implementing Search in your app, but creating an initial version that uses this sample data can help you get familiar with Search and may give you some ideas as to how to structure your own data to make it searchable.

Add a search results page

The search results page processes search queries and displays the result. Let's add one to your project.

Add the Search Results Page item

  1. In the Solution Explorer, right-click on the project node to open the project shortcut menu, and then click Add > New Item. The Add New Item dialog box appears.
  2. In the center pane of the Add New Item dialog box, click Search Results Page. For this example, change the name to SearchResultsPageExample.xaml.
  3. Click the Add button. (You might get a notification that Visual Studio needs to add some files; click Yes to add them automatically.)

Visual Studio creates the SearchResultsPageExample.xaml and an accompanying code-behind file, SearchResultsPageExample.xaml.cs.

Let's take a look at the SearchResultsPageExample.xaml file. Here are some of the more important items it contains:

  • resultsViewSource, a CollectionViewSource resource whose Source is bound to the "Results" property of the DefaultViewModel. (The DefaultViewModel is defined on each page; for more info, see Adding data to a project template.) To display results, we set the DefaultViewModel["Results"] to a list that contains the results.
  • filtersViewSource, a CollectionViewSource resource whose Source is bound to the "Filters" property of the DefaultViewModel.
  • filtersItemsControl, an ItemsControl that is bound to filtersViewSource. This ItemsControl displays your search filters.
  • resultsGridView, a GridView that is bound to resultsViewSource. This GridView displays your search results.
  • A VisualStateManager with two VisualState definitions: one for "ResultsFound", and one for "NoResultsFound". When the visual state is set to "NoResultsFound", the VisualStateManager hides the resultsGridView control and displays some text that says "No results match your search" instead.

The SearchResultsPageExample.xaml.cs contains two methods that we'll modify: navigationHelper_LoadState and Filter_Checked.

We still have work to do on the search results page, but first, let's add a SearchBox to our app. Having a SearchBox will make it easier for us to test our search results page as we implement it.

A SearchBox lets the user enter queries. It can also display suggestions.

To add a SearchBox to your app, just add this markup to a XAML page:

<SearchBox
    Height="35" Width="270"
    Margin="0, 25, 25, 0" />

(You don't have to set the Height, Width, and Margin to these values, but these settings work well for most apps.)

Where should you place your search box? We recommend putting a search box on each page of your app so users can easily search whenever they want to. If space is an issue, you can put the search box in a top app bar.

Usually, the best location to put your SearchBox is in the upper-right corner of the page. Most pages that you create from a Visual Studio template (such as the Basic Page template) have a Grid that contains the page title and a back button:

<!-- Back button and page title -->
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="120"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="backButton" Margin="39,59,39,0" 
                Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                Style="{StaticResource NavigationBackButtonNormalStyle}"
                VerticalAlignment="Top"
                AutomationProperties.Name="Back"
                AutomationProperties.AutomationId="BackButton"
                AutomationProperties.ItemType="Navigation Button"/>
    <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" 
                Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                IsHitTestVisible="false" TextWrapping="NoWrap" 
                VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>

Add an a third column to the Grid, set its Width to Auto, add your SearchBox, and set its Grid.Column to "2":

<!-- Back button and page title -->
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="120"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Button x:Name="backButton" Margin="39,59,39,0" 
                Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                Style="{StaticResource NavigationBackButtonNormalStyle}"
                VerticalAlignment="Top"
                AutomationProperties.Name="Back"
                AutomationProperties.AutomationId="BackButton"
                AutomationProperties.ItemType="Navigation Button"/>
    <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" 
                Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                IsHitTestVisible="false" TextWrapping="NoWrap" 
                VerticalAlignment="Bottom" Margin="0,0,30,40"/>
    <SearchBox
        Grid.Column="2"
        Height="35" Width="270"  Margin="0,25,25,0" />
</Grid>

The examples in this quickstart use a hub app template. Let's walk through adding a SearchBox to a hub page in more detail.

Add a SearchBox to a hub page

  1. Open the XAML file for your hub page and go to the markup that defines the Hub control (this page is usually named HubPage.xaml).

  2. Set the Hub control's HorizontalContentAlignment to Stretch.

    <Hub SectionHeaderClick="Hub_SectionHeaderClick" HorizontalContentAlignment="Stretch">
    
  3. In the Hub control's Header:

    1. Add a third column to the Grid and set its Width to Auto.

    2. Add a SearchBox.

                <Hub.Header>
                    <!-- Back button and page title -->
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Button  x:Name="backButton" Style="{StaticResource NavigationBackButtonNormalStyle}"
                            Margin="-1,-1,39,0" 
                            VerticalAlignment="Top"
                            Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
                            AutomationProperties.Name="Back"
                            AutomationProperties.AutomationId="BackButton"
                            AutomationProperties.ItemType="Navigation Button"/>
                        <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1" 
                            VerticalAlignment="Top" IsHitTestVisible="false" TextWrapping="NoWrap" />
                        <SearchBox
                            x:Name="mySearchBox"
                            Grid.Column="2"
                            Height="35" Width="270"
                            PlaceholderText="Search" 
                            QuerySubmitted="SearchBox_QuerySubmitted"/>
                    </Grid>
                </Hub.Header>
    

Handle the QuerySubmitted event

  1. Open the hub page's code-behind file (HubPage.xaml.cs). Create a QuerySubmitted event handler for your SearchBox and name it SearchBox_QuerySubmitted.

            private void SearchBox_QuerySubmitted(SearchBox sender, SearchBoxQuerySubmittedEventArgs args)
            {
    
            }
    
  2. Use the event handler to navigate to your new search results page.

            private void SearchBox_QuerySubmitted(SearchBox sender, SearchBoxQuerySubmittedEventArgs args)
            {
                 this.Frame.Navigate(typeof(SearchResultsPageExample), args.QueryText);
            }
    

Here's what the hub page should look like:

Let's try it out. Type a test query into the SearchBox and press Enter.

The QuerySubmitted event handler that you wrote navigates to the search results page, passing the query you entered.

It doesn't display any search results, but our search results page is working. In the next steps, we'll update the page to actually search our data for matches. But before we do, go back to the search box and enter your query again. Before you finish typing the query a second time, it should show up as a suggestion:

One of the neat features of the SearchBox is that it automatically uses your search history to provide suggestions. You'll learn more about how to customize suggestions in a later step.

Search your data

It's time to go back to our search results page. The search results page processes search queries and displays the results. In this step, you write code to process incoming queries and prepare a list of results.

Import your data and define your search results

  1. Open SearchResultsPageExample.xaml.cs, the code-behind file for your search results page.

  2. At the top of the page, add a reference to your data classes. As we mentioned previously, our examples use the sample data that Visual Studio created for us when we created a new app. These classes belong to the <ApplicationNamespace>.Data namespace.

    // Change this to <YourApplicationNamespace>.Data
    using SearchBoxAndPageExample.Data;
    
  3. In your SearchResultsPageExample class, create a property for storing and displaying results. This example uses a Dictionary to store results.

    public Dictionary<String, IEnumerable<SampleDataItem>> AllResultsCollection { get; set; }
    

When your app navigates to your search results page, it calls the page's navigationHelper_LoadState method, a method that Visual Studio created for you. Right now, that code doesn't do much. In the next steps, you replace it with your own code for searching your data.

Implement your search logic

  1. In the SearchResultsPageExample.xaml.cs file, go to the navigationHelper_LoadState method and delete the code it contains.

  2. Make the navigationHelper_LoadState method asynchronous by adding the async keyword.

            private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
            {
    
  3. When your app navigates to your search results page, it passes the search query as a navigation parameter. Retrieve the query text from the NavigationParameter property of the LoadStateEventArgs parameter, e. (The LoadStateEventArgs class is defined in NavigationHelper.cs, one of the helper files included in your project when you create a grid, hub, or split app.)

                var queryText = e.NavigationParameter as String;
    
  4. Initialize the results list and create a variable for tracking the total number of matching items.

                // Initialize the results list.
                AllResultsCollection = new Dictionary<string, IEnumerable<SampleDataItem>>();
    
                // Keep track of the number of matching items. 
                var totalMatchingItems = 0;
    
  5. Each time we find a matching item, we'll add the matching item's group to a list of filters. The Search Results Page template creates a Filter class for you. Let's create a List of Filter objects. When we find matching items, we'll add the items' groups to the filter list.

                var filterList = new List<Filter>();
    
  6. Next, we search our data and store results. First, get all the groups that contain your data.

    
                var groups = await SampleDataSource.GetGroupsAsync();
    
  7. Now create a foreach loop to iterate through each group so you can check their items for matches.

                foreach (var group in groups)
                {
    
  8. Inside the foreach loop:

    1. Each group in our data class, SampleDataGroup, contains an Items property that returns an ObservableCollection of SampleDataItem objects. You can use the Where method to search this collection for matches.

      This example performs a case-insensitive search for items whose title contains the query and stores the returned collection of matching items in a variable named matchingItems. This example only checks the title of each item; it could also check additional data, such as the subtitle or description.

      
                      var matchingItems = group.Items.Where(
                          item => 
                              item.Title.IndexOf(
                                  queryText, StringComparison.CurrentCultureIgnoreCase) > -1); 
      

      This example uses the IndexOf(String, StringComparison) method to perform the string comparison. We use this overload so that we can specify a culture-sensitive, case-insensitive search. Making the search culture sensitive will help your code work better with more languages.

      Tip  

      This example only checks one field of our data for query matches, the title field. A real app might search multiple fields, such as a subtitle or description, for matches.

       

      Tip  

      One way to improve the user experience is to track the relevancy of search requests and sort the results by relevance. For example, you might assign each matched item a certain number of points depending on the quality of the match: 5 points for a match in the title field, 2 points for a match in the subtitle field, and 1 point for a match in the description field. Before you present the results, sort them so that items with the most points appear first.

       

    2. Find out how many matches there are. If we found matches, add the matching items to the results list and add the group to the filter list.

      
                      int numberOfMatchingItems = matchingItems.Count();
                      totalMatchingItems += numberOfMatchingItems;
                      if (numberOfMatchingItems > 0)
                      {
                          AllResultsCollection.Add(group.Title, matchingItems);
                          filterList.Add(new Filter(group.Title, numberOfMatchingItems));
                      }
                  }
      
  9. Being able to filter by group is nice, but users might want to view all the search results at once. Add an entry for "All" to the beginning of the filter list.

                // Create an entry for "All" for viewing all search results. 
                filterList.Insert(0, new Filter("All", totalMatchingItems, true));
    
  10. The sample results page has a DefaultViewModel property that is bound to the default DataContext of the page. The DefaultViewModel is a type of dictionary that you can use to store objects that affect the page's UI. Use this property to display filters and the original query: set the DefaultViewModel object's "QueryText" to the original query text, set the "Filters" to your "filterList", and set "ShowFilters" to true.

                // Communicate results through the view model
                this.DefaultViewModel["QueryText"] = '\u201c' + queryText + '\u201d';
                this.DefaultViewModel["Filters"] = filterList;
                this.DefaultViewModel["ShowFilters"] = true;
            }
    

    Here's the complete code for the updated method:

            private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
            {
                var queryText = e.NavigationParameter as String;
    
                // Initialize the results list.
                AllResultsCollection = new Dictionary<string, IEnumerable<SampleDataItem>>();
    
                // Keep track of the number of matching items. 
                var totalMatchingItems = 0;
    
                var filterList = new List<Filter>();
    
                var groups = await SampleDataSource.GetGroupsAsync();
                foreach (var group in groups)
                {
                    var matchingItems = group.Items.Where(
                        item => 
                            item.Title.IndexOf(
                                queryText, StringComparison.CurrentCultureIgnoreCase) > -1); 
                    int numberOfMatchingItems = matchingItems.Count();
                    totalMatchingItems += numberOfMatchingItems; 
                    if (numberOfMatchingItems > 0)
                    {
                        AllResultsCollection.Add(group.Title, matchingItems);
                        filterList.Add(new Filter(group.Title, numberOfMatchingItems));
                    }
                }
    
                // Create an entry for "All" for viewing all search results. 
                filterList.Insert(0, new Filter("All", totalMatchingItems, true));
    
                // Communicate results through the view model
                this.DefaultViewModel["QueryText"] = '\"' + queryText + '\"';
                this.DefaultViewModel["Filters"] = filterList;
                this.DefaultViewModel["ShowFilters"] = true;
    
            }
    

Run the app and search for "Item". The app displays the number of hits found for each group, but it doesn't actually display any search results:

Display search results

The navigationHelper_LoadState method searches for items and stores the matching results and filters, but it's the Filter_Checked event handler that actually displays the results. When the user selects a filter, this method displays the results that match that filter.

Update the Filter_Checked method

  1. In your code-behind file for the search results page, go to the Filter_Checked method and delete the template-generated code it contains.

            void Filter_Checked(object sender, RoutedEventArgs e)
            {
    
            }
    
  2. The filters list is a set of radio buttons. Find out which filter was selected by retrieving the DataContext of the radio button that fired the event.

            void Filter_Checked(object sender, RoutedEventArgs e)
            {
                var filter = (sender as FrameworkElement).DataContext as Filter;
    
  3. The SearchResultsPageExample.xaml file contains a CollectionViewSource named filtersViewSource. This data source is bound to our filters list. When the user selects a radio button, use the MoveCurrentTo method to select the current filter.

                // Mirror the change into the CollectionViewSource.
                if (filtersViewSource.View != null)
                {
                    filtersViewSource.View.MoveCurrentTo(filter);
                }
    
  4. Make sure the filter isn't null, then set its Active property to true. Our XAML file binds the IsChecked state of the radio button to the filter's Active property, so updating this property ensures that the radio button appears checked.

                // Determine which filter was selected
                if (filter != null)
                {
                    // Mirror the results into the corresponding Filter object to allow the
                    // RadioButton representation used when not snapped to reflect the change
                    filter.Active = true;
    
  5. We use the filter to determine which items in the results collection to display. If the "all" filter was selected, display all the items.

                    if (filter.Name.Equals("All"))
                    {
                        var tempResults = new List<SampleDataItem>();
    
                        // Add the items from each group to the temporary results
                        // list. 
                        foreach (var group in AllResultsCollection)
                        {
                            tempResults.AddRange(group.Value);
    
                        }
                        // Display the items.
                        this.DefaultViewModel["Results"] = tempResults;
                    }
    
  6. Otherwise, we display the items that belong to the selected filter.

                    else if (AllResultsCollection.ContainsKey(filter.Name))
                    {
                        this.DefaultViewModel["Results"] =
                          new List<SampleDataItem>(AllResultsCollection[filter.Name]);
                    }
    
  7. Verify that we actually have results to display. If we do, change the page's state to the "ResultsFound" state. Otherwise, we change the page's state to "NoResultsFound".

                    // Ensure results are found
                    object results;
                    ICollection resultsCollection;
                    if (this.DefaultViewModel.TryGetValue("Results", out results) &&
                        (resultsCollection = results as ICollection) != null &&
                        resultsCollection.Count != 0)
                    {
                        VisualStateManager.GoToState(this, "ResultsFound", true);
                        return;
                    }
                }
    
                // Display informational text when there are no search results.
                VisualStateManager.GoToState(this, "NoResultsFound", true);
            }
    

Here's the complete code for the Filter_Checked method.

        void Filter_Checked(object sender, RoutedEventArgs e)
        {
            // Retrieve the data context of the sender (the selected radio button).
            // This gives us the selected Filter object. 
            var filter = (sender as FrameworkElement).DataContext as Filter;

            // Mirror the change into the CollectionViewSource.
            // This is most likely not needed.
            if (filtersViewSource.View != null)
            {
                filtersViewSource.View.MoveCurrentTo(filter);
            }

            // Determine which filter was selected
            if (filter != null)
            {
                // Mirror the results into the corresponding Filter object to allow the
                // RadioButton representation used when not snapped to reflect the change
                filter.Active = true;

                // TODO: Respond to the change in active filter by setting this.DefaultViewModel["Results"]
                //       to a collection of items with bindable Image, Title, Subtitle, and Description properties

                if (filter.Name.Equals("All"))
                {
                    var tempResults = new List<SampleDataItem>();

                    // Add the items from each group to the temporary results
                    // list. 
                    foreach (var group in AllResultsCollection)
                    {
                        tempResults.AddRange(group.Value);

                    }

                    // Display the items.
                    this.DefaultViewModel["Results"] = tempResults;
                }
                else if (AllResultsCollection.ContainsKey(filter.Name))
                {
                    this.DefaultViewModel["Results"] =
                      new List<SampleDataItem>(AllResultsCollection[filter.Name]);
                }

                // Ensure results are found
                object results;
                ICollection resultsCollection;
                if (this.DefaultViewModel.TryGetValue("Results", out results) &&
                    (resultsCollection = results as ICollection) != null &&
                    resultsCollection.Count != 0)
                {
                    VisualStateManager.GoToState(this, "ResultsFound", true);
                    return;
                }
            }

            // Display informational text when there are no search results.
            VisualStateManager.GoToState(this, "NoResultsFound", true);
        }

Run the app and search for "Item-1" (if you're using your own data, search for something you know will get hits).

(Optional) Update the GridView control's ItemTemplate

SearchResultsPageExample.xaml uses a GridView to display search results. The template-generated code for the GridView control's ItemTemplate is designed to work with the sample data source that Visual Studio creates for you; it expects the following fields in each data item: "Image", "Title", "Subtitle", and "Description".

If your data items have different fields, you need to modify the ItemTemplate. For instructions on modifying the ItemTemplate, see Quickstart: Adding ListView and GridView controls.

(Optional) Add search suggestions

Search suggestions are displayed under the search box in the search pane. Suggestions are important because they save users' time and give valuable hints about the kinds of things users can search for in your app.

You can get suggestions from several sources:

  • You can define them yourself. For example, you could create a list of car manufacturers.
  • You can get them from Windows if your app searches local files.
  • You can get them from a web service or server.

For user experience guidelines for displaying suggestions, see Guidelines and checklist for search.

You can use LocalContentSuggestionSettings to add suggestions, based on local files from Windows, in only a few lines of code. Alternatively, you can register for the search box control's SuggestionsRequested event and build your own list of suggestions that is made up of suggestions you retrieved from another source (like a locally-defined list or a web service). This quickstart shows you how to handle the SuggestionsRequested event.

For code examples that show how to add search suggestions, download the SearchBox control sample. The sample demonstrates how to add search suggestions by using all three possible sources, and how to add suggestions for East Asian languages by using alternate forms of the query text generated by an Input Method Editor (IME). (We recommend using query text alternatives if your app will be used by Japanese or Chinese users.)

Handle the SuggestionsRequested event

  1. It's likely that your app will have multiple SearchBox controls; let's define a single static event handler in your SearchResultsPageExample.xaml.cs file that they can all use. Add this code after the Filter_Checked method.

            public async static void SearchBox_SuggestionsRequested(
                SearchBox sender, 
                SearchBoxSuggestionsRequestedEventArgs args)
            {
    
  2. If you want to respond to the SuggestionsRequested event asynchronously (and we do), you must obtain a SearchSuggestionsRequestDeferral object before you edit the suggestion list.

                // This object lets us edit the SearchSuggestionCollection asynchronously. 
                var deferral = args.Request.GetDeferral();
    
  3. The system automatically provides some search suggestions, such as previous searches the user performed. Let's add our search suggestions to whatever the system provides.

                try { 
    
                    // Retrieve the system-supplied suggestions.
                    var suggestions = args.Request.SearchSuggestionCollection;
    
  4. Iterate through each item in your data and check for matches. When we find a match, append the matching item's title to the search suggestions collection.

                    var groups = await SampleDataSource.GetGroupsAsync();
                    foreach (var group in groups)
                    {
                        var matchingItems = group.Items.Where(
                            item => item.Title.StartsWith(
                                args.QueryText, StringComparison.CurrentCultureIgnoreCase));
    
                        foreach (var item in matchingItems)
                        {
                            suggestions.AppendQuerySuggestion(item.Title);
                        }
                    }
    
    
  5. The SearchBoxSuggestionsRequestEventArgs.LinguisticDetails.QueryTextAlternatives property provides additional suggestions for users entering text in an IME. Using these suggestions improves the search experience for users of East Asian languages. Let's check the query text alternatives for strings that contain the original query and add them to our search suggestion list.

                    foreach (string alternative in args.LinguisticDetails.QueryTextAlternatives)
                    {
                        if (alternative.StartsWith(
                            args.QueryText, StringComparison.CurrentCultureIgnoreCase))
                        {
                            suggestions.AppendQuerySuggestion(alternative); 
                        }
                    }
                }
    
  6. Finally, use the SearchSuggestionsRequestDeferral to let the system know that we've finished editing the suggestions list.

                finally {
                    deferral.Complete();
                }
    
            }
    

    That's all the code we need for our search suggestion event handler. Here's the complete SearchBox_SuggestionsRequested method:

            public async static void SearchBox_SuggestionsRequested(
                SearchBox sender, 
                SearchBoxSuggestionsRequestedEventArgs args)
            {
    
                // This object lets us edit the SearchSuggestionCollection asynchronously. 
                var deferral = args.Request.GetDeferral();
    
                try { 
    
                    // Retrieve the system-supplied suggestions.
                    var suggestions = args.Request.SearchSuggestionCollection;
    
                    var groups = await SampleDataSource.GetGroupsAsync();
                    foreach (var group in groups)
                    {
                        var matchingItems = group.Items.Where(
                            item => item.Title.StartsWith(
                                args.QueryText, StringComparison.CurrentCultureIgnoreCase));
    
                        foreach (var item in matchingItems)
                        {
                            suggestions.AppendQuerySuggestion(item.Title);
                        }
                    }
    
                    foreach (string alternative in args.LinguisticDetails.QueryTextAlternatives)
                    {
                        if (alternative.StartsWith(
                            args.QueryText, StringComparison.CurrentCultureIgnoreCase))
                        {
                            suggestions.AppendQuerySuggestion(alternative); 
                        }
                    }
                }
                finally {
                    deferral.Complete();
                }
    
            }
    
  7. Now let's register the event with our SearchBox.

    Because this event handler is static, we can't set the event handler in XAML. Instead, open the code-behind file for the page that contains your SearchBox and use the page's constructor to register for the event.

            public HubPage()
            {
                this.InitializeComponent();
                this.navigationHelper = new NavigationHelper(this);
                this.navigationHelper.LoadState += navigationHelper_LoadState;
    
                // Register for the SuggestsRequested event.
                mySearchBox.SuggestionsRequested += SearchResultsPageExample.SearchBox_SuggestionsRequested;
    
            }
    

Implementing the Search contract (for previous versions of Windows)

Prior to Windows 8.1, apps used the Search charm to provide in-app search. Developers implemented the Search contract and used the SearchPane API to handle queries and obtain suggestions and results.

Although we continue to fully support the Windows 8 Search contract and the SearchPane API, as of Windows 8.1, we recommend using the SearchBox control instead of the SearchPane. Apps that use the SearchBox don't need to implement the Search contract.

Should an app ever use the SearchPane and Search contract? If you don't expect users to search your app very much, you can use the SearchPane and Search contract. We recommend that you use a button with the Search glyph (Segoe UI Symbol 0xE0094 at 15pt) in your app that users can click to activate the search pane. To see code that implements the SearchPane and the Search contract, see the Search contract sample.

Summary and next steps

You used the SearchBox control and the Search Results Page to add search to your app.

For guidelines to help you design and create a good search experience for your users, see Guidelines and checklist for search.

SearchBox control sample

Guidelines and checklist for search