Mixing the Silverlight and JavaScript Esri Web APIs

OK so you may have read the title and thought why would anyone want to do this. Well I can think of a couple of reasons

  • You are more comfortable working with the Esri Silverlight API for creating your GIS workflow as it has better debugging support, may perform better, easier to test complex operations etc.
  • You prefer to design your layout using html
  • You want separation of your main map UI and peripheral controls
  • Because you can! Smile

Communicating between Silverlight and JavaScript is pretty trivial. To go from JavaScript –> Silverlight you need to register the object and define the methods that can be accessed via JavaScript.

// In the constructor
HtmlPage.RegisterScriptableObject("Page", this);            

[ScriptableMember]        
public void DoSomething() 
{     
    // Do something here   
}

To go from Silverlight –> JavaScript you call HtmlPage.Window.Invoke and pass in the relevant parameters. For this example I’ll be concentrating on Silverlight –> JavaScript calls to create a scalebar and legend widget using the Esri JS API with the Silverlight map control.

First we’ll create our Silverlight project and add the map control with some layers and toolkit controls

<UserControl x:Class="SampleSlwithJs.Silverlight.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:esri="http://schemas.esri.com/arcgis/client/2009">
    <Grid x:Name="LayoutRoot" >
    <esri:Map WrapAround="True" x:Name="MyMap">
        <esri:ArcGISTiledMapServiceLayer ID="TiledLayer"
                Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" />
        <esri:ArcGISDynamicMapServiceLayer ID="DynamicLayer" 
                Url="http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Earthquakes/RecentEarthquakesRendered/MapServer" />    
    </esri:Map>

    <esri:Navigation Margin="5" HorizontalAlignment="Left" VerticalAlignment="Bottom"
                         Map="{Binding ElementName=MyMap}"></esri:Navigation>

    <esri:MagnifyingGlass x:Name="MyMagnifyingGlass" Visibility="Visible" ZoomFactor="3"
                              Map="{Binding ElementName=MyMap}" >
        <esri:MagnifyingGlass.Layer>
        <esri:ArcGISTiledMapServiceLayer ID="StreetMapLayer" 
                    Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"/>
        </esri:MagnifyingGlass.Layer>
    </esri:MagnifyingGlass>
    </Grid>
</UserControl>

In the code behind we can now wire up when we want to make the calls to the JavaScript functions. As we’re going to have a scale bar and legend we need to know what the map extent is going to be and what layers we are interested in.

using System.Windows.Browser;
using System.Linq;
using System.Windows.Controls;
using ESRI.ArcGIS.Client;

namespace SampleSlwithJs.Silverlight
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            MyMap.Layers.LayersInitialized += LayersInitialized;
        }

        void LayersInitialized(object sender, System.EventArgs args)
        {
            HtmlPage.Window.Invoke("Initialise", MyMap.Extent.XMin, MyMap.Extent.YMin, MyMap.Extent.XMax, MyMap.Extent.YMax,
                MyMap.SpatialReference.WKID,
                MyMap.Layers.OfType<ArcGISDynamicMapServiceLayer>().First().Url);

            MyMap.ExtentChanged += MapExtentChanged;
            // Call this to create the scalebar for the inital view
            HtmlPage.Window.Invoke("MapExtentChanged", MyMap.Extent.XMin, MyMap.Extent.YMin, MyMap.Extent.XMax, MyMap.Extent.YMax);
        }

        void MapExtentChanged(object sender, ExtentEventArgs e)
        {
            HtmlPage.Window.Invoke("MapExtentChanged", e.NewExtent.XMin, e.NewExtent.YMin, e.NewExtent.XMax, e.NewExtent.YMax);
        }
    }
}

From this code you can see that we are calling two JavaScript functions, one when the map layers are initialised and one each time the map extent changes.

Now you need to add the JavaScript code. The content of your html page should be similar to

<script type="text/javascript">    djConfig = { parseOnLoad: true }; </script>
<script type="text/javascript" src="http://serverapi.arcgisonline.com/jsapi/arcgis/?v=2.4compact"></script>
<script type="text/javascript">
    dojo.require("esri.map");
    dojo.require("esri.dijit.Scalebar");
    dojo.require("esri.dijit.Legend"); 
</script>
<script type="text/javascript">
    var scalebar;
    var map;
    function Initialise(xmin, ymin, xmax, ymax, wkid, layerUrl) {
        var initExtent = new esri.geometry.Extent({ "xmin": xmin, "ymin": ymin, "xmax": xmax, "ymax": ymax, "spatialReference": { "wkid": wkid} });
        map = new esri.Map("map", { extent: initExtent });
        esri.hide(dojo.byId("map"));

        var dynamicLayer = new esri.layers.ArcGISDynamicMapServiceLayer(layerUrl, { "opacity": 0.5 });
        map.addLayer(dynamicLayer);
        var legendDijit = new esri.dijit.Legend({ map: map }, "legend");
        legendDijit.startup();
    }
    function MapExtentChanged(xmin, ymin, xmax, ymax) {

        if (map == 'undefined') return;
        var extent = new esri.geometry.Extent({ "xmin": xmin, "ymin": ymin, "xmax": xmax, "ymax": ymax });
        map.setExtent(extent);

        scalebar = new esri.dijit.Scalebar({
        map: map,
        scalebarUnit: "metric"
        }, dojo.byId("scalebar"));
    }
</script>
<div style="height: 600px;">
<div id="silverlightControlHost" style="width: 600px; height: 400px; float: left; position: relative;">
    <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
    <param name="source" value="ClientBin/SampleSlwithJs.Silverlight.xap" />
    <param name="onError" value="onSilverlightError" />
    <param name="background" value="white" />
    <param name="minRuntimeVersion" value="4.0.60310.0" />
    <param name="autoUpgrade" value="true" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.60310.0" style="text-decoration: none">
    <img src="http://go.microsoft.com/fwlink/?LinkId=161376" 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 id="scalebar"></div>
    <div id="map"></div>
</div>
<div id="legend" style="float: right; width: 240px; height: 400px; overflow: hidden; overflow-y: auto;"></div>    
</div>

Now when we run the application we see our Silverlight map control and our JavaScript scale bar and legend controls (the screenshot is showing an ASP.NET MVC template site but you can run it in any html page).

image

Each time the map extent changes the scale bar control is automatically updated to reflect it.

image

Now this code isn’t production quality (brownie points for whoever spots the error and knows what is causing it) and I’m not saying you should go out and use this approach for the next application you work on but its fun to have a play around yourself. Let me know if you find this information useful.

Advertisements

One comment

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