Introduction
For the general introduction of the app, its basically a utility app which consists of the likes of a search tool, a geocode related tool and a translator tool. I have opted to showcase this app as an entry for the AppInnovation Contest.
The users will have access to search, geocode browsing and a language translator all in one application. One will have the ease to search the web, find their current location and query nearby locations, inter translate different languages. the app will be like a companion to the user serving him with today's basic and most required needs of search, geocode browsing and translate.
Background
The app is basically powered by Microsoft Technologies and APIs offered by Microsoft. Moreover the app depends on the user's windows live account key as a prerequisite for access to the search, geocode and translator which the user would have to subscribe from Windows Azure market place at first (free or paid APIs based upon usage and requirement), for more details visit: http://datamarket.azure.com/.
Initially, I had conceived the idea to develop this app for Windows Phone 7.1 and I am almost halfway through, but with Ultra books around, I have started transforming the same for the Windows 8 platform too.
The Walk Through
Below explained details are currently from my Windows phone app project and as I proceed with the explanation of the app, I will be listing out different modules that I would currently discuss. One can find tutorials on using search, geocode and translator APIs scattered throughout the web. I have used Bing APIs offered by Microsoft for my project. The modules are as follows:
1. Search Module
2. Geocode Module
3. Translator Module
A little bit of GUI
Before proceeding into the modules I would discuss a little bit of GUI part for the app. With the advent of XAML, designing the UI has become a lot easier and fruitful with much less effort. Find the link for tutorial on XAML : http://msdn.microsoft.com/en-us/library/ms752059.aspx Here is my GUI code for one of the pages.
HomePage.xaml
<phone:PhoneApplicationPage
x:Class="MyPhoneApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="123"/>
<RowDefinition Height="645"/>
</Grid.RowDefinitions>
-->
<StackPanel x:Name="TitlePanel" Margin="0,17,0,644" Grid.RowSpan="2">
<TextBlock x:Name="ApplicationTitle" Text="Bing Factory!"
FontSize="30" Style="{StaticResource PhoneTextNormalStyle}" Width="467" />
<TextBlock x:Name="PageTitle" Text="Login" Margin="9,-7,0,0"
FontSize="50" Style="{StaticResource PhoneTextTitle1Style}"
Height="74" Width="469" />
</StackPanel>
-->
<Grid x:Name="ContentPanel" Margin="0,0,0,34" Grid.Row="1">
<Grid RenderTransformOrigin="0.86,0.64" Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="154*"/>
<ColumnDefinition Width="154*"/>
<ColumnDefinition Width="154*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="198*"/>
<RowDefinition Height="198*"/>
<RowDefinition Height="198*"/>
</Grid.RowDefinitions>
<Image x:Name="img_Maps" Margin="8,29,8,30"
Source="Images/MapsLogo.png" Stretch="Fill"
Grid.Column="1" Tap="img_Maps_Tap" />
<Image x:Name="img_Search" Margin="8,29,8,30"
Source="Images/SearchLogo.png" Stretch="Fill" Tap="img_Search_Tap" />
<Image x:Name="img_Translator" Grid.Column="2"
Margin="8,29,8,30" Source="Images/TranslatorLogo.png"
Stretch="Fill" Tap="img_Translator_Tap" />
</Grid>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Search.xaml
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
x:Class="MyPhoneApp.WebSearch"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="PortraitOrLandscape" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480">
-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="The Companion"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="Search"
Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" >
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="71" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Height="72" HorizontalAlignment="Center" Margin="1,8,0,0"
x:Name="txt_SearchText" VerticalAlignment="Top" Width="455" VerticalContentAlignment="Top" />
<Button x:Name="btn_Search" Content="Search" Margin="171,79,159,0"
VerticalAlignment="Top" HorizontalAlignment="Center" Grid.RowSpan="2" Click="btn_Search_Click" />
<ScrollViewer x:Name="ContentScroll" Grid.Row="2"
HorizontalScrollBarVisibility="Auto" FontSize="18.667">
<toolkit:WrapPanel x:Name="WrapPanel_Type" Height="72" Margin="1,14,0,0"
Grid.Row="2" Width="364" VerticalAlignment="Top" HorizontalAlignment="Center" >
<CheckBox x:Name="chk_Web" Content="Web" HorizontalAlignment="Center"
FontSize="18.667" Margin="0,0,0,-2" VerticalAlignment="Center" Checked="chk_Web_Checked" />
<CheckBox x:Name="chk_Images" Content="Images" Width="134"
HorizontalAlignment="Center" FontSize="18.667" Margin="4,0,0,-2"
VerticalAlignment="Center" Checked="chk_Images_Checked" />
<CheckBox x:Name="chk_Videos" Content="Video" Width="117"
HorizontalAlignment="Center" FontSize="18.667" Margin="250,-70,0,0"
Height="72" VerticalAlignment="Center" RenderTransformOrigin="0.752,0.486"
HorizontalContentAlignment="Center" Checked="chk_Videos_Checked" />
</toolkit:WrapPanel>
</ScrollViewer>
<ScrollViewer x:Name="ScrollViewerResult" Margin="11,16,5,0" Grid.Row="3">
<ListBox x:Name="ListBox_Result" Height="382" HorizontalAlignment="Left"
Margin="1,1,0,0" VerticalAlignment="Top" Width="Auto" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Width="Auto" Height="Auto" >
<TextBlock Style="{StaticResource PhoneTextNormalStyle}"
Text="{Binding Title}" TextWrapping="Wrap"/>
<TextBlock Style="{StaticResource PhoneTextNormalStyle}"
Text="{Binding Description}" TextWrapping="Wrap" />
<TextBlock Style="{StaticResource PhoneTextNormalStyle}"
Text="{Binding DisplayUrl}" TextWrapping="Wrap" />
<TextBlock Style="{StaticResource PhoneTextNormalStyle}"
Text="{Binding Url}" TextWrapping="Wrap" Tap="Link_Tap" />
<Image Source="{Binding MediaUrl}" MaxHeight="40" MaxWidth="40"></Image>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Regarding xaml code, I would like to add a point here that to make the UI much interactive, xaml provides transitions effects that would make the app look cool as the user navigates from one page to other. For transitions to take effect in a silverlight app, one has to initialize the rootframe from transitionframe rather from phoneapplicationframe in the InitializePhoneApplication method of app.xaml file. Then one can override the methods which are OnNavigatedTo and OnNavigatedFrom in the called pages for the transition to take place. For more details of transitions refer tutorial: http://www.codeproject.com/Articles/345129/Windows-Phone-7-Navigation-Transitions. There is much to explore regarding designing and effects using xaml code. I would say the more one explores, the more he gets surprised.
Registering APIs
Now lets move on to some coding stuff. But before we actually start coding, we have to make sure that we have access to APIs that we would be using in our app. In order to use the bing APIs for search, maps and translator, it is required that one must have a windows live account. The live account is then used to register in Windows Azure Market Place for subscribing bing search and microsoft translator APIs. Once done with registration, go to My Account->Account Keys. Here you will find a default account key provided intially. You can also generate a new account key and can use it for authenticating with APIs as you would do using a default key.
For using bing maps API, go to https://www.bingmapsportal.com/ and register there with Windows Live Account. After registration, go to create or view keys and generate a key for your app. Now this key can be used to access bing maps API.
NOTE: We are done with registering APIs. As noticed for accessing the APIs we needed account key (for search and translator) and map key (for maps). In case of search and translator APIs they are limitedly free as compared to bing maps API. So I would make the user to configure their own account key in the application to allow them to access their own registered APIs(for search and translator) through the app and make the search and translator to work.
The Search
As registration for accessing bing search is complete, we can download the .NET Class Library (BingSearchContainer.cs) from Windows Azure Market place by visiting My Account->My Data->Bing search API page and include it in the project. The class library has inbuilt methods and setting variables for building the query for search pertaining to categories like web, images, video etc.
From the same location My Account->My Data->Bing search API page, navigate to the "Explore this Dataset" link. Here the Search API url will be found as "Service root URL". Copy the same and use it in the project to execute the search queries. Few example codes are listed below for the reference.
Private void btn_Search_Click(object sender, RoutedEventArgs e)
{
try
{
var bingContainer = new BingSearchContainer(new Uri(
"https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1"));
var accountKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
if (chk_Web.IsChecked.Value)
{
var webQuery = bingContainer.Web(txt_SearchText.Text.Trim(),
"EnableHighlighting", "DisableQueryAlterations",
"en-IN", null, null, null, null);
webQuery.BeginExecute(new AsyncCallback(this.WebResultLoadedCallback), webQuery);
}
else if (chk_Images.IsChecked.Value)
{
var imageQuery = bingContainer.Image(txt_SearchText.Text.Trim(),
"EnableHighlighting","en-IN", null, null, null, string.Empty);
imageQuery.BeginExecute(new AsyncCallback(this.ImageResultLoadedCallback), imageQuery);
}
else if (chk_Videos.IsChecked.Value)
{
}
else
{
}
}
catch (Exception ex)
{ }
}
In the AsyncCallback method use the results fetched from the service API to bind the search result to any of the data container like grid, list view for displaying the search result.
NOTE: For using the BingSearchContainer.cs class, it is required to add a reference to System.Data.Services.Client dll in the project. This dll is required to request to an Open Data Protocol (OData) service(Bing API service).For more details on oData protocol refer: http://www.codeproject.com/Articles/90365/An-overview-of-Open-Data-Protocol-OData.
The Geocode/Maps
Bing maps can be used for development in two ways. Either we can use Bing Map Task or Bing Maps API. Incase of bing map task, we call the bing maps installed in the device whereas for Map control we embed the map in our application (Embeding a map in the application consumes more memory). Bing maps API can be used in scenarios where we have to customize the app as per our requirement.
Bing Maps Task:
Launching map using map tasks provides us with the option to load the installed map app with default center location or a search term and zoom level. Once loaded, we have access to various features like, current position, navigation, search etc. offered by the default installed map app.
bingMapsTask.Center = new GeoCoordinate(<Latitiude>, <Longitude>);
bingMapsTask.ZoomLevel = 2;
bingMapsTask.Show();
bingMapsTask.SearchTerm="search term" ;
bingMapsTask.ZoomLevel = 2;
bingMapsTask.Show();
Bing Maps API:
Using bing maps API requires embedding the map control provided in the tool box of visual studio. Put the maps control inside a layout and provide the Key that was generated during registration for bing maps api in the CredentialsProperty of the map control.
<my:Map Height="309" HorizontalAlignment="Left" Margin="115,52,0,0"
Name="map1" CredentialsProvider="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
VerticalAlignment="Top" Width="174" x:Name:Map/>
The location service is used to get the location for current position using the GeoCoordinateWatcher. GeocordiateWatcher can be used to update the position of the user whenever the geocode position changes. An example for using the Geocordinate watcher to fetch the current position and also when the current position changes is given below:
Private void GetCurrentPosition(object sender, RoutedEventArgs e) { if (watcher == null)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
{
MovementThreshold = 10
};
watcher.PositionChanged += new
EventHandler<GeoPositionChangedEventArgs
<GeoCoordinate>>(watcher_PositionChanged);
watcher.StatusChanged += new
EventHandler<GeoPositionStatusChangedEventArgs>
(watcher_StatusChanged);
watcher.Start();
}
protected void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
break;
case GeoPositionStatus.Initializing:
case GeoPositionStatus.NoData:
break;
case GeoPositionStatus.Ready:
break;
}
}
protected void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if(!e.Position.Location.IsUnknown)
{
this.map.Center = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
if (this.map.Children.Count != 0)
{
var pushpin = map.Children.FirstOrDefault(p => (p.GetType() ==
typeof(Pushpin) && ((Pushpin)p).Tag == "locationPushpin"));
if (pushpin != null)
{ this.map.Children.Remove(pushpin); }
}
Pushpin locationPushpin = new Pushpin();
locationPushpin.Tag = "You are here";
locationPushpin.Location = watcher.Position.Location;
this.map.Children.Add(locationPushpin);
this.map.SetView(watcher.Position.Location, 12.0);
}
}
One can also query a search location. In that case it is required to build a location search query in the form of rest service request, get the response and finally process the response. Either XML or JASON can be used for this request and response session with the map API rest service. Here's a very good example for creation of location query and processing response in XML: http://msdn.microsoft.com/en-us/library/hh534080.aspx.
string UrlRequest = "http://dev.virtualearth.net/REST/v1/Locations/" +
queryString +
"?output=xml" +
" &key=" + BingMapsKey;
After deserialization of the response, get the current latitude and longitude position and use it to set the new location on map and mark pushpin on it.
Note: For using the map control a reference to System.Devices.Location dll is required and to use the pushpin class
a reference to Microsoft.Phone.Controls.Maps DLL is required MSDN.
The Translator
For language translation download the .NET Class Library (TranslatorContainer.cs) from Windows Azure Market place by visiting My Account->My
Data->Microsoft Translator API page and include it in the project. The class library has inbuilt methods and setting variables for building the translation queries.
From the same location My Account->My Data->Microsoft Translator API page, navigate to the "Explore this Dataset" link. Here the Translator
API url will be found as "Service root URL". Copy the same and use it in the project to execute the translation. Translator API provides two services,
one is to translate an input text to a particular language and the other is to detect the language of an input text. Below is listed an example code for translation:
private void button1_Click(object sender, RoutedEventArgs e)
{
TranslatorContainer translatorContainer = new TranslatorContainer(
new Uri("https://api.datamarket.azure.com/Bing/MicrosoftTranslator/v1"));
var accountKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
translatorContainer.Credentials = new NetworkCredential(accountKey, accountKey);
var translationQuery = translatorContainer.Translate(
"Translate this string", targetLanguage.Code, sourceLanguage.Code);
var translationResults = translationQuery.BeginExecute(
new AsyncCallback(this.TranslateResultLoadedCallback), translationQuery);
}
In the AsyncCallback method convert the results fetched to a list of type Translation and collect only the first member of the list for the resulting translation.
Do remember to convert the result into respective language culture for displaying in the windows phone. For details on languages supported by Windows
Phone refer: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh202918(v=vs.92).aspx.
var translationResults = new List<Translation>();
translationResults = result.ToList<Translation>();
string translatedText = translationResults.First().ToString(new CultureInfo(translationCulture));
Note: For using the TranslatorContainer.cs class, it is required to add a reference to System.Data.Services.Client.dll in the project.
Points of Interest
Working with APIs seems always interesting and with new OS platforms around it becomes imperative for one to develop new or transform older apps to support the latest environment.
I had planned to include speech recognition to implement voice commands in my app, but I was restricted to do so as windows phone or metro apps development environment doesnt
have any speech recognition engine as supported by .NET (System.Speech.Recognition). But still there is a way where we can develop our own service using the .NET references
and host it locally in the IIS of the system where the application gets installed. In that case speech recognition can work with ease even on Metro Apps as we have
to just make call to that service to get identified text from an audio input. Hopefully will be updating the article with this fix as soon as I get this idea working in my testing environment.
History
First posted on 14 Oct 2012. The Companion App.