Introduction
In this tip, we will discuss a quick way of how to locate a point in the Map and then add a customized pushpin to these locations through a sample XML.
Background
A while back, I was working for a Windows Phone Application to show location data on pushpins and the requirement was to show multiple locations coming from the web server. Unfortunately, there was no Web API to get XML file from server to achieve this kind of functionality. So in this tip, we will try to see a quick walk-through on how to locate a point in the Map and then add a customized pushpin to these locations through a sample XML.
Locate a point or address in the map and specify more information on that pinned point regardless of whether you zoom it or not. These pinned points are called pushpin.
These pushpins could be used to show more precise information of that location. For example, we have Google India, Bangalore location and for this, pushpin can be used to simply point location on the map or it can specify the address also when you tap on that pushpin.
Through this sample app, we will get to know how we can present the pushpins on the map having multiple pushpin locations. Also, we will try to customize the pushpin's design while tapping to view its content.
Using the Code
Let's have a look at the sample application to show multiple pushpins on Map control. I tried to implement this feature in the MVVM way, but the whole code is not written in that way. Since I haven't written code for Command Binding or I could have used PRISM for getting all these features within a single library, I'll try to make it work in a further version.
Let us proceed with creating a new Windows Phone project.
Now choose the OS version to Windows Phone OS 8.0.
To work with MVVM, I have added Model, View and ViewModel folders in the solution.
Now let's code for the start-up page of our app. Here is the XAML that is UI that we will see on the screen:
<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 Text="Sample Pushpin App"
Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="Google India" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Show Map"
Height="140" Click="Button_Click"></Button>
</Grid>
</Grid>
We have a single button here to switch to map view. Now on the button click, we are navigating to other page where we will see the Map view.
private void Button_Click(object sender, RoutedEventArgs e)
{
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(
new Uri("/View/MapView.xaml", UriKind.Relative));
}
Now before we start the coding ahead, you need to get the credentials for using the map in Windows Phone Application. To know how to get the credentials, see this link. If you don't get the credentials, then you can get irritated since a white strip keeps appearing over the map urging you to get the credential for using the map control.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="PinItemTemplate">
<local:Pushpin Background="Red"
Location="{Binding Location}" Tap="PushpinTap">
<local:Pushpin.Content>
<StackPanel Name="PushpinStack" Visibility="Collapsed">
<TextBlock Text="{Binding ID}" Visibility="Collapsed" />
<Border Background="Black" HorizontalAlignment="Center" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ADDRESS}"
Grid.Row="0" Grid.Column="0" Margin="5,5,5,0"/>
<StackPanel Grid.Row="1"
Grid.Column="0" Orientation="Horizontal">
<TextBlock Text="{Binding CITY}" Margin="5,0,5,5" />
<TextBlock Text="{Binding STATE}" Margin="5,0,5,5" />
<TextBlock Text="{Binding ZIP}" Margin="5,0,5,5" />
</StackPanel>
</Grid>
</Border>
</StackPanel>
</local:Pushpin.Content>
</local:Pushpin>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<Grid x:Name="LayoutRoot">
<phone:Pivot>
<phone:PivotItem>
<phone:PivotItem.Header>
<TextBlock Text="Map Us"></TextBlock>
</phone:PivotItem.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Opacity="0.7">
<StackPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FF424242" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Background>
</StackPanel>
<local:Map Center="{Binding CurrentLocation}" Tap="map_Tap_1"
Name="map" Grid.Row="1" CopyrightVisibility="Collapsed"
LogoVisibility="Collapsed" Foreground="Red"
ZoomLevel="7"
CredentialsProvider="type your credential here"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<local:MapItemsControl x:Name="MapPins"
ItemsSource="{Binding GetLocationData}"
ItemTemplate="{StaticResource PinItemTemplate}"
/>
</local:Map>
</Grid>
</phone:PivotItem>
</phone:Pivot>
</Grid>
This function is useful when you need to perform more specific operation on particular pushpin.
I have written this function to get on which pushpin user has tapped. Since we have 5 pushpins here and I need to find that particular pushpin on which user has tapped, I wanted some more operations while tapping on the pushpin.
private T GetChild<T>
(DependencyObject obj, int selectedindex) where T : DependencyObject
{
DependencyObject child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child.GetType() == typeof(T))
{
if (i == selectedindex)
break;
}
else if (child != null)
{
child = GetChild<T>(child, selectedindex);
if (child != null && child.GetType() == typeof(T))
{
if (i == selectedindex)
break;
}
}
}
return child as T;
}
You can find a function in the Helper folder for parsing the XML of type T
which actually will parse our location.xml and map it to our model.
public static T Deserialize<T>(string xml)
{
try
{
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(xml)))
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
XDocument document = XDocument.Parse(xml);
T theObject = (T)serializer.Deserialize(document.CreateReader());
return theObject;
}
}
catch (Exception ex)
{
throw ex;
}
}
While tapping on the pushpin, user will be able to view the content of the pushpin. You can have more customized designs to this pushpin popups.
private void PushpinTap(object sender, GestureEventArgs e)
{
Pushpin pushpin = sender as Pushpin;
if (pushpin.Content != null)
{
StackPanel content = (StackPanel)pushpin.Content;
if (content.Visibility == Visibility.Collapsed)
{
content.Visibility = Visibility.Visible;
}
}
e.Handled = true;
}
The map tap function will actually collapse all the open pushpins showing content.
private void map_Tap_1(object sender, GestureEventArgs e)
{
int count = MapPins.Items.Count;
for (int i = 0; i <= count; i++)
{
DependencyObject obj = GetChild<Pushpin>(MapPins, i);
if (obj != null)
{
Pushpin pin = obj as Pushpin;
StackPanel sb = pin.FindName("PushpinStack") as StackPanel;
if (sb != null)
{
sb.Visibility = System.Windows.Visibility.Collapsed;
}
}
}
}
Points of Interest
We have seen here how we can implement multiple pushpins from the XML file.
People can also have this XML file coming from the server if they are calling some sort of Web API and they can download this XML using some Web Client and parse it to represent on the UI.
I hope this tip would be informative enough to explain dynamic pushpin implementation on Maps and how we can customized the pushpin view.
Note: Sample code for this article has been written in VS 2012 and I have used Windows Phone SDK 8.0 which could be installed on Windows 8 operating system. So to run this code, you must have Visual Studio 2012 and SDK 8.0 for Windows Phone.
History
- 03-October-2013: First version