Bing Maps and Microsoft Dynamics CRM Online

Microsoft Dynamics CRM 2011 has been improved to make you more productive by giving you different visualizations of your data, such as the Dashboards feature. Another such visualization that has generated a lot of interest is Bing Maps integration. For example, if you wanted to show a graphical representation of each of your contact’s current location on a map such as the one pictured here:


The solution consists of an HTML web resource, which queries CRM for contacts and renders a Bing Map with their location, displayed in a Dashboard.

Preliminary Steps

1. Create a new solution (or open an existing one) to transport the Web Resource and Dashboard.

2. Create a new *.html file and open it in your favorite IDE (I used Visual Studio). I am going to start with the following template to give the example a jump start. It includes references to the required libraries: Virtual Earth (same as Bing Maps), jQuery, and the Global Client context. The map will be rendered in the “div” element with id = “map” and will cover the whole page (notice the adjusted margins and the 100% width/height for the element). I have also included some generic parsing functions that will make parsing the URL parameters much easier.

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN” >
    <title>Bing Map</title>
    <script type=“text/javascript” src=“”></script>
    <script type=“text/javascript” src=“”></script>
    <script type=“text/javascript” src=“ClientGlobalContext.js.aspx”></script>
    <script type=“text/javascript”>
        //Code will go here
<body style=“margin: 0 0 0 0”>
    <div id=“map” style=“width: 100%; height: 100%;” />
<script type=“text/javascript”>
    var map = new VEMap(“map”);

    //Region: Generic Parsing Functions for URL Parameters
function getParameters(values, unescapeValues) {

        //Taken from samples in the CRM SDK
        var parameters = new Array();
        var vals = (‘?’ == values.charAt(0) ? values.substr(1) : values).split(“&”);
        for (var i in vals) {
            vals[i] = vals[i].replace(/\+/g, ” “).split(“=”);
            if (unescapeValues) {
                parameters[unescape(vals[i][0])] = unescape(vals[i][1]);
            else {
                parameters[vals[i][0]] = vals[i][1];

        return parameters;

    //Code will go here

Querying the Server

Defining the loadAddresses function

In order to get data from the server, this example uses the OData (a RESTful endpoint introduced in CRM 2011) to retrieve the data in a JSON format.

function loadAddresses(entityName, idAttribute, nameAttributes, addressAttributes, descriptionAttributes) {

This function can be called to populate the map with pushpins. The idAttribute is a string field indicating the field on the entity that defines the ID, while the nameAttributes, addressAttributes, and descriptionAttributes parameters are Arrays that indicate the list of entities that define the address that needs to be located on the Map and the name/description that should be displayed for the pushpin.

Defining the Query URL

Since the OData endpoint is RESTful, it is queried using URL parameters. The query selects all of the attributes and retrieves all of the attributes that are needed to render the pushpins.

var serverUrl = GetGlobalContext().getServerUrl();

var requestUrl = serverUrl + “/XRMServices/2011/OrganizationData.svc/” + entityName + “Set?$select=” +

nameAttributes.join(“,”) + “,” + addressAttributes.join(“,”);

if (“string” == typeof(idAttribute) && 0 != idAttribute.length) {

requestUrl += “,” + idAttribute;


else {

idAttribute = “”;


if (null != descriptionAttributes && typeof(descriptionAttributes) == “object” && typeof(descriptionAttributes.length) == “number”) {

requestUrl += “,” + descriptionAttributes.join(“,”);


Querying the Server

Next the request needs to be submitted to the server and added to the map:



type: “GET”,

url: requestUrl,

contentType: “application/json; charset=utf-8”,

dataType: “json”,

error: function (request, textStatus, errorThrown) {

   alert(“Error occurred: ” + request.responseXML);



success: function (data) {

   var results = data.d[“results”];

   var addressList = new Array();

   for (resultKey in results) {



addPushpins(new Array(), addressList);



Since this request will be made asynchronously, we have to provide callbacks that jQuery will call in the case of an error or success. To keeps things simple, the error callback simply displays an alert. The success callback, meanwhile, loops through each of the entities that is returned and adds them to an array, which is then passed in to a addPushpins method that still needs to be defined.

Processing the Query Results

The addPushpins method will have the following execution flow:

  1. Remove the first item from the address list
  2. Submit a call to Bing Maps asynchronously
  3. Create a pushpin object and add it to a list of pushpins
  4. Call addPushpins for the next address

Once all of the pushpins have been generated, the pushpins should be added to the Bing Map.

function addPushpins(pushpins, addresses) {

    function convertToString(entity, attributes) {
        var attributeValues = Array();
        for (var i = 0; i < attributes.length; i++) {
            var value = entity[attributes[i]];
            if ("undefined" != typeof (value)) {
                if ("string" != typeof (value) || 0 != value.length) {

        return attributeValues.join(" ").trim();

    if (addresses.length > 0) {
        var item = addresses.pop();

        var title = convertToString(item, nameAttributes);
        var address = convertToString(item, addressAttributes);
        var description = convertToString(item, descriptionAttributes);

        var moreInfoUrl = null;
        if (0 != idAttribute.length) {

            var id = item[idAttribute];
            moreInfoUrl = serverUrl + "/main.aspx?etn=" + entityName.toLowerCase() + "&id=%7b" + id + "%7d&pagetype=entityrecord";
//This can also be done using a $filter parameter against the OData endpoint
        if (0 == address.length) {

            //If the address is blank, skip this one. Do this asynchronously so that there
            //is not the risk of a stack overflow.
            setTimeout(function() { addPushpins(pushpins, addresses) }, 0);

        map.Find(null, address, null, null, 0, 1, false, false, false, false,
        function (shapeLayer, results, places, moreResults, error) {
            var place = places[0];
            var newShape = new VEShape(VEShapeType.Pushpin, place.LatLong);
            if (null != moreInfoUrl) {


            addPushpins(pushpins, addresses);
    else {
        var shapeLayer = new VEShapeLayer();

In addition to rendering a title, address, and description, it will also generate a More Info URL that will link the user to the page for that specific entity. Once all of the pushpins are generated, the pushpins are added as shapes to the Bing Map.

Finishing the Web Resource

Now, we just have to add a call to the loadAddresses function and the Map will be rendered.

var parameters = getParameters(, true);
if ("undefined" != typeof (parameters["data"])) {
    parameters = getParameters(parameters["data"], false);
var entityName = parameters["entity"];
var idAttribute = parameters["id"];
var nameAttributes = ("undefined" == typeof (parameters["name"]) ? Array() : parameters["name"].split(","));
var addressAttributes = ("undefined" == typeof (parameters["address"]) ? Array() : parameters["address"].split(","));
var descriptionAttributes = ("undefined" == typeof (parameters["description"]) ? Array() : parameters["description"].split(","));
if (0 == descriptionAttributes.length) {
    descriptionAttributes = addressAttributes;

This retrieves all of the URL parameters, parses them, and then calls the loadAddresses function.

Create the Dashboard

Now that the HTML has been defined, open the Solution Explorer for the new Solution and create a new Web Resource with Type of Web Page (HTML) – be sure to use the “Browse” button instead of the Text Editor to ensure that your HTML does not get changed. Don’t forget to publish the web resource. One thing to keep in mind about Web Resources: if you use a “/” in your web resource name (to simulate folder structure, such as “scripts/new_MyWebResource”, you will need to change the referenced path for the ClientGlobalContext.js.aspx to “../ClientGlobalContext.js.aspx”).

In the Solution Explorer, create the new Dashboard and insert the Web Resource. The Web Resource needs to include the Custom Parameter in order for the script to know which entity and attributes to use. The following example uses the Contact entity and address attributes:





Now refresh the main page of the Web Client and navigate to the Dashboard.

Final Notes

The Web Resource that was developed can be still be improved (more error checking, retrieving less data from the server, etc), but the code shows how JavaScript can be used to integrate with Bing Maps. I’ve place the code source here for you.


Michael Scott

Comments (11)

  1. cplcarrot says:

    Do you know if there have been any changes to the Terms and Conditions for Bing maps and how they relate to using them in employee only scenarios?



  2. EMR says:

    Isn't there a straight answer to this Bing Map question? vs a link to legal. Say a call center 10 users; using mappping in UI for entering cases,  using maps in dashboards like in the demos online, using geocoding APIs to validate address or get lat/lon.

  3. Aaron says:

    Hi, I just tried the Bing Maps hack but can't get it to work. I downloaded your source code, created a solution, added your code as a web resource, created a dashboard, linked to the web resource from it, pasted the custom parameter code.

    What happens is that I get a blank page with an input field, but nothing else happens. I wonder what I might have missed.

    Any input would be very nice 🙂

    Here's how it looks:

    And by the way, I'm using CRM 2011 Online version.

    Best regards


  4. Krutika Vanazara says:

    hey Aaron,

    The same thing is happening for me too. What you did to resolve it?



  5. Michael Scott says:

    You most likely are seeing a JScript error. If you run this in IE 9, you can use the JScript debugger to see what is happening.


  6. Michael Scott says:

    You most likely are seeing a JScript error. If you run this in IE 9, you can use the JScript debugger to see what is happening.


  7. Bill Gaylor says:

    Chances are when you pasted the parameters, you left as is.  Make sure you remmove the spaces between some of the parameters…the example has wrapped/  For exampel Address1_Line2 is one word but if you pasted the parms as shown in the example it will put a space between Address1 and _Line2

  8. Joe Pass says:

    Any chance to benefit from an update with the release of the bing Maps Ajax v7 ? Pushpins look a lot nicer in v7, but find method has gone. REST query to get latitude & longitude is now required unless using the latitude and logitude fields of MSCRM.

    Also: maps are localized which is really neat.

  9. tardy says:

    Anybody know how to get the zoom bar to show behind the zoom out button? The screenshot at the top of the article shows the problem. I can get this to work only if I don't store the page as a web resource.

  10. jagadish says:

    Hea i am finding problem with following you screen shorts can you pls send me the link if you have videos on this examples