Skip to main content

Weblog Ton Stegeman [MVP]

Go Search
Home
  

ODC 2008
If you have a question or suggestion, please contact me through Windows Live Messenger.
My status: .

If I am not online, please send me an e-mail.
Weblog Ton Stegeman [MVP] > Posts > SharePoint 2010, the Client Object Models and Bing Maps
SharePoint 2010, the Client Object Models and Bing Maps

In a previous blogpost I wrote about the Silverlight application I created to show the location of people posting comments on my blog. These locations were displayed as red dots on a Bing Maps map. In this solution I am using:

  • SharePoint 2010
  • .NET Managed Client Object Model
  • Silverlight Client Object Model
  • Silverlight 3 Bing Maps control

This post describes how I created it. The source code is available in this ZIP file.

Step 1 – Populate the data

First thing I did was the import of the data into a custom SharePoint 2010 list, using the .NET Managed Client Object Model. I exported the comments in SharePoint 2007 to a CSV file and imported them into a list in SharePoint 2010. The code to do that is shown in the snippet below.

   1: int count = 0;
   2: using (ClientContext context = new ClientContext("http://tst.demo/sites/team/Comments"))
   3: {
   4:     Web web = context.Web;
   5:     ListCollection lists = web.Lists;
   6:     IEnumerable<List> resultLists = context.LoadQuery(lists.Where(
   7:                              list => list.RootFolder.Name == "Comments"
   8:         ));
   9:     context.ExecuteQuery();
  10:     List commentsList = resultLists.FirstOrDefault();
  11:     if (commentsList != null)
  12:     {
  13:         String[] lines = System.IO.File.ReadAllLines("C:\\export.csv");
  14:  
  15:         for (int i = 0; i < lines.Length; i++)
  16:         {
  17:             ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
  18:             ListItem companyItem = commentsList.AddItem(itemCreateInfo);
  19:             String[] columns = lines[i].Split(new String[] { ";" }, StringSplitOptions.None);
  20:             companyItem["Title"] = columns[1];
  21:             DateTime created = DateTime.MinValue;
  22:             if (DateTime.TryParse(columns[0], new System.Globalization.CultureInfo(1043), System.Globalization.DateTimeStyles.AssumeLocal, out created))
  23:             {
  24:                 companyItem["Date"] = created;
  25:             }
  26:             companyItem["City"] = columns[2];
  27:             companyItem.Update();
  28:             count++;
  29:         }
  30:         context.ExecuteQuery();
  31:     }
  32: }
  33: MessageBox.Show(String.Format("Imported {0} items", count));

First thing after setting up the ClientContext (based on the URL of the site) to to get a reference to the list. I am using the LoadQuery method of the ClientContext object to get this list. This method allows us to load objects based on a LINQ query. The application tries to find the list based on the name of the rootfolder. Please note this is Linq to Objects, not Linq to SharePoint. Linq to SharePoint is a server side only technology, and we are creating a client application to upload the data. Instead of this way to set up the context, we could have used ClientContext.Current to get the context. If we use this, the SharePoint list must be in the same SharePoint context as the Silverlight application.
For every line found in the CSV file, we add a new item to the list, using the ListItemCreationInformation object. Remember that these list items will be added to SharePoint, once ExecuteQuery gets called. This is nice, because we don’t need a roundtrip to the server for every listitem. In my case, I am uploading some 500 list items in one batch. When I first ran this application, it threw an exception of type Microsoft.SharePoint.Client.ServerException. The message of this exception is The request uses too many resources. Apparently a batch of 500 items is too big. I ran the application in batches of 100 items, and that was no problem.

Step 2 – Get the data from SharePoint in Silverlight

Second step is to get the data from the SharePoint list into custom “Comment” objects. After creating the Silverlight application in Visual Studio 2010 (use Silverlight 3!), I added references to the SharePoint Silverlight client assemblies. These are ‘Microsoft.SharePoint.Client.Silverlight.dll’ and ‘Microsoft.SharePoint.Client.Silverlight.Runtime.dll’ and they can be found in folder <SharePoint Root>\TEMPLATE\LAYOUTS\ClientBin. My Silverlight application is created using the out of the box Visual Studio Silverlight application template. This automatically gives you a Home tab, with Home.xaml. In the code behind of Home.xaml, the method ‘OnNavigatedTo’ is implemented. The code is shown below:

   1: protected override void OnNavigatedTo(NavigationEventArgs e)
   2: {
   3:     demoMap.Mode = new Microsoft.Maps.MapControl.AerialMode();
   4:     _applicationId = ((Microsoft.Maps.MapControl.ApplicationIdCredentialsProvider)demoMap.CredentialsProvider).ApplicationId;
   5:     ThreadPool.QueueUserWorkItem(new WaitCallback(LoadMapData));
   6: }

The data is loaded using the method LoadMapData (on a different thread).  If you don’t run the code that loads the data on a different thread, your application will raise a InvalidOperationException. The message you will get is: The method or property that is called may block the UI thread and it is not allowed. The code snippet below shows the method LoadMapData. This method generates a CAML query and loads the data from SharePoint. CAML is still around in SharePoint 2010. It is the only way in the client object models to query your lists.

   1: private void LoadMapData(Object stateInfo)
   2: {
   3:     ClientContext context = ClientContext.Current;
   4:     Web web = context.Web;
   5:     String listName = "CommentsList";
   6:     ListCollection lists = web.Lists;
   7:     IEnumerable<List> resultLists = context.LoadQuery(lists.Where(list => list.RootFolder.Name == listName));
   8:     context.ExecuteQuery();
   9:     List commentsList = resultLists.FirstOrDefault();
  10:     if (commentsList != null)
  11:     {
  12:         CamlQuery camlQuery = new CamlQuery();
  13:         camlQuery.ViewXml = "<View><Query><Where><IsNotNull><FieldRef Name='City'/></IsNotNull></Where></Query></View>";
  14:         _comments = commentsList.GetItems(camlQuery);
  15:         context.Load(_comments,
  16:              items => items.Include(
  17:                 item => item.Id,
  18:                 item => item["Title"],
  19:                 item => item["Date"],
  20:                 item => item["City"]));
  21:  
  22:         ClientRequestSucceededEventHandler success = new ClientRequestSucceededEventHandler(SuccesHandler);
  23:         ClientRequestFailedEventHandler failure = new ClientRequestFailedEventHandler(FailureHandler);
  24:         context.ExecuteQueryAsync(success, failure);
  25:     }
  26: }

As you see in the example, the query that gets the data from SharePoint is asynchronous. Instead of calling ExecuteQuery, we use ExecuteQueryAsync. This method takes 2 parameters, the eventhandlers that are called when the method completes. There is one handler for successful completion and one in case an exception occurred. The sample below shows my success event handler.

   1: private void SuccesHandler(object Sender, ClientRequestSucceededEventArgs e)
   2: {
   3:     List<Comment> comments = new List<Comment>();
   4:     foreach (ListItem item in _comments)
   5:     {
   6:         Comment comment = new Comment();
   7:         if (item["Title"] != null)
   8:             comment.Title = item["Title"].ToString();
   9:         if (item["City"] != null)
  10:             comment.City = item["City"].ToString();
  11:         if (item["Date"] != null)
  12:             comment.Date = (DateTime)item["Date"];
  13:         comments.Add(comment);
  14:     }
  15:     if (comments.Count > 0)
  16:     {
  17:         for (int i = 0; i < comments.Count; i++)
  18:         {
  19:             GetCity(comments[i].City, i);
  20:         }
  21:     }
  22: }

This snippet iterates through the listitem collection in the result set and creates a new Comment object for every item. For every comment, the method GetCity is called.

Step 3 – Add a Bing Maps control to your XAML

To add the Bing Maps control to your XAML, you need to follow these steps. Chris Pietschmann has a good article that will get you started.

  • Install the Bing Maps Silverlight Control SDK (presuming you have everything in place for Silverlight 3 development)
  • Add project references to the “Microsoft.Maps.MapControl.dll” and “Microsoft.Maps.MapControl.Common.dll” assemblies.
  • Set up a Bing Maps developer account.
  • Add XAML to your application

The XAML to add the Bing Maps control to my application is shown below:

   1: <navigation:Page x:Class="TST.SDE20091214.SilverlightComments.Home"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   5:     xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
   6:     xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
   7:     mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
   8:     Title="Home"
   9:     Style="{StaticResource PageStyle}">
  10:  
  11:   <Grid x:Name="LayoutRoot">
  12:     <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
  13:  
  14:       <StackPanel x:Name="ContentStackPanel">
  15:  
  16:         <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
  17:                            Text="Demo using Silverlight 3 Bing Maps control"/>
  18:             <TextBlock x:Name="ContentText" Style="{StaticResource ContentTextStyle}"
  19:                            Text="Ton Stegeman"/>
  20:                 <m:Map Height="800" Name="demoMap" CredentialsProvider="INSERT_YOUR_ID">
  21:                 </m:Map>
  22:             </StackPanel>
  23:  
  24:     </ScrollViewer>
  25:   </Grid>
  26:  
  27: </navigation:Page>

In the attribute CredentialsProvider of the Map control you need to add your Bing Maps key. Next thing to do is to implement the GetCity method that plots the city on the map.

Step 4 – Find the cities on the map

The method GetCity finds the cities using a Bing Maps webservice and plots them on the map. First thing to do is get a service reference to the Bing Maps service. Add a service reference to the http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc and give it a name. In my case it is called GeocodeService. After setting up the request, you need to pass the credentials. This is your Bing Maps key, the CredentialsProvider of the Map control in the XAML. In the OnNavigatedTo method in Step 2, you will find the private member _applicationId is set to the ApplicationId. This Id is saved in the private member, because the code that uses it is running in a different thread in which we cannot get access to the user interface components.

   1: private void GetCity(string city, int wayPointIndex)
   2: {
   3:     if (String.IsNullOrEmpty(city))
   4:         return;
   5:     GeocodeService.GeocodeServiceClient geocodeService = new GeocodeService.GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
   6:     geocodeService.GeocodeCompleted += new EventHandler<GeocodeService.GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);
   7:     GeocodeService.GeocodeRequest geocodeRequest = new GeocodeService.GeocodeRequest();
   8:  
   9:     Credentials cred = new Credentials();
  10:     cred.ApplicationId = _applicationId;
  11:  
  12:     geocodeRequest.Credentials = cred;
  13:     geocodeRequest.Query = city;
  14:     geocodeService.GeocodeAsync(geocodeRequest, wayPointIndex);
  15: }

The snippet above uses the ‘city’ passed as parameter as the query variable. The Bing Maps service is asynchronous, and we call the service using the GeocodeAsync method. When this method completes successfully, the GeocodeCompleted event is raised. This is implemented as in the snippet below.

   1: private void geocodeService_GeocodeCompleted(object sender, GeocodeService.GeocodeCompletedEventArgs e)
   2: {
   3:     _count++;
   4:     int waypointIndex = System.Convert.ToInt32(e.UserState);
   5:  
   6:     if (e.Result != null && e.Result.Results.Count > 0)
   7:     {
   8:         Dispatcher.BeginInvoke(() =>
   9:         {
  10:             foreach (GeocodeService.GeocodeResult result in e.Result.Results)
  11:             {
  12:                 Location location = new Location(result.Locations[0].Latitude, result.Locations[0].Longitude);
  13:                 Ellipse point = new Ellipse();
  14:                 point.Width = 10;
  15:                 point.Height = 10;
  16:                 point.Fill = new SolidColorBrush(Colors.Blue);
  17:                 point.Opacity = 0.65;
  18:                 MapLayer.SetPosition(point, location);
  19:                 MapLayer.SetPositionOrigin(point, PositionOrigin.Center);
  20:                 demoMap.Children.Add(point);
  21:             }
  22:         });
  23:     }
  24: }

 

Please note that this handler is not running on the UI thread, and therefore we need to call Dispatcher..BeginInvoke() before updating the UI.

Step 5 – Test the application

Last step is to test the application. There are several ways to deploy a Silverlight application, and there are pros and cons for every solution. I have uploaded the XAP file to a document library. On my test page I am using the new Silverlight Web Part of SharePoint 2010 (Media and Content category).

image

Hope you like the solution and it gives you some ideas to get started with SharePoint 2010 and Silverlight. Using the new client object model it is very easy to get your SharePoint data into the Silverlight application. Full source code is available in this ZIP file.

Comments

There are no comments yet for this post.
Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Title


Body *


Your city *


Type the name of the city you live in (making it easier to handle spam...)

CurrentDate *

Select the current date (see if this gives me fewer spam...)
Attachments

 Links

  SharePoint Object on CodePlex
  Screencast introducing SharePoint Objects
  Content by Type and Filter Web Parts on CodePlex
  Archive
  Archive (Calendar)

 My Latest Blog Posts

Scripting SharePoint 2007 setup: choices and conceptsUse SHIFT+ENTER to open the menu (new window).
Adventures in Visual Studio 2010: Migrate the Content By Type web part to SharePoint 2010Use SHIFT+ENTER to open the menu (new window).
Register SharePoint themes by using a featureUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 development on Windows 2008 Server R2 – Getting StartedUse SHIFT+ENTER to open the menu (new window).
New release SharePoint Objects: features and groupsUse SHIFT+ENTER to open the menu (new window).
Constructing the url to the SharePoint Edit Permissions pageUse SHIFT+ENTER to open the menu (new window).
Screencast: introduction to SharePoint ObjectsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 and Reporting ServicesUse SHIFT+ENTER to open the menu (new window).
SharePoint Objects – Insight in usage of your SharePoint artifactsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 Custom list schema and the Content Query Web PartUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 Silverlight Client Object Model – ExecuteQuery vs ExecuteQueryAsyncUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010, the Client Object Models and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Having fun with SharePoint 2010, Silverlight 3 and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Connecting TFS 2010 projects to SharePoint sitesUse SHIFT+ENTER to open the menu (new window).
Adding a database to the SharePoint database Server using SPDatabaseUse SHIFT+ENTER to open the menu (new window).