Clustering with the Bing Maps Silverlight Control – Part 2

Adding the Map and Extending the Navigation Control

Now that we have set up the database and created the web service we can move on to the Silverlight application. First we add the Bing Maps Silverlight control as reference to our Silverlight project:

image

Next we add a service reference pointing to the web service that we have created before. Click on discover to find the service and give it a name.

image

Particularly when we work with Windows Azure we must be able to modify the endpoint of the service dynamically and hence we add the following code to the public sub new method of the MainPage.xaml.vb

'Reset service reference endpoint (important when port or domain changes)
Dim wsURL As String = "http://" + HtmlPage.Document.DocumentUri.Host + ":" + _
HtmlPage.Document.DocumentUri.Port.ToString + "/svcPhotos.svc" svc.Endpoint.Address = New ServiceModel.EndpointAddress(wsURL)

In the MainPage.xaml we add the namespace for the Bing Maps Silverlight Control and the map itself.

image

We also add a mini-map that provides an overview. In this overview-map we remove all components that might obstruct the view on the map itself.

image

To add additional elements to the navigation control we add a handler right after we initialized the component:

Public Sub New()
    InitializeComponent()

    ' Add additional controls to the navigation bar
    AddHandler MyMap.MapForeground.TemplateApplied, AddressOf MapForeground_TemplateApplied

We are going to add 3 additional control elements

  • toggle mini map
  • toggle full-screen and
  • show/hide photos
#Region "Navigation"
    Private Sub MapForeground_TemplateApplied(ByVal sender As Object, ByVal e As EventArgs)
        AddHandler MyMap.MapForeground.NavigationBar.TemplateApplied, _
AddressOf NavigationBar_TemplateApplied End Sub Private Sub NavigationBar_TemplateApplied(ByVal sender As Object, ByVal e As EventArgs) Dim myNavCtrl As NavigationBar = MyMap.MapForeground.NavigationBar ' Add custom command buttons myNavCtrl.HorizontalPanel.Children.Add(New CommandSeparator()) Dim btnToggleMiniMap As New CommandToggleButton( _
New ToggleMiniMap(), "Mini-Map", "Toggle Mini-Map") myNavCtrl.HorizontalPanel.Children.Add(btnToggleMiniMap) myNavCtrl.HorizontalPanel.Children.Add(New CommandSeparator()) Dim btnPhotos As New CommandToggleButton( _
New TogglePhotos(), "Photos", "Show/Hide Photos") myNavCtrl.HorizontalPanel.Children.Add(btnPhotos) Dim imgFullScreen As New Image imgFullScreen.Source = New BitmapImage( _
New Uri("FullScreen.png", UriKind.RelativeOrAbsolute)) imgFullScreen.Width = 15 Dim btnFullScreen = New CommandButton( _
New ToggleFullscreen(), imgFullScreen, "Toggle Fullscreen") myNavCtrl.VerticalPanel.Children.Add(btnFullScreen) End Sub #End Region

The functions that are being executed when we click these control-elements are in separate classes and I have simply added them at the end of MainPage.xaml.vb

#Region "Additional Commands in Dashboard"

Class ToggleMiniMap
    Inherits NavigationBarCommandBase

    Public Sub New()
    End Sub

    Public Overloads Overrides Sub Execute(ByVal map As MapBase)
        Dim status As NavigationBarCommandStatus = Me.GetStatus(map)

        If status = NavigationBarCommandStatus.Checked Then
            MainPage.myMiniMap.Visibility = Visibility.Collapsed
        ElseIf status = NavigationBarCommandStatus.Normal Then
            MainPage.myMiniMap.Visibility = Visibility.Visible
        End If
    End Sub

    Public Overloads Overrides Function GetStatus(ByVal map As MapBase) _
As NavigationBarCommandStatus Dim status As NavigationBarCommandStatus = NavigationBarCommandStatus.Normal If MainPage.myMiniMap.Visibility = Visibility.Visible Then status = NavigationBarCommandStatus.Checked End If Return status End Function End Class Class TogglePhotos Inherits NavigationBarCommandBase Public Sub New() End Sub Public Overloads Overrides Sub Execute(ByVal map As MapBase) Dim status As NavigationBarCommandStatus = Me.GetStatus(map) If status = NavigationBarCommandStatus.Checked Then MainPage.slPhotos.Visibility = Visibility.Collapsed MainPage.slPhotos.Children.Clear() RemoveHandler map.ViewChangeEnd, AddressOf MainPage.MyMap_ViewChangeEnd ElseIf status = NavigationBarCommandStatus.Normal Then MainPage.slPhotos.Visibility = Visibility.Visible AddHandler map.ViewChangeEnd, AddressOf MainPage.MyMap_ViewChangeEnd AddHandler MainPage.svc.GetClusterInViewCompleted, _
AddressOf MainPage.svc_GetClusterInViewCompleted Dim bounds As LocationRect = map.BoundingRectangle MainPage.svc.GetClusterInViewAsync(bounds.Northwest.Latitude, _
bounds.Northwest.Longitude, bounds.Southeast.Latitude, _
bounds.Southeast.Longitude, CInt(map.TargetZoomLevel), _
MainPage.mapWidth, MainPage.mapHeight) End If End Sub Public Overloads Overrides Function GetStatus(ByVal map As MapBase) _
As NavigationBarCommandStatus Dim status As NavigationBarCommandStatus = NavigationBarCommandStatus.Normal If MainPage.slPhotos.Visibility = Visibility.Visible Then status = NavigationBarCommandStatus.Checked End If Return status End Function End Class Class ToggleFullscreen Inherits NavigationBarCommandBase Public Sub New() End Sub Public Overloads Overrides Sub Execute(ByVal map As MapBase) Application.Current.Host.Content.IsFullScreen = _
Not Application.Current.Host.Content.IsFullScreen End Sub End Class #End Region

 

Preparing the Photo-Viewer

The Photo-Viewer is basically a ChildWindow that comes as part of the Silverlight Toolkit for Visual Studio 2008 SP1 and is out-of-the-box part of Visual Studio 2010 Beta 2. So if you are using Visual Studio 2010 Beta 2 just add a new item of type ChildWindow

image

The basic xaml is quite simple since we add most of child-elements dynamically through code…

<controls:ChildWindow x:Class="BM_SL_PhotoMap.PhotoShow"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
           Width="400" Height="300"
           Title="PhotoShow">

    <Grid x:Name="LayoutRoot" Margin="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <Image x:Name="imgCurrentPhoto" Stretch="Uniform" Grid.Column="0" Grid.Row="0" 
Grid.RowSpan="3"/> <StackPanel x:Name="spPhotos" Background="#CC646464" Opacity="0.8" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="800" Height="130" Grid.Row="2" Grid.Column="0"
MouseEnter="spPhotos_Enter"
MouseLeave="spPhotos_Leave" > <Border CornerRadius="5" BorderThickness="3" BorderBrush="White"
Margin="5" Height="120" Width="60"> <StackPanel Orientation="Vertical" Height="120"> <Image x:Name="imgPlay" Source="Play.png" Width="40" Stretch="Uniform"
Margin="0,5,0,0" VerticalAlignment="Top" /> <Image x:Name="imgPause" Source="Pause.png" Width="40" Stretch="Uniform"
Margin="0,25,0,0" /> </StackPanel> </Border> <Image Source="PanLeft.png" Width="50" Stretch="Fill" Height="120"
MouseLeftButtonDown="PanLeft_MouseLeftButtonDown"/> <ScrollViewer x:Name="svThumbs" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" Width="600" BorderBrush="{x:Null}" > <StackPanel x:Name="spThumbs" Orientation="Horizontal" Margin="-5,-5,0,0" > </StackPanel> </ScrollViewer> <Image Source="PanRight.png" Width="50" Stretch="Fill" Height="120"
MouseLeftButtonDown="PanRight_MouseLeftButtonDown"/> </StackPanel> <StackPanel x:Name="spTitle" Background="#CC646464" Opacity="0.8" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left" Width="800" Height="30" Grid.Row="0" Grid.Column="0"
MouseEnter="spPhotos_Enter"
MouseLeave="spPhotos_Leave" > <StackPanel x:Name="spTitleText" Orientation="Horizontal" Margin="5"> </StackPanel> </StackPanel> </Grid> </controls:ChildWindow>

…but I have changed the style and added storyboards in Expression Blend in order to show the title and thumbnails when you mouse over the regions and hide them when you don’t:

image

 

Calling the Web Service

Remember when we created the additional controls in our dashboard we also created a class to show or hide the photos. When activated this element adds a handler for the ViewChangeEnd event and queries our database for the first time.

The ViewChangeEnd event calls our web service asynchronously and adds a handler that processes the result.

Public Shared Sub MyMap_ViewChangeEnd(ByVal sender As Object, ByVal e As MapEventArgs)
        Dim map As Map = DirectCast(sender, Map)
        Dim bounds As LocationRect = map.TargetBoundingRectangle

        AddHandler svc.GetClusterInViewCompleted, AddressOf svc_GetClusterInViewCompleted
        svc.GetClusterInViewAsync(bounds.Northwest.Latitude, _
bounds.Northwest.Longitude, bounds.Southeast.Latitude, _
bounds.Southeast.Longitude, CInt(map.TargetZoomLevel), mapWidth, mapHeight) End Sub

When we process the result we first delete all pins in the layer before we loop through the items in our list-object and create new pins. In the tag of each pin we add the name of the photo and also any other information that we want to show in the title. We also add a handler that fires whenever we click on the pin.

    Public Shared Sub svc_GetClusterInViewCompleted(ByVal sender As Object, _
ByVal e As GetClusterInViewCompletedEventArgs) slPhotos.Children.Clear() If e.Error Is Nothing Then For i = 0 To e.Result.Count - 1 Dim myPin As New Pushpin myPin.Location = New Location(e.Result(i)._Lat, e.Result(i)._Lon) myPin.Tag = e.Result(i)._Names slPhotos.Children.Add(myPin) AddHandler myPin.MouseLeftButtonDown, AddressOf PhotoShow Next Else MessageBox.Show(e.Error.Message) End If End Sub

When we click on the pin we split its tag into a string-array the element 0 and all items with an even number contain the file-name of the photo (without the extension (.jpg). All odd-number elements contain the information that we want to show in the title.

image

Now we cycle through this string array and create child elements for our Photo-Viewer. The thumbnail images will have a handler attached that allows us to click on it in order to display a larger image in the main window and and set the title.

    Public Shared Sub PhotoShow(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) cwPhotoShow.spThumbs.Children.Clear() cwPhotoShow.spTitleText.Children.Clear() Dim myPin As Pushpin = DirectCast(sender, Pushpin) Dim myNameArray() As String = Split(myPin.Tag.ToString, ",") For i = 0 To (myNameArray.Length / 2) - 1 If i = 0 Then cwPhotoShow.imgCurrentPhoto.Source = New BitmapImage( _
New Uri(baseURL + "/digicam/h600/" + myNameArray(i) + ".jpg", UriKind.Absolute)) Dim myTitleTxt As New TextBlock With { .Text = Replace(myNameArray(i * 2 + 1), "|", ", "), .Foreground = New SolidColorBrush(Colors.White), .HorizontalAlignment = Windows.HorizontalAlignment.Center} cwPhotoShow.spTitleText.Children.Add(myTitleTxt) End If Dim myImg As New Image With { .Source = New BitmapImage( _
New Uri(baseURL + "/digicam/h100/" + myNameArray(i * 2) + ".jpg", _
UriKind.Absolute)), .Stretch = Stretch.None, .Tag = myNameArray(i * 2 + 1)} AddHandler myImg.MouseLeftButtonDown, _
AddressOf BM_SL_PhotoMap.PhotoShow.Thumb_MouseLeftButtonDown Dim myLocArray() As String = Split(myNameArray(i * 2 + 1), "|") Dim myTxt As New TextBlock With { .Text = myLocArray(0), .Foreground = New SolidColorBrush(Colors.White), .HorizontalAlignment = Windows.HorizontalAlignment.Center} Dim mySP As New StackPanel With { .Orientation = Orientation.Vertical, .VerticalAlignment = Windows.VerticalAlignment.Center} mySP.Children.Add(myTxt) mySP.Children.Add(myImg) Dim myBorder As New Border With { .CornerRadius = New CornerRadius(5), .BorderThickness = New Thickness(3), .BorderBrush = New SolidColorBrush(Colors.White), .Margin = New Thickness(5), .Height = 120} myBorder.Child = mySP cwPhotoShow.spThumbs.Children.Add(myBorder) Next BM_SL_PhotoMap.PhotoShow.numPhotos = cwPhotoShow.spThumbs.Children.Count cwPhotoShow.Show() End Sub

Finally we add a little bit of code to the Photo-Viewer in order to scroll through the thumbnails, display the photo in the main window and also provide a slide-show function. We won’t go into more detail here but it’s all in the sample code and you can explore it for yourself.

Well that brings us to an end of part 2. The application is ready for your local environment.

image

You can have a look at it here and download the sample code here.

If you want to learn more about publishing the application to Windows Azure have a look at part 3.

Advertisements
This entry was posted in Bing Maps. Bookmark the permalink.

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