Visualizing List Data using a Map Control (Heinrich Wendel)

Visualizing List Data using a Map Control (Heinrich Wendel)

Rate This
  • Comments 31

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 1 and 8 and type the answer here:
  • Post
  • This is a great article. Explained a lot about the Visual Collections. The paging API is very clean considering how difficult async calls are to deal with.

  • Didn´t call HTML5 modern. It´s only Hype.

  • Re my earlier post. I had not named the Dialog Screen "Details". Now it works...Thanks

  • Excellent article. In your render function, I'm curious why you set a change listener for isLoaded instead of doing this:

    visualCollection.load().then(function () {

       showItems(...);

    });

    Is it not best practice to handle "then" as above?

  • John,

    In this particular case, you could either do what Heinrich posted or use a promise like you describe because the isLoaded value changes at the same point in time that the promise is completed. In other cases, it may be possible to only listen to a change event or only wait for completion of a promise, depending on the API available.

    In general if you have the option of both, I’d probably opt for the change event because it’s easier to understand than promises from a "UI just reacts to change events" perspective. The promise is somewhat more explicit on when your code will be called though. So really it's up to you.

    Hope that helps,

    Stephen

  • I was not able to make a connection using the address above, i needed to use services.odata.org/.../Northwind.svc, note the addition of the "/" to the end of url.

    Is this the standard address convention for OData connections?

  • Does Microsoft plan on adding support for ESRI's ArcGIS server maps?

  • @Josh - Looks like ArcGIS already supports Silverlight & JavaScript clients, so it shouldn't be too hard to integrate into either type of LightSwitch client. Check this out: www.esri.com/.../get-started

  • Can we do it for Google too?

  • Hi Heinrich,

    The above Bing Map sample is not working using Latest version. Is there any version / JS file changes required?

    As per the  Fiddler the log and download content is fine.

    Regards

    Dwarapudi

  • Dwarapudi,

    I update the walkthrough to also work with the CTP4. There is basically only two changes I made:

    - You have to set the the height of the custom control and tab to StretchToContainer since we changed the defaults

    - Dialogs are now called popups, so the call to showDialog should be showPopup now

    Heinrich

  • Hi Heinrich,

    Thank you. Its working.. Instead of showing the entire world map how can I show a specific country / or show map focus on  available PINS as per the data.  

    E.g I just want to show only Australia.

    Thank You

    Dwarapudi

  • Dwarapudi,

    Bing Maps has a very rich API which is described on their homepage: www.microsoft.com/.../web.aspx. My tutorial only uses a small subset of the API. You would need to change the lightswitch.bing-maps control to use some of the other APIs.

    Heinrich

  • To use the CTP4, I did the following changes in the file lightswitch.bing-maps.js:

    /// <reference path="jquery-1.8.2.js" />

    /// <reference path="jquery.mobile-1.2.0.js" />

    /// <reference path="msls-1.0.0.js" />

    but I am getting th efollowing error when I run your sample:

    Unhandled exception at line 12, column 5 in http://localhost:50188/HTMLClient/Scripts/lightswitch.bing-maps.js

    0x800a01b6 - JavaScript runtime error: Object doesn't support property or method 'widget'

    Please advise. Thanks in advance.

  • @Dwarapudi, if you just wish to show Australia, you need to edit the lightswitch.bing-maps.js file and add within the $widget options something like:

               zoom: 4.5,

               center: new Microsoft.Maps.Location(-25.641526, 135.323437),

Page 1 of 3 (31 items) 123