DevLost

A developer lost in the mountains

A Silverlight menu for Sharepoint




Read my articles on silverlightshow.net:

Silverlight and Sharepoint working together: a Silverlight menu for Sharepoint - Part 1

Silverlight and Sharepoint working together: a Silverlight menu for Sharepoint - Part 2

Discover Sharepoint with Silverlight - Part 1

This article is available also on SilverlightShow:
http://www.silverlightshow.net/items/Discover-Sharepoint-with-Silverlight-Part.aspx

Let me take a step back: in my first article on the winning pair (Sharepoint & Silverlight) I described how to set up a development environment and how to use three different ways to insert a Silverlight application in a Sharepoint site. In a second article I briefly highlighted how to interact with Sharepoint objects inside of a Silverlight application. Now it should be the time to go through the concepts in depth and try to better understand how we can steer Sharepoint by using the Silverlight Client Object Model. So I wondered why not creating an application that can navigate through the hierarchy of the objects exposed by Sharepoint and show its properties? And this was when the title of this article crossed my mind. Exploring, and in a certain way discovering Sharepoint using a sort of Silverlight navigator could be a good opportunity to learn something on the Sharepoint framework and, at the same time, to become familiar with the Silverlight Client Object Model.

This is exactly what we start to see in this article. In the next article we will see the application in action and it will be possible to download the source code.

 

This article may be useful to…

Silverlight developers who want to create Rich Internet Applications exploiting the potential offered by the Sharepoint 2010 platform. To achieve this they need to learn the main concepts behind that and know how to use the internal objects of Sharepoint.

 

Two words on Sharepoint 2010

There is something that I overlooked in my previous articles, indeed. I thought that from the point of view of a Silverlight developer, knowing in broad terms what is Sharepoint as well as the potential contribution of Silverlight are elements deserving a few words. SharePoint is essentially a business productivity platform built on a Web-based technology and you can interact with it using a web browser over an intranet, extranet, or Internet solution. Here is what sharepoint offers:

- An extended collaboration platform which includes enterprise content management (ECM), Web content management (WCM), business intelligence features and social capabilities (blogs, portals, tagging and so on)

- A complete interoperability platform with office applications and means to handle line of business data

- a rich development platform which ensures extensibility and customization.

Focusing on the last point, we can say that Sharepoint 2010 provides both a framework with tools and means to aid power users in building high level applications and a complex programming environment. You may work with page layouts and master pages to change the look and feel as well as the way in which the contents are structured. You can control the flow of information creating the so called workflows, namely business processes intended as sequences of steps to be performed in order to achieve a target. All these tasks can be performed using the high level tools and features offered by the environment. For developers who want more control, Sharepoint comes with a rich object model which has been totally server-based since the beginning. And for client application? Now in Sharepoint 2010 there is a client object model declined into three sub-types: for .NET managed applications, for ECMAScripts executing in the browser and for Silverlight applications. In this model most of the server objects are mapped to client objects with a similar name as in the non-exaustive table below:

tabellaClient

Explaining in detail all these objects is beyond the scope of this article but it’s obvious that a Silverlight developer who wants to seriously build Silverlight apps that strongly interact with the framework should be aware of the Server Object model.

 

The Silverlight client object model behind the scenes

I don’t want to describe the basics of the SL COM (Silverlight Client Object Model) here, since there is the official tutorial for that. But there is something else I want to highlight, that is the mechanism behind. When I was doing my first experiments a particular method of the class Clientcontext caught my attention, that is “ExecutingWebRequest”. I wrote the piece of code below to investigate it further:

 private void ContactSharepoint(string siteUrl)
 {
     myClContext = new ClientContext(siteUrl);
     myClContext.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(myClContext_ExecutingWebRequest);
     myClContext.Load(myClContext.Site);
     myClContext.ExecuteQueryAsync(OnQuerySucceeded, OnQueryFailed);
 }
  
 void myClContext_ExecutingWebRequest(object sender, WebRequestEventArgs e)
 {
     WebRequest wb = e.WebRequest;
  
 }

Here substantially I created a new ClientContext passing the Url of my Sharepoint site in order to know its properties. The logic is simple and straightforward: if you want to obtain information about properties and features regarding one or more properties of one or more client objects you have to use the ClientContext as main entry point, “load” it in your request (the “Site” object in the example above)and fire the query. I added an event handler for ExecutingWebRequest and I put a breakpoint inside the handler as in the image below:

FirstRequest

In this way I discovered that the ExecuteQueryAsync(…) method performs a first HttpWebRequest to the Site Web service (http://<server-url>/_vti_bin/sites.asmx) and then a second HttpWebRequest to the Client Web Service (http://<server-url>/_vti_bin/client.svc) as in the image below:

SecondRequest

The first request is performed only once when Sharepoint is contacted for the first time, all the subsequent requests are turned to the Client web service. So, what happens next? To understand it better I used the Fiddler tool in order to capture and analyze the web traffic.

The image below shows the details captured by Fiddler:

FirstFiddler

In the text area on the top right panel you can see the request invoking the method GetUpdatedFormDigest of the Site web service, and in the text area below the response. This method returns a security validation value (a FormDigest value). What is its purpose? This link http://msdn.microsoft.com/en-us/library/ms472879.aspx clarifies it all. It claims that a web application cannot “modify the contents of the database unless you include security validation on the page making the request.” And furthermore “When the user requests a page, the server returns the page with security validation inserted. When the user then submits the form, the server verifies that the security validation has not changed.” Practically this first request is made to obtain a token with an expiration time which is used by Sharepoint to verify that the subsequent requests to the Client Web Service are genuine. Let’s analyze the second request now. The traffic captured is:

SecondFiddler

In the response we see clearly that we requested information about an object of type SP.Site and all its properties. We also notice another thing: the request is formatted in XML and the response, instead, is in JSON format. Why this? Most probably because the “client.svc” web service is used also by the ECMAScript Object Model to allow javascript developers to use a similar object -oriented pattern. What is important is that for a “client developer” (i.e. Silverlight, javascript or even .managed apps developer) the mechanism is transparent:

1) the Client Object Model translates the request in xml and passes it to the client.svc

2) The client.svc interrogates the content database and returns the response in a JSON format

3) the Client Object Model finally maps the JSON objects to the relevant client objects

now , what if we want to know more about the object Web and, in particular, its title as in the code below?

 private void ContactSharepoint(string siteUrl)
 {
     myClContext = new ClientContext(siteUrl);
     myClContext.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(myClContext_ExecutingWebRequest);
     myClContext.Load(myClContext.Web, w => w.Title);
     myClContext.ExecuteQueryAsync(OnQuerySucceeded, OnQueryFailed);
 }
  
 void myClContext_ExecutingWebRequest(object sender, WebRequestEventArgs e)
 {
     WebRequest wb = e.WebRequest;

 Once again the web traffic captured reveals some interesting things:

ThirdFiddler

The request passes the Digest value previously obtained to ensure that it is valid and the list of actions required with the query linked. In the response we are informed that the value of the title property of the object SP.Web is “Team Site”. This introduces another interesting argument, that is how the parameters are passed to the Load(…) and LoadQuery(…) methods of the ClientContext object and this is the object of the next paragraph.

 

Asking information to Sharepoint: Load(…) and LoadQuery(…)

The Load(…) and LoadQuery(…) methods of the ClientContext obect are defined as follows:

 public void Load<T>(T clientObject, params Expression<Func<T, object>>[] retrievals) where T : ClientObject;
  
 public IEnumerable<T> LoadQuery<T>(ClientObjectCollection<T> clientObjects) where T : ClientObject;
 public IEnumerable<T> LoadQuery<T>(IQueryable<T> clientObjects) where T : ClientObject;

We have already seen some simple examples of using the Load(…) method in this and in my previous article.

At this point it should be clear that when using these methods, the underlying Client Object Model engine prepares a request to Sharepoint in order to know the values of the objects passed as an argument. The request is fired only when we call the ExecuteQueryAsync(…) or the ExecuteQuery() methods of the CleintContext and you can inspect and use the Client objects requested only after this call.

I want to focus on the second argument now. It is (in case of Load(…) method) an array of Lambda Expression with reference to objects of type “ClientObject”. The ClientObject class is the base class for all the client objects listed in the “server-client objects correspondence table” above. So what? Let’s think of an application where starting from a root node you want to know information on all the child nodes as in a treeview – logic and let’s imagine that each node represents a ClientObject of Sharepoint with its properties (petals). In this way you could start from the root node, i.e. the Site object and get all the child nodes with subsequent requests to Sharepoint. Virtually you could navigate each branch of the tree preparing each time the correct expression to pass to the Load(…) method. The question is: how to generalize this via code? One way is to use reflection and in the second part of this article we will see how.

 

ExecuteQueryAsync(…) VS ExecuteQuery(…)

In my previous article I told you that “ […] since Silverlight follows an asynchronous model, you can only make asynchronous queries to the Sharepoint Server from the UI thread. You can perform synchronous queries only in a thread that does not interact with the UI thread […] “.

On the basis of this statement it should be possible to make synchronous queries using ExecuteQuery() also in Silverlight with some caution. In my application I put the call in a secondary thread and I verified that it works. The implementation is very simple, in the code below I instantiated a Thread object passing a lambda expression as argument and then I started the thread. In this way the ExecuteInThread(…) method is executed in a secondary thread and can be used to call ExecuteQuery(…).

 public void ExecuteAction()
 {
  
     Thread t = new Thread(() => ExecuteInThread(new
                                            {
                                                ClientContext = m_clientContext,
                                                ClientObject = m_clientObject,
                                                LeftTreeItem = m_currentItem
                                           }
                                           ));
     
     
     t.Start();

In conclusion using either ExecuteQueryAsync(…) or ExecuteQuery(…) is your choice depending on the scenario.

 

The Silverlight Client Object Model and OOB application limitations

During my experiments I discovered one inconvenience. If you want to use the SL Client Object Model in a SL OOB application, keep in mind that there are strong limitations. The fact is that inside an OOB (Out Of Browser) application from the point of view of Sharepoint you are impersonating an anonymous user and you can view and do just what the anonymous user can view and do. This depends on the fact that the SL Client Object Model interacts with Sharepoint using the default credentials as you can see in the image below where I put a breakpoint on the ExecutingWebRequest() event handler:

DefaultCredentials

With the managed client model (that is the model used for managed applications like command-line apps, winforms and WPF applications) you can change the authentication mode on the ClientContext object using the AuthenticationMode property. For instance, you can set this property on forms authentication which gives you the possibility to supply username and password (obviously you have to enable forms authentication in your Sharepoint site in this case). Unfortunately in the Silverlight client object model this property is not implemented and this means that if you want to interact with Sharepoint using an OOB application you have to take care of all the details needed to perform the authentication.

 

Summary

In this article we began exploring Sharepoint using Silverlight. We have looked more closely at the Silverlight Object Model and we have discovered how it works behind the scenes. We have also given a look to server and client object hierarchy of Sharepoint and highlighted the limitations in terms of interaction between the SL client object model and SL OOB applications. In the second part of the article we will see a SL application able to navigate the hierarchy of client objects and to show their properties.

Silverlight and Sharepoint 2010 a step forward: how to build a small Silverlight 4 utility to upload files in a List or Library of Sharepoint

This article may be considered as the continuation of my previous article “Silverlight and Sharepoint 2010: Getting Started” where I described how to integrate Silverlight applications in a SharePoint solution. This time we will take a step forward investigating on the Silverlight Client Object Model available in Sharepoint 2010. To better understand what we can do we will create, as an exercise, a small but powerful Silverlight utility to upload files, images and documents inside a Library or List.

What we want to do

The objective is to create a Silverlight application capable to list the Libraries and Lists of a Sharepoint site in a DataGrid. We want to be able to insert one or more files into a Library or List through a simple drag & drop of files from the file system over an item of the DataGrid. I decided to do this because the standard procedure to upload files on Sharepoint is a little boring as you will see in the next paragraph.
In this video you can see briefly the creation of the application and here you can download the code.

HomePage

The standard procedure to upload images and files in Sharepoint

Depending on the site template you have chosen, SharePoint 2010 provides a series of lists and libraries where it is suggested to load the images, style sheets, documents, files, etc. In particular, the “Team Site” template includes four Document Libraries which, as you can see in the image below, should be used according to the rules described on the right.

documentLibraries

Now let’s suppose we want to add a series of images to the Site Assets Library and a css file to the Style Library. Starting from the home page, we have to click on “Libraries”, then on “Site Assets”, then on “add document”, then on “Upload multiple files…”, then drag&drop the image files and eventually click OK. To insert the css file we have to follow more or less the same procedure. The following image shows all the steps required.

SP_procedure

It is a little boring, isn’t it? Would not it be better to see all the Libraries and Lists inside a flat grid and perform the drag and drop on a row of the grid? Let’s find out in the next paragraphs.

Building the Silverlight application responsible for uploading files

First of all make sure you have installed the Silverlight Toolkit, since we will use some controls included in this package. Let’s create a new project of type “Silverlight application” in Visual Studio; you can skip the option to “Host the Silverlight application in a new Web site” since we will use a Sharepoint site instead. Add the references to “System.Windows.Controls.Toolkit” and “System.Windows.Controls.Data.Toolkit” and insert the xaml code below in MainPage.xaml:

 xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"             
 xmlns:toolkitData="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Toolkit"  
 >
  
  <toolkit:BusyIndicator x:Name="busyIndicatorElement" IsBusy="false" BusyContent="Contacting Sharepoint..." >
      <toolkitData:DataGridDragDropTarget x:Name="MainGridDragDropElement" HorizontalAlignment="Stretch">
          <sdk:DataGrid Name="MainGridElement" AutoGenerateColumns="True" AllowDrop="True"  HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible"  HorizontalAlignment="Stretch" />
      </toolkitData:DataGridDragDropTarget>
  </toolkit:BusyIndicator>

As you can see the interface is very basic: a BusyIndicator Control surrounds a DataGrid with its drag-n-drop-target Content Control. I used the BusyIndicator Control to get an immediate feedback each time the application is waiting for a response from Sharepoint.

Getting data from Sharepoint using the Silverlight Client Object Model

Using the SL Client Object model to retrieve data from Sharepoint is easier than it seems. However, before going any further you should have three things clear in mind:

  1. since Silverlight follows an asynchronous model, you can only make asynchronous queries to the Sharepoint Server from the UI thread. You can perform synchronous queries only in a thread that does not interact with the UI thread for instance using a ThreadPool.QueueUserWorkItem()
  2. you have to perform two separate tasks: firstly inform the Client Object Model about operations that you need (i.e. accessing properties of Sharepoint objects, data retrieval etc) using a “Load()” method and then send these “loaded operations” to Sharepoint launching an asynchronous call.
  3. any asynchronous query you make, be it successful or failed, will fire a callback running into another thread other than the main UI thread.

To play with the Client Object model you need to add the following reference assemblies: Microsoft.SharePoint.Client.Silverlight and Microsoft.SharePoint.Client.Silverlight.Runtime. They are usually located in “\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin”.

The namespace to use is, as you may guess, Microsoft.SharePoint.Client;

In our specific application that we want to build we need to know the title of the Libraries and Lists included in our Sharepoint site. How can that be obtained? First by defining a ClientContext object in the MainPage code behind , then by creating a method to load your requests to the Client Model and recall it from the MainPage() constructor after InitializeComponent():

 public partial class MainPage : UserControl
  
    private ClientContext myClContext;
  
    public MainPage()
    {
        InitializeComponent();
  
        // Drop Event Handler
        MainGridElement.Drop += new DragEventHandler(SPListsGrid_Drop);
  
        ConnectToSP();
    }
     
    private void ConnectToSP()
    {
        myClContext = new ClientContext("http://My_Server_name");
  
        myClContext.Load(myClContext.Web);
        myClContext.Load(myClContext.Web.Lists);
   
        myClContext.ExecuteQueryAsync(OnConnectSucceeded, OnConnectFailed);
        busyIndicatorElement.IsBusy = true;
    }

As you can see, I instantiated a ClientContext giving the Url of the server then I asked for the access to the Web and Lists objects of Sharepoint calling the load() method. Eventually, I called ExecuteQueryAsync() passing the callbacks to activate in case of success and failure.

Since the callbacks run in a separate thread we can only schedule the execution of the necessary update of the UI using a Dispatcher:

 // callbacks
 private void OnConnectSucceeded(Object sender, ClientRequestSucceededEventArgs args)
 {
     Dispatcher.BeginInvoke(FillGrid);
 }
  
  
 private void OnConnectFailed(object sender, ClientRequestFailedEventArgs args)
 {
     Dispatcher.BeginInvoke(NotifyFailure);
 }

The OnConnectSucceeded(..) callback invokes the execution of the method FillGrid() in the UI thread; FillGrid() performs the databinding to the DataGrid as follows:

 private void FillGrid()
 {
     busyIndicatorElement.IsBusy = false;
     var isource = from List ls in myClContext.Web.Lists.AsEnumerable()
                   select new InfoList() { Title = ls.Title, Description = ls.Description };
  
     MainGridElement.ItemsSource = isource;
 }
   
 public class InfoList
 {
     public string Title { get; set; }
     public string Description { get; set; }
 }

where I used a dummy object (InfoList) since the databinding with anonymous types is still an issue in Silverlight.

Capturing the Drop event and uploading the files dragged

Now it’s time to add the logic for handling the drag&drop of files in the DataGrid. Just an event handler for the Drop event is needed:

 void SPListsGrid_Drop(object sender, DragEventArgs e)
 {
     if (e.Data == null)
         return;
  
     // we have to identify the item in the Grid where the file is dropped 
     Point loc = e.GetPosition(Application.Current.RootVisual);
  
     var list = VisualTreeHelper.FindElementsInHostCoordinates(loc, MainGridElement);
     DataGridRow row = list.FirstOrDefault(i => i is DataGridRow) as DataGridRow;
   
     if (row != null)
     {
         IDataObject dataObject = e.Data as IDataObject;
         FileInfo[] files = dataObject.GetData(DataFormats.FileDrop) as FileInfo[];
   
         InfoList listDetails = row.DataContext as InfoList;
         foreach (FileInfo file in files)
         {
             UploadFile(file, listDetails.Title);
         }
     }
 }

The code above locates the row where the files were dropped and for each of them calls the Upload() method where, again, the Client Object Model is implicated. Here the code for the Upload() method:

 private void UploadFile(FileInfo fileToUpload, string libraryTitle)
 {
     var web = myClContext.Web;
     List destinationList = web.Lists.GetByTitle(libraryTitle);
  
     var fciFileToUpload = new FileCreationInformation();
  
     Stream streamToUpload = fileToUpload.OpenRead();
     int length = (int)streamToUpload.Length;  // get file length
   
     fciFileToUpload.Content = new byte[length];
  
     int count = 0;                        // actual number of bytes read
     int sum = 0;                          // total number of bytes read
  
     while ((count = streamToUpload.Read(fciFileToUpload.Content, sum, length - sum)) > 0)
         sum += count;  // sum is a buffer offset for next reading
     streamToUpload.Close();
   
     fciFileToUpload.Url = fileToUpload.Name;
   
     Microsoft.SharePoint.Client.File clFileToUpload = destinationList.RootFolder.Files.Add(fciFileToUpload);
  
     myClContext.Load(clFileToUpload);
     myClContext.ExecuteQueryAsync(OnLoadingSucceeded, OnLoadingFailed);
     busyIndicatorElement.IsBusy = true;
 }

What is interesting to highlight in the UploadFile() above is that we make use of the Web object of the ClientContext included as a request in the previous query to get the List (or Library) where we want to put the file. Once we have the list (destinationList), we read the file and create a Sharepoint.Client.File object, then load it in the ClientContext created at the beginning. Once again, an asynchronous request is submitted to perform the action. In the OnLoadingSucceeded() callback you can notify the user about the positive result of the operation in various ways. In the source code of my example I just added a “OK, file uploaded!” message box.

Deploy the Silverlight application using a Visual WebPart

In my previous article “Silverlight and Sharepoint 2010: Getting Started” I described three different methods to insert a Silverlight application in a Sharepoint Site. Now we are going to see the fourth (yes, another one). Somehow it is a combination between the second method, “Using a web part”, and the third method “Using a Sharepoint module”. In fact, as in the second method, the Silverlight application is attached to the Visual Web Part by adding a portion of HTML code, which includes the Silverlight host control to the rendering of the web part. Furthermore, for the deployment this fourth method makes use of a mechanism similar to the module method where a module ferries the Silverlight application inside Sharepoint but this time the 'ferry' is the Visual WebPart.

The reason why I used this new method will be clear in a moment, for now I anticipate that in this way we will be able to pass values and parameters between the web part and the Silverlight application. Let’s go ahead by adding a new project to the solution of the Siverlight app that we have just created. Choose the “Sharepoint 2010 Visual Web Part” template from “New Project in Visual Studio:

lib

Choose the option “Deploy as a farm solution”, in the Solution Explorer, locate the “VisualWebPart1” automatically generated and delete it. Now right click on the Project name and choose “Add new item”; from the dialog box choose the “Visual Web Part” template of Sharepoint 2010 and call it “FileUploaderVWP” as in the image below:

FileUploaderVWP

Don’t worry about this strange procedure, it’s just a pragmatic way to give an identifiable name to the Visual Web Part without having to change it by hand here and there in various files. Open the source code of the Visual Web Part (it should be the file “FileUploaderVWP.cs”) and override the Prerender() method as follows:

 protected override void OnPreRender(EventArgs e)
 {
     string webUrl = SPContext.Current.Web.Url;
      
     string renderHost = @"<div id='silverlightControlHost'>
     <object data='data:application/x-silverlight-2,'
     type='application/x-silverlight-2' width='100%' height='100%'>
     <param name='source' value='/_catalogs/wp/SLFileUpload.xap'/>
     <param name='background' value='white' />
     <param name='minRuntimeVersion' value='4.0.50303.0' />
     <param name='autoUpgrade' value='true' />
     <param name='windowless' value='false'/>
     <param name='initParams' value='host_url=" + webUrl +  @"' />
     <a href='http://go.microsoft.com/fwlink/?LinkID=149156
     &v=4.0.50303.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>";
    
     LiteralControl host = new LiteralControl(renderHost);
     Controls.Add(host);
     base.OnPreRender(e);
 }

As you may see a Silverlight host control is injected into the HTML before the rendering of the web part. Now give a look at the value of “source” parameter: “/_catalogs/wp/”; it is the default path where Sharepoint puts all the web parts. We will put also our Silverlight app in this folder in a while. The other interesting thing here is that at the beginning we make a call to the server object model of Sharepoint to retrieve the Url of our Sharepoint site (this code runs on the server so we don’t need to invoke the client model here). The url is then passed to the Silverlight app as an initParams attribute called “host_url”. Why are we doing this? Because when in the Silverlight app we query the Client object model for the first time , we can’t obtain this information in any way.

If you have noticed in the code snippet added in the paragraph Getting data from Sharepoint using the Silverlight Client Object Model, I had included the name of the server statically:

 myClContext = new ClientContext(http://my_server_name/);

This makes the application unusable in other domains without recompile. Passing this information as parameter solves the problem. Now you can substitute the line of code above with the following:

 string webUrl = App.Current.Host.InitParams["host_url"];
 myClContext = new ClientContext(webUrl);

and the application will work also in other domains.

Using the Visual Web Part as a ‘ferry’

The next step is to raise your thumb and hitchhike to find someone who has a seat available for your Silverlight application, destination: 'Sharepoint'. In my previous article it was a module, this time it is the visual Web Part itself. Let’s go back to Visual Studio and, in the Solution Explorer, right click on your Web Part (FileUploaderVWP) and click on “Properties” in the context menu. In the Properties panel Select the “Project Output References” property and click on the small button on the right to open the “Project Output References” Dialog Box; here click on the “Add” button to add an item to the list on the left; configure the properties on the right as shown in the following image:

ProjectOutputReferences

Unfortunately this step is not enough to fully instruct the deployment process. We need to manually modify the file Elements.xml under the Visual Web Part tree including the line highlighted:

 <?xml version="1.0" encoding="utf-8"?>
 <Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
   <Module Name="SPFileUploadVWP" List="113" Url="_catalogs/wp">
     <File Path="SPFileUploadVWP\SPFileUploadVWP.webpart" Url="SPFileUploadVWP.webpart" Type="GhostableInLibrary" >
       <Property Name="Group" Value="Custom" />
     </File>
     <File Path="SPFileUploadVWP\SLFileUpload.xap" Url="SLFileUpload.xap" />
   </Module>
 </Elements>

Here, contrary to what we saw in the 'module method' Visual Studio does not help us to update the file automatically.
You can now select the Sharepoint project just included in the Solution as the “Startup project”, add the Silverlight Project to its “Project Dependencies” and deploy your solution. Your “.xap” file will be added to Sharepoint in the same folder of the other web parts.

Don’t forget to enable debugging: open the dialog box of the properties of your Sharepoint Visual Web Part project; click on the “SharePoint” tab option and set the “Enable Silverlight debugging (instead of Script debugging) checkbox”.

EnableDebugging

Summary

In this article we have put togheter the potential of Silverlight and of the SL Client Object Model added in Sharepoint 2010 to create a small but useful utility to quickly load files in a List or Library of Sharepoint. We have learned how to get the title and description of all the Libraries and Lists in our Sharepoint site and how to insert files in a List. We have also learned how to pass data from the host Web Part to the Silverlight application. Possible improvements are: finding a way to allow upload ing of large files (this is regulated by a setting of the site accessible from the administration) , an extended DataGrid showing the files included in each List or Library, interaction with the publishing framework and a better look and feel in general.

How to extend Bing Maps Silverlight with an elevation profile graph - Part 2

This article is available also on SilverlightShow:
http://www.silverlightshow.net/items/How-to-extend-Bing-Maps-Silverlight-with-an-elevation-profile-graph-ndash-second-part.aspx

Introduction

This is the second and conclusive article about an example of a Bing Maps extension using Silverlight. Let me briefly recall the objective: in the first article I wrote about the need which may arise when planning an itinerary, I underlined that knowing the elevation profile would be useful. Having this functionality using the Maps Silverlight Control is not difficult. In the following sections we will see how to get elevation data as well as to plot them on a graph. You can enjoy a demo here and download the code here.

Where to get the elevation data

There is a series of web services for finding geography-related information around the world provided by some associations and simple websites for free. A valuable source of geographical web services is the GeoNames geographical database (http://www.geonames.org/ ). It offers a series of REST web services to get the elevation data from different measurement models. As you can see in the image below within the application you can choose between 4 different Elevation Digital Models.

.

NewRoute

The ASTER Global Digital Elevation Model (GDEM), whose details you can find here, is done with a sample area of c.a 30m x 30 m and gives the elevation in meters. The SRTM3 model is based on the Shuttle Radar Topography Mission and considers a sample area of ca 90m x 90m. The GTOPO30 is a Global Digital Elevation Model with a sample area of c.a. 1 km x 1km made by the Earth Resources Observation and Science (EROS) Center. The SRTM USG04 option is provided by the http://www.earthtools.org/ website and it is based essentially on the SRTM model described above, integrated and improved using other data sources like the National Elevation Dataset (NED), the Canadian Digital Elevation Data (CDED), and the work of single experts.
Among the others we can also mention the U.S. Geological survey that offers a complete Elevation Query Web Service at the following link:

http://gisdata.usgs.net/XMLWebServices2/Elevation_Service.php

Calling the web services: things get hard

With the contents of the first article and the paragraph above, now we have all the elements to complete the application. In sum, either by choosing the “Route Profile” option or the “Arbitrary Profile” button you obtain a collection of geographic points (i.e. points with latitude and longitude) and we want to know their elevation. This can be accomplished by using one of the web services listed above, and, as you know, Silverlight uses an asynchronous model for all network related calls. On one hand this guarantees that the UI thread will not be blocked by a web request, on the other hand this makes things more difficult when performing a number of requests (one for each geographical point essentially). You could cycle between all the geographical points and each time fire a web request. Then in the Callback you have to link the returned elevation data to the respective geographical point and above all you must be able to understand when all the web requests have been completed before launching the creation of the graph. If you hook an event to each web request, you can set that event at the end of the Callback in order to get a notification, but you cannot obviously wait for that event on the UI thread. So using threads seems to be the solution.

Calling the web services: using threads

For a good developer the first rule of using threads should be “avoid them if you can”. Indeed, threads can be problematic, tricky and tend to make applications harder to debug. However, I was not able to solve the problems listed in the previous paragraph without using threads. The image below explains the method I used.

SchemaThreads

First of all, I created a background thread and I passed to the thread procedure an object containing the collection of geographical points and the GDEM to use. In the thread procedure a number of AutoReset Events equalling the number of points is created. Then, always in the thread procedure, a threadPool launches a number of WebClient calls passing each of them an object containing a point , the GDEM to use and an Event. When each request is executed, the elevation data is saved into the object passed and the relevant Event is set. In the meantime the main thread procedure is stopped by a WaitAll(). After the completion of all the requests the main thread procedure updates the collection of points with the data received and invokes the BuildGraph() method of the UI thread to create the graph.

As you can see in the procedure of the main thread listed in the portion of code below, the requests are splitted in chunks of 64 because the WaitAll() function does not support more than 64 WaitHandles.

 public void MainThreadFunc(object objData)
 {
     mainThreadData mthData = (mainThreadData)objData;
  
     ThreadManager.dataProfileArray = mthData.dataProfileArray;
    int itemsNum = ThreadManager.dataProfileArray.Length;
     ThreadManager.eventsArray = new AutoResetEvent[itemsNum];
     ThreadManager.ThreadObjectInfo[] thInfoArray = new ThreadManager.ThreadObjectInfo[itemsNum];
  
     for (int i = 0; i < itemsNum; i++)
     {
         thInfoArray[i] = new ThreadManager.ThreadObjectInfo(i,
                                                             new AutoResetEvent(false),
                                                             ThreadManager.dataProfileArray[i],
                                                             mthData.serviceType
                                                             );
     }
   
     // for each waypoint we connect to the server in order to obtain the elevation
     // if the num. of waypoints is > 64, then we need to split the requests into chunk of 64 
     // because the WaitAll function does not support more than 64 WaitHandles
     try
     {
         int startindex = 0;
         int endindex = 64;
  
         int chunkWsRequestNum = (int)Math.Ceiling(((double)itemsNum / 64.00));
         if (itemsNum < 64)
             endindex = itemsNum;
  
         AutoResetEvent[] tempArray = new AutoResetEvent[endindex];
   
         for (int chunk = 0; chunk < chunkWsRequestNum; chunk++)
         {
  
             for (int i = startindex; i < endindex; i++)
             {
                 tempArray[i - 64 * chunk] = thInfoArray[i].RequestEvent;
                 ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadManager.DoElevationRequests), thInfoArray[i]);
             }
  
             // the main thread sets a timeout of 40 sec for the getting of elevation data
             AutoResetEvent.WaitAll(tempArray, 40000);
             startindex += 64;
             endindex += 64;
  
             if (endindex > itemsNum)
                 endindex = itemsNum;
         }
  
         lock (theLock)
         {
             // after all threads ended, we can fill the dataProfileArray with elevation data
             for (int i = 0; i < itemsNum; i++)
             {
                 dataProfileCollection[i].Elevation = Convert.ToDouble(thInfoArray[i].LocationToSearch.Elevation, CultureInfo.InvariantCulture);
             }
         }
     }
     catch (Exception ex)
     {
         throw new ArgumentException(ex.Message);
     }
  
     Dispatcher.BeginInvoke(delegate()
     {
         buildGraph();
         ElevationIsInProgress = false;
     });
 }

Since each of the web services I inserted as an option have slightly different modalities of calling and contents of response, I used this simple interface:

 public interface IElevationServiceUsage
 {
     Uri GetUri(string lat, string lng);
     double GetElevation(string result);
 }

which exposes two methods; the GetUri() method builds the url with parameters to make the call. The GetElevation() method parses the content of the response. Below, the class for the request of SRTM data.

 public class SRTM3WSUsage : IElevationServiceUsage
 {
     private const string webServiceUrl = "http://ws.geonames.org/srtm3XML?";
  
     public Uri GetUri(string lat, string lng)
     {
         return new Uri(webServiceUrl + "lat=" + lat + "&lng=" + lng);
     }
  
     public double GetElevation(string result)
     {
         // parse data
         XDocument doc = XDocument.Parse(result);
         var elevation = from value in doc.Descendants("srtm3")
                         select value;
  
         return Convert.ToDouble(elevation.First().Value, CultureInfo.InvariantCulture);
     }
 }

Creation of the graph

The graph used is a line series graph and displays the elevation profile putting the distance in X and the elevation in Y. The source of data is an ObservableCollection of GeoLocation data where the GeoLocation class is shown below:

 public class GeoLocation : Pair
 {
     private object _latitude;
     private object _longitude;
  
     /// <summary>
     /// Gets or sets the Latitude value.
     /// </summary>
     public object Latitude
     {
         get
         {
             return _latitude;
         }
         set
         {
             _latitude = value;
         }
     }
  
     /// <summary>
     /// Gets or sets the Longitude value.
     /// </summary>
     public object Longitude
     {
         get
         {
              return _longitude;
         }
         set
         {
             _longitude = value;
         }
     }
  
     /// <summary>
     /// Gets or sets the Distance value from preceding location.
     /// </summary>
     public object Distance
     {
         get
         {
             return First;
         }
         set
         {
             First = value;
         }
     }
     /// <summary>
     /// Gets or sets the elevation at given lat/long
     /// </summary>
     public object Elevation
     {
         get
         {
             return Second;
         }
         set
         {
             Second = value;
         }
     }
  }

The GeoLocation class is derived from the Pair class which is defined below:

 public class Pair : INotifyPropertyChanged, IComparable
 {
  
     private object _first;
     private object _second;
    private static PairSortMethod _sortOrder;
  
  
    /// <summary>
     /// Gets or sets the first value.
     /// </summary>
     public object First
     {
         get
         {
             return _first;
         }
         set
         {
             _first = value;
             OnPropertyChanged("First");
         }
     }
  
     /// <summary>
     /// Gets or sets the second value.
     /// </summary>
     public object Second
     {
         get
         {
             return _second;
         }
         set
         {
             _second = value;
             OnPropertyChanged("Second");
         }
     }
  
  
     /// <summary>
     /// Implements the INotifyPropertyChanged interface.
     /// </summary>
     public event PropertyChangedEventHandler PropertyChanged;
  
     /// <summary>
     /// Fires the PropertyChanged event.
     /// </summary>
     /// <param name="propertyName">Name of the property that changed.</param>
     private void OnPropertyChanged(string propertyName)
     {
         PropertyChangedEventHandler handler = PropertyChanged;
         if (null != handler)
         {
             handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
     }
  
     public static PairSortMethod SortOrder
     {
         get { return _sortOrder; }
         set { _sortOrder = value; }
     }
  
  
     int IComparable.CompareTo(object obj)
     {
         if (obj is Pair)
         {
             Pair p2 = (Pair)obj; 
             return ((Double)First).CompareTo(p2.First) + ComparerHelper.PairComparison(Second, p2.Second);
         }
         else
             throw new ArgumentException("Object is not a Pair.");
  
     }
 }

The creation of the graph preliminarily implies the creation of the linear Axes X and Y and the creation of a LineSeries object to which the datasource is assigned. In order to correctly dimension the graph, you have to set the max and min value along the X and Y axes. This can be accomplished using the AsQueryable() operator as you may see in the code snippet below:

 public void BuildGraph(ObservableCollection<GeoLocation> newPairCollection, string dependentVariable, string independentVariable)
 {
     ECChartBusyIndicator.IsBusy = true;
     object _first = double.NaN;
    object _second = double.NaN;
  
     double firstMin = (double) (from firstSerie in newPairCollection.AsQueryable()
                                     where (firstSerie.First != null)
                                      select firstSerie.First).Min();
   
    double firstMax = (double)(from firstSerie in newPairCollection.AsQueryable()
                                      where (firstSerie.First != null)
                                      select firstSerie.First).Max();
  
     double secondMin = (double)(from SecondSerie in newPairCollection.AsQueryable()
                                      where (SecondSerie.Second != null)
                                      select SecondSerie.Second).Min();
  
     double secondMax = (double)(from SecondSerie in newPairCollection.AsQueryable()
                                      where (SecondSerie.Second != null)
                                      select SecondSerie.Second).Max();
   
  
     // Add Axes
     if(ECChartElement.Axes != null)
         ECChartElement.Axes.Clear();
  
     rangeX = Math.Abs(firstMax - firstMin);
     rangeY = Math.Abs(secondMax - secondMin);
     
     IAxis myAxisX = new LinearAxis
     {
         Orientation = AxisOrientation.X,
         Minimum = firstMin - rangeX / 10,
         Maximum = firstMax + rangeX / 10,
         ShowGridLines = true
     };
     ECChartElement.Axes.Add(myAxisX);
   
     IAxis myAxisY = new LinearAxis
     {
         Orientation = AxisOrientation.Y,
         Minimum = secondMin - rangeY / 10,
        Maximum = secondMax + rangeY / 10,
         ShowGridLines = true
     };
     ECChartElement.Axes.Add(myAxisY);
  
  
     LineSeries mySerie = new LineSeries();
     mySerie.Name = dependentVariable + independentVariable;
     mySerie.Title = "";
  
     mySerie.ItemsSource = newPairCollection;
     mySerie.IndependentValueBinding = new Binding(independentVariable);
     mySerie.DependentValueBinding = new Binding(dependentVariable);
  
     ECChartElement.Series.Clear();
     ECChartElement.Series.Add(mySerie);
  
     ECChartElement.Title = "Elevation profile";
     ECChartElement.LegendTitle = "";
     ECChartElement.DataContext = newPairCollection;
  
     mySerie.Loaded += new RoutedEventHandler(mainSerie_Loaded);
  
 }

Selecting portions of the graph

The graph offers some kind of interactivity by means of two markers that can be moved over the points. The area between them is highlighted in green and some statistical data related to the area are shown in a small box on the left top corner of the graph. To create the highlighted effect of the area selected I used a Polygon primitive ( ECAreaSelectedElement in the code snippet below).

 private void DrawAreaSelected()
 {
  
     ECAreaSelectedElement.Points.Clear();
  
     LineSeries mainLS = (LineSeries)ECChartElement.Series[0];
     if (mainLS == null)
         return;
  
     double firstMarkerY = (double)ECFirstDelimiterElement.GetValue(Canvas.TopProperty) + ECFirstDelimiterElement.Height / 2;
     double firstMarkerX = (double)ECFirstDelimiterElement.GetValue(Canvas.LeftProperty) + ECFirstDelimiterElement.Width / 2;
  
     double secondMarkerY = (double)ECSecondDelimiterElement.GetValue(Canvas.TopProperty) + ECSecondDelimiterElement.Height / 2;
     double secondMarkerX = (double)ECSecondDelimiterElement.GetValue(Canvas.LeftProperty) + ECSecondDelimiterElement.Width / 2;
   
     double stepX = ECFirstDelimiterElement.Height / 2;
     double stepY = ECFirstDelimiterElement.Width / 2;
   
     List<Point> avgPoints = new List<Point>();
      
     int objCounter = 0;
     foreach (Point lspt in mainLS.Points)
     {
         Point asPt = FromLineSeriesToCanvasCoord(lspt);
         Point asPtbase = new Point(asPt.X, originOfLSInCanvasSpace.Y + pixelExtentY);
  
         if ((Math.Abs(asPt.X - firstMarkerX) <= (stepX)))
         {
             ECAreaSelectedElement.Points.Add(asPtbase);
             ECAreaSelectedElement.Points.Add(asPt);
  
             avgPoints.Add(GetPointValuesTroughReflection(objCounter));
         }
         if ((asPt.X - firstMarkerX) > stepX && (asPt.X - secondMarkerX) < -stepX)
         {
             ECAreaSelectedElement.Points.Add(asPt);
             avgPoints.Add(GetPointValuesTroughReflection(objCounter));
         }
  
         if ((Math.Abs(asPt.X - secondMarkerX) <= (stepX)))
         {
             ECAreaSelectedElement.Points.Add(asPt);
             ECAreaSelectedElement.Points.Add(asPtbase);
             avgPoints.Add(GetPointValuesTroughReflection(objCounter));
         }
         objCounter += 1;
     }
     ECAreaSelectedElement.Visibility = Visibility.Visible;
  
     double avg = (double)(from P in avgPoints.AsQueryable()
                           select P.Y).Average();
  
     double distance = avgPoints[avgPoints.Count - 1].X - avgPoints[0].X;
  
     double drop = avgPoints[avgPoints.Count - 1].Y - avgPoints[0].Y;
     double slope = drop / (distance * 1000.0) * 100.0;
         
  
     ECAvgValueElement.Text = "Avg elevation = " + avg.ToString("F2") + " m\n";
     ECAvgValueElement.Text += "Tot distance = " + distance.ToString("F2") + " km\n";
     ECAvgValueElement.Text += "Avg drop = " + drop.ToString("F2") + " m\n";
     ECAvgValueElement.Text += "Avg slope = " + slope.ToString("F2") + " %";
     ECAvgBorderElement.SetValue(Canvas.TopProperty, 6.0);
     ECAvgBorderElement.SetValue(Canvas.LeftProperty, 2.0);
 }

The basic concept is really simple; the X and Y coordinates of the points of the LineSeries are compared with those of the markers, each point included in the range of the markers is added to the Polygon. To make the polygon appear as an area, two other points are added: one at the beginning on the left and the other at the end on the right side, both of them on the X Axe and with an Y value equal to, respectively, the first and last point.

Summary

In this second part we have carried on the description of the application anticipated in the first article where we spoke of extending the Bing Maps with an elevation profile. In particular, we have given detailed indications about the main free resources for getting elevation data, then we have described a possible solution via threads to get the elevation data allowing the user to interact with the application in the meantime. Finally we briefly described how the graph is created and how the functionality of selection was added. The improvements that can be done are adding right mouse inputs handling and allowing the zooming on the chart as I had already done in my previous article “An excel file viewer in silverlight”.