Geocoding With the Search Charm

In this blog post we are going to create a simple mapping application that allows the user to search for locations using the Search charm. To do this, there are three main tasks that have to be done:

  1. Create a basic mapping app
  2. Integrate with the Search Charm
  3. Add geocoding logic to the Search Charm

By making use of the Search charm, users can search within your app from anywhere in their system. The full source code in C# and VB for this blog post is available in the Visual Studio Galleries here.

Creating a Basic Mapping App

Before diving into adding logic for tying into the Search charm, let’s start off with creating a basic mapping application which will be used to display the results on.

  1. Start by opening Visual Studios 2012 and create a new project. In the window that opens, select Visual C# -> Windows Store. Select the Blank App template. Call the application GeocodingSearchCharm and press OK.GeocodingSearchCharmNewProject

  2. Add a reference to the Bing Maps SDK. To do this, right click on the References folder and press Add Reference. Select Windows -> Extensions select Bing Maps for C#, C++ and Visual Basic. If you do not see this option, ensure that you have installed the Bing Maps SDK for Windows Store apps. While you are here, also add a reference to the Microsoft Visual C++ Runtime Package as this is required by the Bing Maps SDK when developing using C# or Visual Basic.

    GeocodingSearchCharmReferenceManager 

  3. You may notice that there is a little yellow indicator on the references that you just added. The reason for this is that when using the C++ runtime package you have to set the Active solution platform in Visual Studio to one of the following options: ARM, x86 or x64. To do this, right click on the Solution folder and select Properties. Then go to Configuration Properties -> Configuration. Find your project and under the Platform column set the target platform. For this blog post, I’m going to select x86. Press Ok and the yellow indicator should disappear from our references.

    GeocodingSearchCharmConfigurationManager

  4. Now add a map to our application. To do this open the MainPage.xaml file. You will first need to add the Bing Maps SDK as a namespace at the top of the file. After you do this you can add a Map object to the Grid control and add your Bing Maps key to the credentials properties of the map. Give the map a name of MyMap.

     <Page
        x:Class="GeocodingSearchCharm.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:GeocodingSearchCharm"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:m="using:Bing.Maps"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <m:Map Name="MyMap" Credentials="YOUR_BING_MAPS_KEY"/>
        </Grid>
    </Page>
    
  5. At this point if you run the application, you will end up with a Bing map that takes up the whole screen and looks like this:

GeocodingSearchCharmMap

Integrating with the Search Charm

Now that you have a map to display search results on the next step is to tie into the Search charm.

  1. Add a Search contract to the project. To do this right click on the project and select Add -> New Item. Locate the Search contract item and call it SearchResultsPage1.xaml.

    GeocodingSearchCharmAddNewItem

  2. An alert will be displayed that asks if you would like to add some missing files to your project. Press Yes.

    GeocodingSearchCharmVisualStudio

  3. At this point you will notice a number of changes have been made to the project.

    • In the app manifest the Search contract is declared under the Declarations tab.
    • A basic search results page is created for your app. This results page adheres to the UX guidelines for results pages in Guidelines and checklist for search.
    • OnSearchActivated event handler is added to the App.xaml.cs file. This allows your application to respond to search queries, even when your app isn't on the screen.
    • A number of helper files are added under the Common folder.
  4. For this sample you don’t need the SearchCharmResults1.xaml. So delete this file from the project.

  5. Open the App.xaml.cs file and locate the OnSearchActivated event handler. Near the end of this event handler, find the following line of code:

     frame.Navigate(typeof(SearchCharmResults1), args.QueryText);
    

    Replace the SearchCharmResults1 value with MainPage. This will cause the main application to start when performing a search against it, even if the app isn’t loaded.

  6. Open the MainPage.xaml.cs file. In here you will want to get a reference to the search charm panel and attach a QuerySubmitted event when the user navigates to the app. You will also want to remove this event handler when the user navigates away from the app. This file should look like this:

 using Windows.ApplicationModel.Search;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace GeocodingSearchCharm
{
    public sealed partial class MainPage : Page
    {
        private SearchPane searchPane;

        public MainPage()
        {
            this.InitializeComponent();         
            
            //Get a reference to the Search charm 
            searchPane = SearchPane.GetForCurrentView();

            //Add watermark text to the search  textbox 
            searchPane.PlaceholderText = "Search for an  address, city or place.";
            searchPane.ShowOnKeyboardInput = true;
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e); 
            searchPane.QuerySubmitted += searchPane_QuerySubmitted;
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e); 
            searchPane.QuerySubmitted -= searchPane_QuerySubmitted;
        }

        private void searchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
        {
            //TODO: Add logic for geocoding the query  text. 
        }
    }
}

Adding the Geocoding Logic

At this point we have two of the three main tasks completed. The next step is to integrate in the geocoding logic by using the Bing Maps REST services.

  1. First we add the JSON data contracts for the Bing Maps REST services to your project. To do this, right click on your project and select Add -> New Item. Create a class called BingMapsRESTServices.cs. Open the file and delete the contents.

  2. Copy the C# JSON data contracts for the Bing Maps REST services from the MSDN documentation. Paste in the JSON data contracts into the BingMapsRESTServices.cs 

  3. In the MainPage.xaml.cs file add the following helper method for handing requests to the Bing Maps REST services.

     private async Task<Response> GetResponse(Uri uri)
    {
       System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
       var response = await client.GetAsync(uri);
        using (var stream = await response.Content.ReadAsStreamAsync())
       {
          DataContractJsonSerializer ser = new 
    DataContractJsonSerializer(typeof(Response));
          return ser.ReadObject(stream) as Response;
       }
    } 
    
  4. Next the logic for creating the geocoding request can be added to the searchPane_QuerySubmitted event handler. Once a request has been made, you can loop through the results and add them as pushpins on the map. Update the searchPane_QuerySubmitted event handler such that it looks like this:

 private async void searchPane_QuerySubmitted(SearchPane sender, SearchPaneQuerySubmittedEventArgs args)
{
   //Remove any existing search result items from the map. 
   MyMap.Children.Clear();        

   //Logic for geocoding the query text. 
   if (!string.IsNullOrWhiteSpace(args.QueryText))
   {
       Uri geocodeUri = new Uri(

       string.Format("https://dev.virtualearth.net/REST/v1/Locations?q={0}&c={1}&key={2}",
             Uri.EscapeUriString(args.QueryText),  args.Language, MyMap.Credentials));        

       //Get response from Bing Maps REST  services  
       Response r = await GetResponse(geocodeUri);        

       if (r != null &&
            r.ResourceSets != null &&
            r.ResourceSets.Length > 0  &&
            r.ResourceSets[0].Resources != null &&
            r.ResourceSets[0].Resources.Length  > 0)
       {
            LocationCollection locations = new LocationCollection();            
            int i = 1;            

            foreach (BingMapsRESTService.Common.JSON.Location l in r.ResourceSets[0].Resources)
            {
               //Get the location of each result 
               Bing.Maps.Location location = new Bing.Maps.Location(l.Point.Coordinates[0], l.Point.Coordinates[1]);              

              //Create a pushpin each location 
              Pushpin pin = new Pushpin()
              {
                  Tag = l.Name,
                  Text = i.ToString()
              }; 

              i++;

              //Add a tapped event that will display  the name of the location 
              pin.Tapped += (s, a) =>
              {
                 var p = s as Pushpin;
                 new MessageDialog(p.Tag as string).ShowAsync();
              };

              //Set the location of the pushpin 
              MapLayer.SetPosition(pin, location);

              //Add the pushpin to the map 
              MyMap.Children.Add(pin);

              //Add the coordinates of the location to a location  collection 
              locations.Add(location);
          }

          //Set the map view based on the location  collection 
          MyMap.SetView(new LocationRect(locations));
   }
   else 
   {
          await new MessageDialog("No results found.").ShowAsync();
   }
 } 
}

At this point the application is complete. The final step is to run and test the application. Press the Debug button to launch the application. Open up the Search charm from the right side panel and perform a search. Below is a screenshot of a search from London:

GeocodingSearchCharm