Bing Maps Silverlight with 3D Mini Map

On one of my recent projects I had the task of figuring out how to display flash components on top of a Silverlight map. After figuring that out I realized how easy it is to add in other content to your page that isn’t supported by Silverlight but is supported by HTML. The purpose of this article is to show how to perform simple communication with Silverlight and JavaScript, and how to overlay non-Silverlight content on top of Silverlight. For this article we will be creating a Silverlight map that uses Bing Maps 3D as a mini-map. To make things a bit trickier we are going to add a Silverlight border to our 3D map. Here is a screen shot of the finished application:

image

The first step is to create a new Silverlight application in Visual Studios. We will start off by adding in our javascript and HTML in the web page. Note that in order to get our map to appear on top of the Silverlight map we will need to set the windowless parameter of the Silverlight object to true. We will need to make a refence to the Bing Maps AJAX control in the head of the document. We will want to create three javascript functions; one to load the map, one to reposition the map div, and one to set the center and zoom level of the map. The following will need to be added to the head of your web page:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>

<script type="text/javascript">
    var map = null;

    function GetMap() {
        map = new VEMap(‘myMap’);
        map.LoadMap(null, null, VEMapStyle.Hybrid, null, VEMapMode.Mode3D, false);
        map.HideDashboard();
        map.HideScalebar();
    }

    function SetMapCenterAndZoom(latitude, longitude, zoomLevel) {
        map.SetCenterAndZoom(new VELatLong(latitude, longitude), zoomLevel);
    }

    function PositionMap(left, top) {
        var div = document.getElementById(‘myMap’);
        if (div) {
            div.style.top = top + "px";
            div.style.left = left + "px";
        }
    }

</script>

Note that we are loading the map into 3D mode and hiding all the navigation bars. Also note that our SetMapCenterAndZoom method is taking in basic objects.

We can then create a div for our map and add an onload event to the page to load the map. Because I want to be able to move the map around and have it sit on top of the Silverlight control I’ll set the position to absolute. The body of your web page should look like this:

<body onload=”GetMap()”>
<form id="form1" runat="server" style="height:100%">
    <div id="silverlightControlHost">
         <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
             <param name="source" value="ClientBin/BingMapCommunication.xap"/>
            <param name="onError" value="onSilverlightError" /> <param name="background" value="white" />
             <param name="minRuntimeVersion" value="3.0.40818.0" /> <param name="autoUpgrade" value="true" />
             <param name="windowless" value="true"/>
             <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0&quot; style="text-decoration:none">
            <img src="http://go.microsoft.com/fwlink/?LinkId=108181&quot; alt="Get Microsoft Silverlight" style="border-style:none"/> </a>
         </object>
         <iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe>
     </div>
</form>
<div id=’myMap’ style=’position:absolute;width:400px;height:400px;top:0px;left:0px;z-index:1;’></div>
</body>

We can now start working the Silverlight part. Our Silverlight xaml will consist of a Map, a border for our 3D map, and a grid which will be a placeholder for our 3D map. The grid will basically just take up the space in Silverlight that we need so that the border will be sized correctly behind our map. You xaml should look like this:

<UserControl x:Class="BingMapCommunication.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl">
    <Grid x:Name="LayoutRoot">
        <m:Map Name="MyMap" CredentialsProvider="Your Bing Maps App Key">
            <Border CornerRadius="10" Background="Blue" Width="420" Height="420"
                    VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10">
                <Grid x:Name="AjaxMapContainer" Width="400" Height="400" Margin="10"></Grid>
            </Border>
        </m:Map>
    </Grid>
</UserControl>

In the code behind for the main page we will want to add an event handler to our grid so that when ever it’s layout is updated the position of our map is updated.  In this event handler we will want to calculate the top left screen position of the grid. This is allows us to move our grid around anywhere in Silverlight and have the 3D map move with it accordingly. This will be noticeable when you resize your browser. We will also want to add an event handler for when the map view has finished changing. This will allow use to tell the 3D map to updates it position. Putting this all together your code behind to your Silverlight app should look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Browser;
using Microsoft.Maps.MapControl;

namespace BingMapCommunication
{
    public partial class MainPage : UserControl
    {
        #region Constructor

        public MainPage()
        {
            InitializeComponent();

            AjaxMapContainer.LayoutUpdated += new EventHandler(AjaxMapContainer_LayoutUpdated);
            MyMap.ViewChangeEnd += new EventHandler<MapEventArgs>(MyMap_ViewChangeEnd);
        }

        #endregion

        #region Private Event Handlers

        private void AjaxMapContainer_LayoutUpdated(object sender, EventArgs e)
        {
            try
            {
                // Get the transform information based off the root element
                GeneralTransform gt = AjaxMapContainer.TransformToVisual(Application.Current.RootVisual);

                // Find the top left corner position of the LayoutRoot element
                Point topLeft = gt.Transform(new Point(0, 0));

                if (topLeft != null)
                {
                    HtmlPage.Window.Invoke("PositionMap", new object[] { topLeft.X, topLeft.Y });
                }
            }
            catch (Exception) { }
        }

        private void MyMap_ViewChangeEnd(object sender, MapEventArgs e)
        {
            HtmlPage.Window.Invoke("SetMapCenterAndZoom",
                new object[] { MyMap.Center.Latitude, MyMap.Center.Longitude, MyMap.ZoomLevel });
        }

        #endregion
    }
}

You should now be able to run your application and see both a Silverlight and a 3D map. Moving the Silverlight should update the position of the 3D map when you have stopped moving the Silverlight map. If you try attaching to the event when the frame changes in the view you will notice that the 3D map moves slowly as it will receive several calls which it will try and animate to.

This is a simple example of how to control a 3D map from Silverlight. If you want to take things a bit further a simple functionality to add would be to have a Silverlight slider to change the pitch of the 3D map. If your a bit more adventurous you could always create a 3D plugin that communicates with your Silverlight. This might be useful if you want to pull in altitude coordinates from the 3D map and get them into Silverlight.

You can find the complete working sample code here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/BingMapCommunication.zip

Advertisements

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