Silverlight in SharePoint: The Silverlight Client Object Model
- Silverlight in SharePoint: Overview
- Silverlight in SharePoint – Basics of Hello World!
- Silverlight in SharePoint: The Silverlight Client Object Model
- Silverlight in SharePoint: Developing Your Own Silverlight Web Part
In this third part of the series, I will build a Silverlight application that communicates with SharePoint to render data that is stored within a SharePoint list. The Silverlight app will again be hosted within the out-of-the-box Silverlight web part that comes with SharePoint 2010.
Through this series of articles we will build a SharePoint demo application that allows members to offer their services and accept another service in return. For example, John likes to garden, but he is lousy in the kitchen. Through the application he can offer his services as a gardener. Hanne likes to cook but needs a hand in the garden. She can accept his offer and cook him a delicious meal. All data will be stored in a SharePoint list called Service Offering.

In this part of the series we will build a Silverlight application that renders a scrolling banner displaying the services offered that day. Because the offered services are stored in a SharePoint list, they need to be retrieved by the Silverlight application before they can be displayed in the scrolling banner. This can be achieved by using the Silverlight client object model.
You can download the complete source code here.
The Silverlight Banner
You can create a simple Silverlight 4 application using Visual Studio 2010. If you need a more complex design, you will need tools like Microsoft Expression Blend, but the sample Silverlight application for this article can easily be built in Visual Studio 2010 using the Silverlight Application template.
The Silverlight application consists of a canvas with a panel. For each service offer, a control like the following will be populated with the information about who offers what and which services are desired in return. Some services need to be fulfilled within a certain time frame; therefore, a countdown control will indicate how much time is left. All service controls will be added to the panel and will scroll over the page. If members see an interesting offer, they can check which they want to fulfill.
I’m not going to dive into the XAML of the Silverlight application, because that is not the subject of this article, but I’m going to walk you through the code where interaction with SharePoint is needed.
The point of entry of the Silverlight application is the Application_Startup method. The StartupEventArgs argument contains information on how to initialize the Silverlight application. One of these elements is the InitParams dictionary. In this sample we will pass the name of the SharePoint list where all service offerings will be stored. The value will be stored in a static variable that can be consulted from within the different controls that make up the Silverlight application.
public static string ListName = null;
The Application_Startup method looks as follows:
private void Application_Startup( object sender, StartupEventArgs e) { if (e.InitParams != null) { if (e.InitParams.ContainsKey("list") && !string.IsNullOrEmpty(e.InitParams["list"])) { ListName = e.InitParams["list"].Replace("%20", " "); } } this.RootVisual = new MainPage(); }
The last statement initializes the main user control of the Silverlight application. The XAML of this control is very simple and contains the following elements:
- A Canvas: This is the root control of the user control.
- A Storyboard: This element is responsible for making the service offerings scroll over the screen from right to left.
- A StackPanel: This element will be used to add a custom control (as shown in the picture) for each service offering.
- A status border: This has a start/pause button to start/pause the storyboard.
The code-behind of the MainPage user control is stored in the MainPage.xaml.cs file and populates the ServiceItemPanel stack panel by calling the Silverlight client object model for SharePoint and binding the list item to a separate ServiceItemControl, which is displayed here.
picture
Using the Silverlight Client Object Model
SharePoint 2010 comes with three different client object models:
- One for use within .NET applications like a WPF application
- One for use from within JavaScript
- One for use from within Silverlight applications
Before you can start using the Silverlight client object model for SharePoint, you have to reference two assemblies:
- Microsoft.SharePoint.Client.Silverlight.dll
- Microsoft.SharePoint.Client.Silverlight.Runtime.dll
Both assemblies are located in the 14\TEMPLATE\LAYOUTS\ClientBin folder of the SharePoint root.
Calls to the SharePoint server using the Silverlight client object model execute asynchronously and on a different thread. Therefore, you first have to capture the current SynchronizationContext to make sure you can come back on the UI thread.
public MainPage() { InitializeComponent(); ResizeCanvas(); this.SizeChanged += new SizeChangedEventHandler(MainPage_SizeChanged); uiThread = System.Threading.SynchronizationContext.Current; if (uiThread == null) uiThread = new System.Threading.SynchronizationContext(); PopulateBanner(); }
The uiThread variable is declared as a class-level variable of type SynchronizationContext. This class resides in the System.Threading namespace:
private System.Threading.SynchronizationContext uiThread;
The client context is the starting point for the communication between Silverlight and SharePoint and is located in the Microsoft.SharePoint.Client namespace. When you host your Silverlight applications within SharePoint artifacts, you can use ClientContext.Current, instead of passing in the URL to the SharePoint site and creating a ClientContext based on that URL. This will be explained later when hosting the Silverlight application within the out-of-the-box Silverlight web part.
The code contains a property for the client context in order to encapsulate code that verifies the existence of a client context.
private ClientContext clientContext; private ClientContext ClientCtxt { get { if (clientContext == null) clientContext = ClientContext.Current; if (clientContext == null) throw new Exception("Connection failed!"); return clientContext; } }
The PopulateBanner method retrieves the list items from the SharePoint list. Before you can start retrieving list data, you have to load the current web from the client context. Also, the current user is retrieved for later use. The purpose will become clearer later in the article.
web = ClientCtxt.Web; ClientCtxt.Load(web); currentUser = ClientCtxt.Web.CurrentUser; ClientCtxt.Load(currentUser);
Once the web is loaded, the list is retrieved using the GetByTitle method of the client object model using the list name passed in when the Silverlight application is initialized. This list contains all services offered by members of the site. The GetByTitle method is available on the Lists collection of the Web object of the current client context.
serviceList = ClientCtxt.Web.Lists.GetByTitle(App.ListName);
The list items are retrieved by means of a CamlQuery object. The ViewXml property can be used to specify a sort order and/or a filter criteria. In this sample only, the list items with a start date before today and no expiration date or an expiration date after today are retrieved.
camlQuery.ViewXml = "<View><Query>" + "<OrderBy>" + " <FieldRef Name='PublishDate' Ascending='False' />" + " <FieldRef Name='Title' />" + "</OrderBy>" + "<Where>" + " <And>" + " <Leq><FieldRef Name='ServicePublishDate'/>" + " <Value Type='DateTime' IncludeTimeValue='FALSE'><Today /></Value>" + " </Leq>" + " <Or>" + " <Geq><FieldRef Name='ServiceExpirationDate' />" + " <Value Type='DateTime' IncludeTimeValue='FALSE'><Today /></Value>" + " </Geq>" + " <IsNull><FieldRef Name='ServiceExpirationDate' /></IsNull>" + " </Or>" + " </And>" + "</Where>" + "</Query></View>";
The GetItems method is then executed using the CamlQuery object. The returned object is of type ListItemCollection:
listItems = newsList.GetItems(camlQuery);
To actually retrieve the list items, you have to execute the Load method on the client context. Together with the Load method, you have to specify which columns of the list items you want to see returned. This allows you to limit the columns returned over the wire. In this sample, columns like the title, the short description, the publish date, the expiration date, and so on, are retrieved because these are the columns that will be displayed within the service item control.
ClientCtxt.Load(listItems, items => items.Include( item => item["Title"], item => item["ServiceShortDescription"], item => item["ServicePublishDate"], item => item["ServiceExpirationDate"], item => item["ServicesInReturn"], item => item["Author"], item => item["ServiceTaker"], item => item["SelectedService"] ));
The ExecuteQueryAsync method of the client context is responsible for starting the call to the server. Because this method runs asynchronously, you have to specify two methods on which the call can come back when it returns from the server: one method for when the call returns successfully and another method for when the call to the server fails.
ClientCtxt.ExecuteQueryAsync(HandleClientRequestSucceeded, HandleClientRequestFailed);
The HandleClientRequestFailed method will be executed when the call to the server fails. The EventArgs argument contains an error message that is temporarily stored in a private string message. Then the Post method on the main UI thread is called to redirect the request to the OperationFailed method on the main UI thread. There a message box will be displayed rendering the error message.
private string errorMessage; private void HandleClientRequestFailed(object sender, ClientRequestFailedEventArgs e) { errorMessage = e.Message; uiThread.Post(new System.Threading.SendOrPostCallback(delegate(object state) { EventHandler h = OperationFailed; if (h != null) h(this, EventArgs.Empty); }), null); } public void OperationFailed(object sender, EventArgs e) { MessageBox.Show(string.Format("Failure! /n{0}", errorMessage)); }
The HandleClientRequestSucceeded method will be executed when the call to the server returns successfully. Also, here the Post method on the main UI thread is used to redirect the request back to the main UI thread. The OperationSucceeded method is called to handle the result:
private void HandleClientRequestSucceeded(object sender, ClientRequestSucceededEventArgs e) { uiThread.Post(new System.Threading.SendOrPostCallback(delegate(object state) { EventHandler h = OperationSucceeded; if (h != null) h(this, EventArgs.Empty); }), null); }
The OperationSucceeded method calls the BuildBanner method, which loops through the list items collection returned by the server.
public void OperationSucceeded(object sender, EventArgs e) { BuildBanner(); }
The BuildBanner Method
The BuildBanner method loops through the list items collection, which got populated by executing the ExecuteQueryAsync method of the client context. For each list item in this collection, a Silverlight user control is instantiated, setting its DataContext property to the list item.
private void BuildBanner() { // Load the services banner foreach (ListItem item in listItems) { ServiceItemControl itemControl = new ServiceItemControl(); itemControl.DataContext = item; // the other code comes hereafter } }
Data like ServicesInReturn is passed to the item control through a property. This is because the data is stored as a #;-separated string and some extra code needs to be executed before it can be rendered as check boxes.
// pass the services in return if (item["ServicesInReturn"] != null) itemControl.ServicesInReturn = GetServicesInReturn(item["ServicesInReturn"].ToString());
The ExpirationDate is also passed through a property because it needs to be passed to the countdown control on the item control. We also pass an empty expiration date because in that case the countdown control will not be displayed.
// pass the expiration date DateTime? expirationDateTime = null; if (item["ServiceExpirationDate"] != null) expirationDateTime = System.Convert.ToDateTime(item["ServiceExpirationDate"]); itemControl.ExpirationDateTime = expirationDateTime;
The username of the current user is also passed through a property. When the services offered scroll over the screen, the current user can select one of the services asked for in return. At that time, the SharePoint list item that corresponds with the item control will be updated with the name of the current user and the selected service in return.
// pass the name of the current user itemControl.UserName = currentUser.LoginName;
When the service is already honored by a user, the selected service in return is passed through a property, and the corresponding check box will be checked.
if (item["SelectedService"] != null) itemControl.SelectedService = item["SelectedService"].ToString();
A custom event handler is added to the item control. When a user decides to accept a service and offer a service in return, this choice should be stored in SharePoint. I could have passed the client context to the item control, but I prefer to raise an event and handle all SharePoint communication from within MainPage.xaml.cs:
itemControl.ServiceSelected += new ServiceSelectedHandler(itemControl_ServiceSelected);
Then the control is added to the ServiceItemPanel stack panel control of the Silverlight application.
ServiceItemPanel.Children.Add(itemControl);
When all service item controls are created, the size of the stack panel and the status borders need to be recalculated based on the width of one single service item control and the number of list items. The To and From properties of a DoubleAnimation storyboard indicate from where to where a certain control is moved. To avoid that the banner scrolls as fast as light when a lot of services are offered, the duration of the DoubleAnimation is recalculated. Then the storyboard is started.
The ServiceItemControl
The ServiceItemControl is a Silverlight user control and looks like the following:

The DataContext property of this user control is used for data binding purposes. As explained in the previous section, when an instance of this control is created, a SharePoint list item is passed to this property. The properties of the incoming list item are data bound in XAML. Strings and other primitive data types can easily be bound to Silverlight controls. For example, the service offered, which is stored in the Title property of a list item, is data bound as follows:
<TextBlock x:Name="TitleTextBlock" Margin="3,0,0,0" Foreground="Yellow" FontFamily="Comic Sans MS" FontSize="14" Text="{Binding [Title]}" TextAlignment="Left" Width="160" />
But some properties have a more complex data type like, for example a user. In that case, we need to define a data-binding expression. The following code snippet shows the TextBlock that is used to bind the username of the demander by using a ListItemConverter. The ConverterParameter defines to which list item property you want to bind:
<TextBlock x:Name="DemanderTextBlock" Foreground="Yellow" FontFamily="Comic Sans MS" FontSize="14" Text="{Binding Converter={StaticResource listItemConverter}, ConverterParameter='Author'}" />
The ListItemConverter is a class that inherits from IValueConverter. The Convert method accepts the value, the data type, and the name of the column. If the column is of type Text or Number, the value will be returned without conversion. If the incoming data is of type user, then the incoming value is cast to the type FieldUserValue. The LookupValue property contains the username and is returned for display. If the incoming column is of type URL, then the incoming value is cast to the type FieldUrlValue, and the Url property is returned for display.
The ListItemConverter class in this sample does not convert all types of columns and contains only the conversions necessary in this sample. It looks as follows:
public class ListItemConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { // get the list item's field to be displayed string fieldToDisplay = parameter as string; if (string.IsNullOrEmpty(fieldToDisplay)) fieldToDisplay = "Title"; ListItem sourceListItem = value as ListItem; if (sourceListItem == null) throw new ArgumentException("value"); if (fieldToDisplay.StartsWith("Lookup.")) { string lookupFieldName = fieldToDisplay.Replace("Lookup.", string.Empty); FieldLookupValue lookupValue = sourceListItem[lookupFieldName] as FieldLookupValue; if (lookupValue != null) return lookupValue.LookupValue; else throw new ArgumentException("Invalid lookup field."); } else if (sourceListItem[fieldToDisplay] is FieldUrlValue) { FieldUrlValue urlValue = sourceListItem[fieldToDisplay] as FieldUrlValue; if (urlValue != null) return urlValue.Url; else throw new ArgumentNullException("Invalid URL field."); } else if (sourceListItem[fieldToDisplay] is FieldUserValue) { FieldUserValue userValue = sourceListItem[fieldToDisplay] as FieldUserValue; if (userValue != null) return userValue.LookupValue; else throw new ArgumentNullException("Invalid User field."); } else { // return what's being asked for return sourceListItem[fieldToDisplay]; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }
To be able to use the ListItemConverter from within Silverlight user controls, you have to add an extra element to the Resources element of the user control:
<UserControl.Resources> <local:ListItemConverter x:Key="listItemConverter" /> </UserControl.Resources>
As a side node, in Silverlight 3 you had to add all SharePoint data types, but since Silverlight 4 you can data bind list item properties using square brackets.
The code behind the ServiceItemControls defines a delegate and an event handler that is raised when a user selects one of the checkboxes populated with the services wanted in return.
public delegate void ServiceSelectedHandler(ListItem item, string service); public partial class ServiceItemControl : UserControl { public event ServiceSelectedHandler ServiceSelected; // rest of the code omitted for brevity … }
Some list item properties cannot be data bound using the list item converter. They are handled in the code-behind. For example, the ServicesInReturn property contains a semicolon-separated string and needs to be rendered as a list of checkboxes. The PopulateCheckBoxPanel method creates a checkbox for each string in the list of services. Each CheckBox has a TextBox with its Text property set to the service. To each checkbox the custom event handler is added.
private void PopulateCheckBoxPanel() { ReturnStackPanel.Children.Clear(); if (servicesInReturn != null) { foreach (string service in servicesInReturn) { CheckBox chkbox = new CheckBox(); chkbox.Foreground = new SolidColorBrush(Colors.White); chkbox.FontFamily = new FontFamily("Comic Sans MS"); chkbox.FontSize = 12.0; chkbox.Width = 220.0; // use a textblock to be able to wrap the text //chkbox.Content = s; TextBlock textblock = new TextBlock(); textblock.Text = service; textblock.Width = 195.0; textblock.TextWrapping = TextWrapping.Wrap; chkbox.Content = textblock; chkbox.Checked += new RoutedEventHandler(checkbox_Checked); ReturnStackPanel.Children.Add(chkbox); } } }
I could have done this differently by creating a list box with a data template containing a checkbox. In that case I would have been able to bind the list of services to the list box using the ItemsSource property.
The ExpirationDateTime property handles the countdown control. If there is no expiration date, the control is hidden. When a service is already offered in return, the SetSelectedService method displays a “Served!” label over the list of checkboxes. When a service offer is not yet honored, a user can click one of the services offered in return, which fires the event handler checkbox_Checked to place the label “Served!” over the checkboxes. This label is wrapped in its own Silverlight user control.
The label is moved smoothly over the list of services using a storyboard. The event handler also raises another custom event: the ServiceSelected event. It returns the impacted list item and the selected service. This ServiceSelected event is caught by the MainPage user control, which will save the information to SharePoint. Also, the update executes asynchronously and on a different thread.
private void itemControl_ServiceSelected(ListItem item, string service) { // Save to SharePoint item["SelectedService"] = service; item["ServiceTaker"] = ClientCtxt.Web.CurrentUser; item.Update(); serverAction = ServerAction.UpdateService; clientContext.ExecuteQueryAsync(HandleClientRequestSucceeded, HandleClientRequestFailed); }
In this sample, the ExecuteQueryAsync method points to the same success and failed handlers. Therefore, I added an enumeration to the code indicating which server action is taking place. In the HandleClientRequestSucceeded event handler, I test upon the value of this enum variable to decide what to do. Only when the services are retrieved on initialization of the Silverlight application is the scrolling banner built up.
public void OperationSucceeded(object sender, EventArgs e) { switch (serverAction) { case ServerAction.RetrieveServices: BuildBanner(); break; default: break; } }
That’s it for the code in the Silverlight application. The compiled Silverlight application is a file with the extension .xap. It is in fact a package, which you can rename with the .zip extension and take a look at what’s inside. You’ll see that both assemblies for the Silverlight client object model are packaged within the .xap file. The Silverlight application also contains an assembly containing the compiled code.

When extracting the AppManifest.xaml file from the package, you’ll notice the entry point is App.xaml. It also contains a list of all assemblies that make up the Silverlight application.
<Deployment xmlns="<a href="http://schemas.microsoft.com/client/2007/deployment">http://schemas.microsoft.com/client/2007/deployment</a>" xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>" EntryPointAssembly="Sample1.ServicesBanner" EntryPointType="ServicesBanner.App" RuntimeVersion="4.0.50401.0"> <Deployment.Parts> <AssemblyPart x:Name="Sample1.ServicesBanner" Source="Sample1.ServicesBanner.dll" /> <AssemblyPart x:Name="Microsoft.SharePoint.Client.Silverlight" Source="Microsoft.SharePoint.Client.Silverlight.dll" /> <AssemblyPart x:Name="Microsoft.SharePoint.Client.Silverlight.Runtime" Source="Microsoft.SharePoint.Client.Silverlight.Runtime.dll" /> </Deployment.Parts> </Deployment>
Now it is time to deploy the Silverlight application to SharePoint. I upload the Silverlight application to the XAPS document library. I place a Silverlight web part on a web part page and specify the path to the Silverlight application.
In the editor part of the web part, you have to enter the initParams string requested by your Silverlight application. In this sample, that means passing the name of the list where the service offerings are stored.

Once the initialization parameters, you can click the OK button of the web part. The Silverlight application will be rendered on the page.

Conclusion
In this article, I explained how to use the Silverlight client object model to retrieve and update data stored in SharePoint. The Silverlight application is hosted in the out-of-the-box Silverlight web part. In the next article, I will explain how you can build your own Silverlight web part using the Visual Web Part item template available in Visual Studio 2010.


July 26, 2011 







About the Author
No comments yet... Be the first to leave a reply!