Implement Search Contract the easy way #win8dev

Most apps contain some sort of data that you as a developer would like exposed. That’s were the Search Contract comes into play. Say the user is on the start screen and wishes to search your app, they could just start typing and select your app from the list. If you implemented Search contract, your app will be listed in the app list and the user could just type away and hit enter to see results. That would be nice wouldn’t it ?

Lets get on with it and add search contract in a simple / easy way.
Right click on the Win8 project and select Search Contract. Name it whatever you wish and hit enter.

This does two things:

  • Adds override for OnSearchActivated in your App.xaml.cs
  • Adds required page for search and display of results.

This is what the OnSearchActivated looks like

protected async override void OnSearchActivated(Windows.ApplicationModel.Activation.SearchActivatedEventArgs args)
{
    // TODO: Register the Windows.ApplicationModel.Search.SearchPane.GetForCurrentView().QuerySubmitted

    // event in OnWindowCreated to speed up searches once the application is already running

    // If the Window isn't already using Frame navigation, insert our own Frame
    var previousContent = Window.Current.Content;
    var frame = previousContent as Frame;

    // If the app does not contain a top-level frame, it is possible that this 
    // is the initial launch of the app. Typically this method and OnLaunched 
    // in App.xaml.cs can call a common method.
    if (frame == null)
    {
        // Create a Frame to act as the navigation context and associate it with
        // a SuspensionManager key
        frame = new Frame();
        Cineworld.Common.SuspensionManager.RegisterFrame(frame, "AppFrame");

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            // Restore the saved session state only when appropriate
            try
            {
                await Cineworld.Common.SuspensionManager.RestoreAsync();
            }
            catch //(Cineworld.Common.SuspensionManagerException)
            {
                //Something went wrong restoring state.
                //Assume there is no state and continue
            }
        }
    }

    frame.Navigate(typeof(SearchResults), args.QueryText);
    Window.Current.Content = frame;

    // Ensure the current window is active
    Window.Current.Activate();
}

as you can see it is passing the search query to the Search Results page while navigating.
Now lets look at the code-behind in SearchResults.xaml.
FYI, I am loading my dataset if required in the OnNavigatedTo method. This page is a simple results display page. It contains GridView / ListView to display results. Lets look at the LoadState method

protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
    var queryText = navigationParameter as String;

    List<SearchResult> matches = new List<SearchResult>();

    foreach (var f in App.Films.Values)
    {
        IEnumerable<CastInfo> casts = from cast in f.FilmCast
                                        where cast.Name.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                        select cast;

        foreach (var c in casts)
        {
            //searchCinemas.Add(c.Name, c);
            matches.Add(new SearchResult() { Name = c.Name, Subtitle = String.Format("{0} in {1}", c.Character, f.Title), SearchObject = f, Image = c.ProfilePath });
        }
    }

    IEnumerable<FilmInfo> films = from film in App.Films.Values
                                    where film.Title.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                    select film;

    searchFilms.Clear();
    searchCinemas.Clear();

    foreach (var f in films)
    {
        searchFilms.Add(f.Title, f);
        matches.Add(new SearchResult() { Name = f.Title, Image = f.PosterUrl, SearchObject = f });
    }

    IEnumerable<CinemaInfo> cinemas = from cinema in App.Cinemas.Values
                                        where cinema.Name.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)
                                        select cinema;

    foreach (var c in cinemas)
    {
        searchCinemas.Add(c.Name, c);
        matches.Add(new SearchResult() { Name = c.Name, SearchObject = c, Image = new Uri("ms-appx:///Assets/Background.png") });
    }
            
    // TODO: Application-specific searching logic.  The search process is responsible for
    //       creating a list of user-selectable result categories:
    //
    //       filterList.Add(new Filter("<filter name>", <result count>));
    //
    //       Only the first filter, typically "All", should pass true as a third argument in
    //       order to start in an active state.  Results for the active filter are provided
    //       in Filter_SelectionChanged below.

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

    bAllowNav = false;

    this.DefaultViewModel["Results"] = matches;

    bAllowNav = true;

    var filterList = new List<Filter>();
    filterList.Add(new Filter("All", 0, true));

    //this.DefaultViewModel["Filters"] = filterList;
    //this.DefaultViewModel["ShowFilters"] = filterList.Count > 1;
}

I added code to search my dataset and to add results to DefaultViewModel. I don’t use MVVM but the default code implemented basic binding etc and I thought I’d leave it at that. I accumulate the results and save them and let bindings do the rest. Let me show you how it looks
Say I got to start screen, type I ben and select my app, this is what I get.

Now you have a nice list of matched entries. Now suppose I wanted to let users click on a matched entry and to navigate them to details page ? I implemented GridView ItemClick event (SelectionChanged doesn’t work for some reason)

private void resultsGridView_ItemClick(object sender, ItemClickEventArgs e)
{
    if (e.ClickedItem != null && bAllowNav)
    {
        SearchResult res = (SearchResult)e.ClickedItem;
        if (res.SearchObject is FilmInfo)
        {
            FilmDetails.SelectedFilm = (res.SearchObject as FilmInfo);
            this.Frame.Navigate(typeof(FilmDetails));
        }
        else if (res.SearchObject is CinemaInfo)
        {
            CinemaDetails.SelectedCinema = (res.SearchObject as CinemaInfo);
            this.Frame.Navigate(typeof(CinemaDetails));
        }
    }
}

Hope this post is helpful to others looking to implement the search contract. Happy coding!

One thought on “Implement Search Contract the easy way #win8dev

  1. Pingback: Implement Search Contract the easy way #win8dev

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s