Visualizing List Data using a Map Control (Heinrich Wendel)

Visualizing List Data using a Map Control (Heinrich Wendel)

Rate This
  • Comments 28

The true power of LightSwitch lies in its combination of Access-like ease of use and quick ramp-up, while also remaining attractive for complex coding scenarios. The new LightSwitch HTML Client allows you to build modern apps in a couple of minutes, deploy them to online services like SharePoint and Azure and access them from a variety of mobile devices.

You already learned about the basic usage of the new LightSwitch HTML Application project type and screen templates in our previous blog posts. We also introduced you to some of our more powerful concepts. We discussed the new HTML Client APIs, such as Event Handling and Promises, showed you how to use them to write your own custom controls that bind to data and how to integrate existing jQueryMobile controls.

In this blog post we will dig even deeper into code and show you how to implement a custom collection control that shows a map. While LightSwitch already provides two collection controls out of the box, namely the List and Tile List controls, location based data is usually visualized using a map. This is especially useful on mobile devices and LightSwitch provides an API that allows you to write your own collection controls.

We will use the familiar Northwind database in this article. Instead of downloading it, we will just connect to an existing OData endpoint on the web. The Northwind database comes with a list of customers and their addresses. We will simply display those customers as pins on Bing Maps and allow users to drill into the details by clicking on one of the pins. In addition to that, we will limit the number of customers displayed on the list by implementing a paging mechanism.

Before we start you should make sure that you have the latest LightSwitch HTML Preview 2 installed on your machine. You can download the LightSwitch HTML Client Preview 2 here. We will go through the basic steps very quickly. If you are not familiar with those you should first walk through our previous tutorials.

Creating a project and connecting to existing data

We will start with a new “LightSwitch HTML Application” project. After creating the project select “Attach to external Data Source”, choose “OData Service” and put in the following endpoint address: “http://services.odata.org/Northwind/Northwind.svc”. Select “None” for the Authentication Type and proceed to the next screen. Check the box to include all Entities and close the dialog by clicking “Finish”. After those preparation steps your project should look similar to the following screenshot:

Building the basic screen

Our small sample application only needs one Screen. We will start with the “Browse” Screen Template. Right click the “Client” node in the Solution Explorer, select “Add Screen…”, use the “Browse Data Screen” and select the “NorthwindEntitiesData.Customers” Entity. This will create a Screen with a list of all customer names that are in the database.

Now we will enhance this Screen to show some more information when selecting a customer. Add a new Popup to the Screen by dragging the “Selected Item” property of the “Customers” Query into the Popups node. Then select the Rows Layout and set “Use read-only controls” in the Properties Window. Finally, select the List and change the “Item Tap” action in the Properties Window to show the created Popup.

The result should look similar to the following screenshot:

Running (F5) the application will show you a list of all customers and selecting one will open a popup with all the details.

Implementing the Control

Now we are ready to replace the built-in List control with our custom maps control. Go back to the Screen Designer and change the List into a Custom Control. In the properties window, also change the height for the Custom Control as well as the Customers List Tab to "StretchToContainer" otherwise the map will come up blank. Now that we have a Custom Control we have to actually implement the code to render it.

Adding the lightswitch.bing-maps control

First we need to add the lightswitch.bing-maps control to our Solution. It will provide a wrapper around the Bing Maps SDK that can be used in your LightSwitch application. We already used a similar wrapper in the Contoso Moving walkthrough. This one is slightly enhanced to provide some additional functionality like adding multiple pins. Switch to the “File View” in the Solution Explorer and add the lightswitch.bing-maps.js (attached to the end of this blog post) to the “Scripts” folder of the Client project. Then open default.htm and add the following line at the beginning of the script block:

<script type="text/javascript" charset="utf­8" 
src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
And this one at the end of the script block:
<script type="text/javascript" charset="utf­8" src="Scripts/lightswitch.bing-maps.js"></script>

Note: If you are deploying your application to an https endpoint, make sure that you use https in your script reference instead of http and also change all occurrences of http within lightswitch.bing-maps.js to https.

Implementing the custom control

Let’s implement the code of the actual control now. Select the Custom Control in the Screen Designer and select “Edit Render Code” from the Properties Window. This will open the BrowseCustomers.js file.

In there, replace the prepopulated code with the following one:

/// <reference path="../GeneratedArtifacts/viewModel.js" />

var mapDiv;
var current = 0;
var step = 15;

myapp.BrowseCustomers.Customer_render = function (element, contentItem) {
mapDiv = $('<div />').appendTo($(element));
$(mapDiv).lightswitchBingMapsControl();

var visualCollection = contentItem.value;
if (visualCollection.isLoaded) {
showItems(current, step, contentItem.screen);
} else {
visualCollection.addChangeListener("isLoaded", function () {
showItems(current, step, contentItem.screen);
});
visualCollection.load();
}
};

function showItems(start, end, screen) {
$(mapDiv).lightswitchBingMapsControl("resetMap");

$.each(screen.Customers.data, function (i, customer) {
if (i >= start && i <= end) {
$(mapDiv).lightswitchBingMapsControl("addPinAsync", customer.Address,
customer.City, customer.Country, i + 1, function () {
screen.Customers.selectedItem = customer;
screen.showPopup("Details");
});
}
});
};

This code will basically initialize the Bing Maps control and then add pins for the first 15 items in the list. Running the application will now no longer show a list, but a map with 15 pins. Clicking one of the pins will open a details popup again.

The code explained

In order to understand the code we first have to understand the Visual Collection and its API. The Visual Collection is used as proxy between queries and the UI and provides various properties and methods, amongst them:

  • visualCollection.isLoaded: This property tells you to whether the data has already been loaded.
  • visualCollection.data: This property gives you to access to the loaded data.
  • visualCollection.load(): This method will start to load the data.
  • visualCollection.selectedItem: This property gives you access to the currently selected item.

In the render method we initialize the lightswitch.bing-maps control and append it to the DOM tree. We will then check whether the visualCollection is already loaded. If that is the case, we add the customers to the list. Otherwise we add a change listener which will call showItems() as soon as the data has been loaded.

In the showItems() method we first clear the map. Then we iterate over the list of customers and add the first 15 items to the map. We also pass a callback function which will open the details popup as soon as a pin is been clicked.

Adding support for Paging

At the moment the map only shows 15 customers. We do not want hundreds of pins to be displayed on the map at once, this would look very chaotic. But we want to be able to look at the next 15 customers. Therefore we are going to implement a paging mechanism.

We are going to add support for going to the next 15 items and back again using two buttons. First add a New Group below the Custom Control, change it to a Columns Layout and set its “Height” to “Fit to Contents” in the Properties Window. Then add two Buttons inside of it (+Add, New Button). In the Add Button dialog select the “Write my own method” option. Name the methods “PreviousItems” and “NextItems”.

Right-click on the NextItem button and select “Edit execute code” and add the following code:

myapp.BrowseCustomers.NextItems_execute = function (screen) {
current = current + step;
if (current + step > screen.Customers.count) {
if (screen.Customers.canLoadMore) {
screen.Customers.loadMore().then(function (result) {
showItems(current, current + step, screen);
});
}
} else {
showItems(current, current + step, screen);
}
};

myapp.BrowseCustomers.PreviousItems_execute = function (screen) {
current = current - step;
showItems(current, current + step, screen);
};

Running the application will now give you the following screen with a working paging mechanism:

The code explained

In order to implement the paging mechanism we first have to understand the built-in query paging mechanism of LightSwitch. In order to save bandwidth LightSwitch will only load a limited number of items at once. It is not important to know how many items are actually loaded just the fact that not all items are loaded at once. The Visual Collection offers two properties and one method for dealing with paging:

  • visualCollection.count: This property tells you the number of currently loaded items. It is important to notice that this is not the total number of items.
  • visualCollection.canLoadMore: This property tells you whether there are more items that can be loaded.
  • visualCollection.loadMore(): This will load more items.

In the execute method of the NextItems button we first check whether we already have enough items loaded to be displayed. If that is not the case we will check whether we can actually load more items and then load them. Using a Promise object, which we explained in our previous API post, we then show the newly loaded items using the showItems() method that we already implemented before.

Wrapping it up

That’s it! One screen, an external control and a few lines of glue code is all you need. The simple yet powerful API of LightSwitch makes it easier than ever to leverage the existing HTML ecosystem.

Please also note that if you want to use the Bing Maps control in a production environment you have to acquire your own API key. You can get the code on the Bing Maps SDK webpage. After you have the code you need to replace our test code in lightswitch.bing-maps.js with your own one.

I hope you will play around with some of the more advanced capabilities of our LightSwitch HTML Client Preview 2 and provide us with feedback in the MSDN Forums. We are always eager to learn more about what you are trying to accomplish using LightSwitch.

- Heinrich Wendel, Program Manager LightSwitch Team

Attachment: lightswitch.bing-maps.zip
Leave a Comment
  • Please add 6 and 6 and type the answer here:
  • Post
  • For the benefit of us JavaScript newbies such as myself, if you wish to visualize more than one entity within your application, simply change "showItems" to another name such as "showItems1". That had me stumped for a few hours as Bing Maps would only populate one entity.

  • Found the paging script might be off by one record.

    given " var step = 25" , 26 records will load with one record repeating itself between pages.

    I was able to correct by changing: "showItems(current, current + step, screen);" to: "showItems(current, current + step - 1, screen);"

    Still not a 100% correct though, as the initial page load will still load that additional record. :(

    Let me know if anyone else is getting the same behaviour, and if so, a suggestion on how to correct that initial page load.

    Yikes! three comments on one post.... sorry about that.

    Roger

  • Hi,

    My experience is that the map control doesn't work under HTTPS mode for Windows Phone 7 and 8.  I got

    AppMapTemplate_render_ method error: TypeError: Object doesn't support property or method 'lightswitchBingMapsControl'

    Wonder if someone on your team can test this out? Not concern if this works on Phone 7 but at least it should work on Phone 8.

  • @Frank: Try to include the Bing Maps script using the following link instead of the one in the post: ecn.dev.virtualearth.net/.../mapcontrol.ashx

  • (The blog actually prettifies the link, make sure that you copy and paste the complete link, including the https:// at the beginning and ?v=6.3&s=1 at the end)

  • Hi,

    Done exactly what you did but screen only shows a map. No pins, no data.

    I'm using vs2013. Maby it's not working under vs2013?

    Any thoughts

    Sven

  • @Sven: Would you mind providing me with your project, I am happy to take a look at it.

  • Hi,

    My Project is very large and complex.

    I should take ages, uploding it.

    I will try to build a small one and see if the issue remains.

    Can you give me some directions. The example, provided in the Contoso works...Strange.

    Regards

    Sven

  • Now it's working. Made some tweaking in your code :)

    Regards

    Sven

  • Hi, I have made this application in VS 2012, Update 4, and when I run this app, I see only "Browse Customers". When I have only List, it was ok, but when I changed to Custom cotrol, maps I dont see. Can you send me please application, which is working? Thank for all.

  • Nice Post!

  • Great tutorial, but to use it with https end point, url needs to beecn.dev.virtualearth.net/.../mapcontrol.ashx or it will not work.

    Kind Regards,

    V

  • Using Visual Studio 2012 Ultimate with Update 4 was not seeing the Map at all.

    Did some testing into the lightswitch.bing-maps.js to follow the code to see that it was creating the map and that the pin logic was working properly... but the code was still not displaying the map at all!

    Modified "BrowseCustomers.JS" to use this code:

    function showItems(start, end, screen)

    {

      $(mapDiv).lightswitchBingMapsControl("resetMap");

      $.each(screen.Customers.data, function (i, customer)

      {

         if (i >= start && i <= end)

         {

            $(mapDiv).lightswitchBingMapsControl("addPinAsync", customer.Address,

                customer.City, customer.Country, i + 1, function ()

                {

                   // visualCollection.selectedItem: This property gives you access to the currently selected item.

                   screen.Customers.selectedItem = customer;

                   screen.showPopup("Details");

                });

         }

      });

      // Set the Map Size!

      $(mapDiv).width(500); // Added

      $(mapDiv).height(500); // Added

    };

    WITHOUT the Width and Height, the Map was NOT visible at all!

    Thank you for this wonderful article.

    :)

Page 2 of 2 (28 items) 12