Estimated Time: 45 Minutes
Silverlight has enjoyed continued success branching out from simply a browser plug-in, to the desktop, and now it’s the platform of choice for Windows Phone 7 development. This lab is designed to guide developers through the process of developing a Silverlight application for the Windows Phone.
In the lab you'll create a package tracking application. Along the way you'll learn how to navigate between pages, work with the Maps control, and connect to a WCF service from a Windows Phone project.
You'll start by creating three screens the main screen, a details screen, and a map screen. Next, you’ll connect to a WCF service. Finally, you’ll learn how to set a custom icon for your application. The Windows Phone application that you'll create is shown next:
If you already know how to create a new project, open the starting solution and add a new project instead, and move to step 2
If you don’t see Silverlight for Windows Phone in the Installed Templates list, Make sure: The tools installed correctly, you can download them from: http://www.silverlight.net/getstarted/devices/windows-phone/ You have selected Visual C# instead of Visual Basic. Currently VB is not supported on the Windows Phone 7 platform.
Add controls for searching
<Grid x:Name="LayoutRoot" Background="White">
<Grid Margin="12,70,12,12" d:LayoutOverrides="Width">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="331"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="ResultsListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="3,3,3,8" d:DesignWidth="349.5" d:DesignHeight="59">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ActivityDate, FallbackValue=6/10/1999}" d:LayoutOverrides="GridBox" Grid.Column="1" Margin="3" TextWrapping="Wrap"/>
<TextBlock Text="{Binding ActivityTime, FallbackValue=12:00 PM}" Margin="3" d:LayoutOverrides="GridBox" Grid.Column="2" TextWrapping="Wrap" />
<TextBlock Text="{Binding StatusDescription, FallbackValue=DELIVERED}" Margin="3" d:LayoutOverrides="GridBox" Grid.Column="3" TextWrapping="Wrap"/>
<toolkit:WrapPanel Margin="3">
<TextBlock Text="{Binding City, FallbackValue=ANYWHERE}" Margin="0" TextWrapping="Wrap" />
<TextBlock Text=", " Margin="0" TextWrapping="Wrap" />
<TextBlock Text="{Binding CountryCode, FallbackValue=US}" Margin="0" TextWrapping="Wrap" />
<TextBlock Text=", "Margin="0" TextWrapping="Wrap" />
<TextBlock Text="{Binding State, FallbackValue=GA}" Margin="0" TextWrapping="Wrap"/>
</toolkit:WrapPanel>
</Grid>
You can delete everything inside LayoutRoot to simplify the Xaml for this sample
The details Grid contains TextBlocks for labels and for showing the results, and the ListBox, will contain the historical information about where the package has been.
Once again, to simplify the Xaml, you can delete everything in the LayoutRoot by default
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="0.293*"/>
<RowDefinition Height="0.707*"/>
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Margin="0">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.492,-0.338" RadiusX="0.915" RadiusY="0.827" Center="0.49,0.338">
<GradientStop Color="#FF4E4E4E" Offset="0"/>
<GradientStop Color="#FF1D1D1D" Offset="1"/>
</RadialGradientBrush>
</Border.Background>
</Border>
<Grid x:Name="LatestStatusGrid" Margin="12,8,12,59">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.296*"/>
<ColumnDefinition Width="0.704*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Status" d:LayoutOverrides="Height" VerticalAlignment="Top" Margin="2"/>
<TextBlock TextWrapping="Wrap" Text="Last updated" d:LayoutOverrides="Width, Height" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2"/>
<TextBlock TextWrapping="Wrap" Text="Location" d:LayoutOverrides="Width" Grid.Row="2" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="2"/>
<TextBlock TextWrapping="Wrap" Text="Tracking #" d:LayoutOverrides="Width, Height" Grid.Row="3" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2"/>
<TextBlock TextWrapping="Wrap" Text="{Binding StatusDescription}" Grid.Column="1" d:LayoutOverrides="Height" Margin="2" FontWeight="Bold"/>
<TextBlock TextWrapping="Wrap" Text="{Binding ActivityDate}" Grid.Column="1" Grid.Row="1" d:LayoutOverrides="Height" Margin="2" FontWeight="Bold"/>
<StackPanel d:LayoutOverrides="Width" Grid.Row="2" Grid.Column="1" Margin="0" Orientation="Horizontal">
<TextBlock Text="{Binding City}" TextWrapping="Wrap" VerticalAlignment="Top" Margin="5,0,4,0" FontWeight="Bold" />
<TextBlock Text="{Binding State}" Margin="0,0,4,0" TextWrapping="Wrap" HorizontalAlignment="Left" />
<TextBlock Text="{Binding CountryCode}" Margin="5,0,0,0" TextWrapping="Wrap" HorizontalAlignment="Left" d:LayoutOverrides="HorizontalMargin" />
</StackPanel>
<TextBlock TextWrapping="Wrap" Text="{Binding TrackingNumber}" Grid.Column="1" Grid.Row="3" d:LayoutOverrides="Height" Margin="2" FontWeight="Bold"/>
</Grid>
<Button x:Name="MapButton" Content="MAP THIS" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,0,-5"/>
<ListBox x:Name="PackageHistoryList" Grid.Row="1"></ListBox>
</Grid>
Notice that we used Binding to bind all the TextBlocks to the Properties of a TrackingActivity class from the dotNetShipping project since later on, this page will always be bound to a certain TrackingActivity object
When creating the url a query string value that contains the tracking number is passed in. The results page will consume this value and then query for results.
private void TrackButton_Click(object sender, RoutedEventArgs e)
{
// Pass the tracking number to the next page
Uri theUri = new Uri("/PackageDetails.xaml?trackingNumber=" + TrackingNumber.Text, UriKind.Relative);
NavigationService.Navigate(theUri);
}
private void PackageDetails_Loaded(object sender, RoutedEventArgs e)
{
if (NavigationContext.QueryString.ContainsKey("trackingNumber"))
{
var trackingNumber = NavigationContext.QueryString["trackingNumber"];
}
}
Add a service reference
Notice if the service doesn’t add properly and isn’t recognized as a namespace in your project, you can try to remove the service reference, clean the solution ,close Visual Studio and try to add the Service Reference again
var client = new Service1Client(); client.GetTrackingActivityAsync(trackingNumber); client.GetTrackingActivityCompleted += client_GetTrackingActivityCompleted;
Service1Client is located in the PackageTracker.PackageTrackingService namespace There are a couple of things to Notice the if statement contains a check to see if there is a parameter in the navigation query string: NavigationContext.QueryString.ContainsKey("trackingNumber") If the tracking number exists, then a new ServiceClient is created, the value is sent, and finally the event handler for the completed event is registered.
public static Shipment CurrentPackage { get; set; }
void client_GetTrackingActivityCompleted(object sender, GetTrackingActivityCompletedEventArgs e)
{
if (e.Error == null)
{
App.CurrentPackage = e.Result;
var sortedList = e.Result.TrackingActivities.OrderBy((a) =>
a.ActivityDate).Reverse();
if (sortedList.Count() == 0) return;
this.PackageHistoryList.ItemsSource = e.Result.TrackingActivities;
LatestStatusGrid.DataContext = sortedList.Last();
}
}
xmlns:Microsoft_Phone_Controls_Maps="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
<Grid x:Name="LayoutRoot" Background="Transparent">
<Microsoft_Phone_Controls_Maps:Map x:Name="Map" ZoomLevel="4" LogoVisibility="Collapsed" ZoomBarVisibility="Visible"/>
</Grid>
private void MapButton_Click(object sender, RoutedEventArgs e)
{
Uri theUri = new Uri("/DetailsMap.xaml", UriKind.Relative);
NavigationService.Navigate(theUri);
}
Since the map is going to display locations where the package has been, we’ll need one more web service reference that geocodes the locations returned from the package tracking service. There are many out there, but in this example we’ll be using Bing Maps Geocoding service, http://www.codeproject.com/KB/IP/BingMapsWebServiceExample.aspx.
private void DetailsMap_Loaded(object sender, RoutedEventArgs e)
{
string key = "YOUR KEY";
// Configure the request object
GeocodeRequest request = new GeocodeRequest();
request.Credentials = new UPSTracking.WP7.GeocodeService.Credentials();
request.Credentials.ApplicationId = key;
// Initialize the client
GeocodeService.GeocodeServiceClient client = newGeocodeServiceClient("BasicHttpBinding_IGeocodeService");
client.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(client_GeocodeCompleted);
// loop through each activity and make a request
foreach (var activity in App.CurrentPackage.TrackingActivities)
{
request.Query = string.Format("{0} {1} {2}", activity.City, activity.State, activity.CountryCode);
client.GeocodeAsync(request);
}
}
For this sample, your Bing Maps Key is: AsrtnIJN_DSjg1772uR7pUmW6gRNL5gdXwcaZkNo_qIYG0h2YXdg7ShFPpUht342
void client_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
if (e.Error == null)
{
// The result is a GeocodeResponse object
GeocodeResponse geocodeResponse = e.Result;
// Get the latitude and longitude for the first item in the results
var latitude = geocodeResponse.Results[0].Locations[0].Latitude;
var longitude = geocodeResponse.Results[0].Locations[0].Longitude;
var altitude = geocodeResponse.Results[0].Locations[0].Altitude;
// Create a new pushpin
Pushpin pin = new Pushpin()
{
Content = e.Result.Results[0].Address.FormattedAddress.ToString(),
Location = new System.Device.Location.GeoCoordinate(latitude,longitude,altitude) };
// Add the pushpin to the Map
this.Map.Children.Add(pin);
this.Map.Center = pin.Location;
this.Map.ZoomLevel = 5d;
}
}
using UPSTracking.WP7.GeocodeService; using Microsoft.Phone.Controls.Maps;
The icon for your application will enable users to quickly identify your application. So it’s important to make sure you find or create a unique icon. In our case we will use the below icon.
In MainPage.xaml we have a list that represents our shipped items, but it is not bound to any data and doesn’t provide any functionality, so we want to make it display a list of shipments that we sent, as it loads.
using UPSTracking.WP7.PackageTrackingService;
...
private List<Shipment> MyShipments { get; set; }
// Pre-Populate the shipments in the C'tor
this.PopluateMyShipments();
private void PopluateMyShipments()
{
//Insert your logic instead of the code below
MyShipments = new List<Shipment>()
{
new Shipment(){TrackingNumber = "1Z12345E0291980793"},
new Shipment(){TrackingNumber = "1Z12345E6692804405"},
new Shipment(){TrackingNumber = "1Z12345E0390515214"},
new Shipment(){TrackingNumber = "1Z12345E1392654435"},
new Shipment(){TrackingNumber = "1Z12345E6892410845"},
};
//Insert your logic instead of the code above
//Bind the collection of packages we just filled to the UI
this.MyPackagesList.ItemsSource = MyShipments;
}
this.MyPackagesList.SelectionChanged += new SelectionChangedEventHandler(MyPackagesList_SelectionChanged);
void MyPackagesList_SelectionChanged(object sender,SelectionChangedEventArgs e)
{
if (MyPackagesList.SelectedItem != null)
{
var selectedShipment = (MyPackagesList.SelectedItem as Shipment);
if (selectedShipment != null)
{
Uri theUri = new Uri("/PackageDetails.xaml?trackingNumber=" +selectedShipment.TrackingNumber, UriKind.Relative);
NavigationService.Navigate(theUri);
}
}
}
As you might have noticed, the Shipments in our MainPage, and the TrackingActivities in out PackageDetails page show as a string representation of the class using a ToString, which makes no sense and looks less then appealing, we want to fix that.
<DataTemplate>
<StackPanel>
<TextBlock Margin="0" TextWrapping="Wrap" Text="{Binding TrackingNumber}" Style="{StaticResource PhoneTextLargeStyle}" Foreground="{StaticResource PhoneContrastForegroundBrush}"/>
<TextBlock TextWrapping="Wrap" Text="Status " VerticalAlignment="Top" Foreground="{StaticResource PhoneContrastForegroundBrush}" Visibility="Collapsed"/>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Top" Visibility="Collapsed">
<TextBlock TextWrapping="Wrap" Text="Tracking # " d:LayoutOverrides="Height" Style="{StaticResource PhoneTextSmallStyle}"
Margin="0,0,3,0" FontSize="13.333" Foreground="{StaticResource PhoneContrastForegroundBrush}" Opacity="0.565"/>
<TextBlock TextWrapping="Wrap" Text="{Binding TrackingNumber}" d:LayoutOverrides="Height" Style="{StaticResource PhoneTextSmallStyle}" Margin="0,0,3,0" FontSize="13.333"
Foreground="{StaticResource PhoneContrastForegroundBrush}" Opacity="0.565"/>
</StackPanel>
</StackPanel>
</DataTemplate>
We bound each field directly to the corresponding property name of the Shipment class, since we know the ListBox that we will apply it to, will always contain Shipment items.
<ListBox x:Name="MyPackagesList" Margin="0,199,0,0"d:LayoutOverrides="VerticalAlignment" Width="480">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Margin="0" TextWrapping="Wrap" Text="{Binding TrackingNumber}" Style="{StaticResource PhoneTextLargeStyle}" Foreground="{StaticResource PhoneContrastForegroundBrush}"/>
<TextBlock TextWrapping="Wrap" Text="Status " VerticalAlignment="Top" Foreground="{StaticResource PhoneContrastForegroundBrush}" Visibility="Collapsed"/>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Top" Visibility="Collapsed">
<TextBlock TextWrapping="Wrap" Text="Tracking # " d:LayoutOverrides="Height" Style="{StaticResource
PhoneTextSmallStyle}" Margin="0,0,3,0" FontSize="13.333" Foreground="{StaticResource PhoneContrastForegroundBrush}" Opacity="0.565"/>
<TextBlock TextWrapping="Wrap" Text="{Binding TrackingNumber}" d:LayoutOverrides="Height" Style="{StaticResource PhoneTextSmallStyle}" Margin="0,0,3,0" FontSize="13.333" Foreground="{StaticResource PhoneContrastForegroundBrush}" Opacity="0.565"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="PackageHistoryList" Grid.Row="1" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="3,3,3,8" d:DesignWidth="349.5" d:DesignHeight="59">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="{Binding City}" TextWrapping="Wrap" HorizontalAlignment="Left" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
<TextBlock Text=", " TextWrapping="Wrap" d:LayoutOverrides="Width" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
<TextBlock Text="{Binding CountryCode}" TextWrapping="Wrap" HorizontalAlignment="Left" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
<TextBlock Text=", " TextWrapping="Wrap" d:LayoutOverrides="Width" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
<TextBlock Text="{Binding State}" TextWrapping="Wrap" HorizontalAlignment="Left" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
</StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="{Binding ActivityTime}" TextWrapping="Wrap" VerticalAlignment="Top" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
<TextBlock Text="{Binding ActivityDate}" TextWrapping="Wrap" VerticalAlignment="Top" Foreground="{StaticResource PhoneContrastForegroundBrush}" />
</StackPanel>
<TextBlock Text="{Binding StatusDescription}" TextWrapping="Wrap" HorizontalAlignment="Left" Foreground="{StaticResource PhoneContrastForegroundBrush}" Margin="0"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you’ve already seen, now all of our UI shows appropriate information but it might be a bit hard to see, so we would like to change that, we could go to each TextBlock and modify it manually, but we can go even further and use a style, to give our UI a unified look across all our pages that we can store once and change on request.
<Application.Resources>
<Style x:Key="DetailsListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="LayoutRoot" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" Width="480">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentContainer"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderThickness="0,0,0,1" Margin="0" BorderBrush="#FF333333">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFF3F3F3" Offset="0"/>
<GradientStop Color="#FFDADADA" Offset="0.996"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Margin="15"/>
</Grid>
</ControlTemplate>
</Setter.Value>
/Setter>
</Style>
</Application.Resources>
This is a unified style that we created for all items that display details about items, we will use it both in MainPage.xaml and in PackageDetails.xaml
ItemContainerStyle="{StaticResource DetailsListBoxItemStyle}"
You can style the application further, the solution contains added styles for the 2 ListBoxes in MainPage.xaml and PackageDetails.xaml, but it’s purely esthetic.
Geocoding: http://www.codeproject.com/KB/IP/BingMapsWebServiceExample.aspx
Account Name - SchumanCorey140209
Bing Maps key - AsrtnIJN_DSjg1772uR7pUmW6gRNL5gdXwcaZkNo_qIYG0h2YXdg7ShFPpUht342
In this exercise you examined an existing ASP.NET application and supporting data access and service layers. You then migrated the existing functionality in the application to Silverlight and satisfied the following requirements:
Although the application created in this lab demonstrates how XAML and managed code can be used, other labs will provide additional details about application design practices that can be followed such as the Model-View-ViewModel (MVVM) pattern that you can use to build Silverlight applications.
Comments (0)