Using Silverlight and COM for SharePoint Server 2010 Data Access (Wrox)

Summary: Learn how to build Microsoft Silverlight applications for Microsoft SharePoint 2010 by using the client object model. Specifically, learn how to use Silverlight to load, edit, and update SharePoint data through the client object model API.

Wrox logo

Wrox SharePoint Books

Applies to: Business Connectivity Services | Open XML | SharePoint | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Author: Ryan Morgan

Editors: WROX Tech Editors for SharePoint 2010 Articles

Contents

  • Introduction to Using Silverlight and COM for SharePoint 2010 Data Access

  • Introduction to the Client Object Model

  • Retrieving Data by using the Client Object Model

  • Transforming Data to Object Properties

  • Binding a View to the ViewModel

  • Submitting Changes to Data

  • About the Author

  • Additional Resources

The accompanying code example provides a working example of how to implement a basic Model View ViewModel project to manage SharePoint 2010 data. It also includes code that you can use in similar projects to provide undo functionality.

Download the code

Introduction to Using Silverlight and COM for SharePoint 2010 Data Access

This article uses the following scenario to explain how to build Silverlight applications in SharePoint 2010 by using the client object model.

The Adventure Works marketing department managed its assets through a SharePoint team site. The team decided to improve the user experience and provide a rich user interface. Now, they use Silverlight to browse and administer approved graphical assets for their products. Marketing department members use SharePoint workflows and a tasks list to create requests for new assets from the graphic design team. When the graphic design team submits the asset, the contents of the asset are reviewed first by the marketing team, and then by the legal team before the assets are made available to the broader company as approved versions. A simple sequential workflow built in Visual Studio marks the asset as approved after the legal and marketing approval dates are set. The example in this article has a view and edit mode for assets that are in the assets list, and manages the items in this list by using the client object model API.

Before you read this article, you should have a basic understanding of how the ClientContext object loads data and how to submit changes to data.

Introduction to the Client Object Model

The client object model API is new to SharePoint 2010. It enables you to access and manage the sites and content of SharePoint 2010 applications. Unlike earlier versions of SharePoint, knowledge of the SOAP web services is not required to build an application because the library abstracts all calls to the WCF service by using a strongly typed API that works very much like the server object model. By using this API, you can work against a remote SharePoint instance and use a style that is familiar if you have developed custom Web Parts or services in SharePoint.

Retrieving Data by using the Client Object Model

If you have experience using the server object model for SharePoint development, many of the namespaces and operations will look familiar. Table 1 shows several SharePoint API namespaces and their corresponding namespaces in the managed client object model.

Table 1.

Server Object Model

Client Object Model

SPContext

ClientContext

SPSite

Site

SPWeb

Web

SPUser

User

SPList

List

SPListItem

ListItem

SPField

Field

The main thing to remember when you move from server object model programming to developing Silverlight applications with the client object model, is that Silverlight runs on the client. This simple fact translates into the following key differences:

  • The client object model does not have the concept of the current context that SPContext.Current provides. The ClientContext object is instantiated with a parameter of the SharePoint Web URI to which it should connect.

  • In Silverlight, all calls to the server are asynchronous. Regardless of whether the ClientContext.Load method is called, the data cannot be accessed until the ExecuteQueryAsync method is called. In the following example, none of the objects or queries that are loaded are available until the OnQuerySucceeded method is called. The OnQuerySucceeded method is registered as the callback handler for a successful query when the asynchronous call returns from the ExecuteQueryAsync method.

  • There is no way to run code with elevated privileges.

The ClientContext object is the mechanism through which all access to the SharePoint server data is provided. The ClientContext object tracks the state of ListItems, provides batch commanding for calls to SharePoint, and enables all calls to the client object model API.

The following code example shows how to load the contents of a list by using C# with the client object model. For brevity, error handling is not explicitly shown, and errors during the query are thrown to the default error handling in the Silverlight project. For best practices on cross-cutting concerns like logging and exception handling, see the Enterprise Library for Silverlight.

public HomeViewModel()
{
// First - instantiate the Assets Web.
// Note that SharePointSiteURI resolves to "http://sl2sp.local/".
AssetsContext = new ClientContext(AppViewModel.Instance.SharePointSiteURI); 
            
// Queue the call to load the web. 
AssetsContext.Load(AssetsContext.Web);

// Build the CAML query used to select the data. 
// In this case, simply select all fields. 
CamlQuery qry = new CamlQuery();
string strQuery = "<View/>";
qry.ViewXml = strQuery;

// Query the list by loading a query for the ProductAssets list. 
ProductAssetsListItems = AssetsContext.Web.Lists.GetByTitle("ProductAssets").GetItems(qry);

// Mark the query for loading by the AssetsContext. 
AssetsContext.Load(ProductAssetsListItems);

// Run the query, and assign the return method.
AssetsContext.ExecuteQueryAsync(onQuerySucceeded, onQueryFailed);
}

private void onQuerySucceeded(object sender, ClientRequestSucceededEventArgs args)
{
// Because the AssetsCount property is bound in the UI, the Dispatcher 
//  for the UI thread should be used when assigning a value to it. 
AppViewModel.Instance.AppDispatcher.BeginInvoke(
    () =>
    {                    
        AssetsCount = ProductAssetsListItems.Count; 
    }
    );
}

private void onQueryFailed(object sender, ClientRequestFailedEventArgs args)
{
    //Bubbles the error message up. This will
    // be caught by the unhandled exception handler in App.XAML.
    throw new Exception(string.Format("Query failed  with message {0}", args.Message));
}

Although it is simple, the preceding example illustrates many of the main points described earlier. The ClientContext object is loaded with the URL to the site for which it will operate, and an asynchronous callback method operates on the return value after the query has finished running. If any code tries to access the properties of the Web object or the List object before the ExecuteQueryAsync call is made, a NotInitialized exception is raised.

Transforming Data to Object Properties

Now that you understand the basics of ClientContext and how to access data from SharePoint, you can continue with the marketing example. Figure 1 shows the interface that the team designed to manage products.

Figure 1. Product Asset Management Interface

Product Asset Management Interface

List and Library data that is returned from the client object model API comes in the form of a weakly-typed dictionary. To take advantage of the powerful binding model that is available in Silverlight applications, developers must manually build the objects and properties to represent the data from SharePoint lists in the Silverlight user interface. The interface is built to edit the details about a product asset. To surface that data as strongly-typed objects that can be bound to controls in the view, the application requires the following objects:

  • An object to represent the lookup list for categories.

  • An object to represent the lookup list of products.

  • An object to represent the product assets that the application manages.

Because each of the preceding objects represents a SharePoint ListItem object, you must first create a base class that all three objects can inherit from. Consider the following definition for the SharePointEntityBase class.

public class SharePointEntityBase : ObservableObjectBase
{
public ListItem SourceItem { get; set; }
public int? Id
{
get
{
if (SourceItem != null)
    return SourceItem.Id;
else
    return null;
}
}
public void UpdateSourceItem(string fieldName, object value)
{
if (!IsLoading)
{
if (SourceItem != null)
{
    SourceItem[fieldName] = value;
}
HasChanges = true;
}
} 
}

First, the class inherits from a base class ObservableObjectBase, which implements the INotifyPropertyChanged interface. This class provides the functionality to raise PropertyChanged events that empower binding (see the downloadable source code). The properties in the base class manage the ListItem object to which the derived classes are attached. The SourceItem property keeps the ListItem instance that was used to load the object from SharePoint. Because the ClientContext object keeps track of the state of a ListItem object, keeping a reference to ListItem enables the objects that inherit from it to automatically update the underlying field in the ListItem object.

Now that the base class is ready, the next step is to build the class to hold the ProductAsset properties. Consider the following definition for the ProductAsset class.

public class ProductAsset : SharePointEntityBase
{
public string FileName
{
get { return _FileName; }
private set
{
    _FileName = value;
    NotifyPropertyChanged(_FileNameChangedEventArgs);
}
}
private string _FileName;
private PropertyChangedEventArgs _FileNameChangedEventArgs = new PropertyChangedEventArgs("FileName");

public string FileType
{
get { return _FileType; }
private set
{
    _FileType = value;
    NotifyPropertyChanged(_FileTypeChangedEventArgs);
}
}
private string _FileType;
private PropertyChangedEventArgs _FileTypeChangedEventArgs = new PropertyChangedEventArgs("FileType");

public Product AssociatedProduct
{
get { return _AssociatedProduct; }
set
{
    _AssociatedProduct = value;
    NotifyPropertyChanged(_AssociatedProductChangedEventArgs);
    if (value != null)
        UpdateSourceItem("Product", value.Id);
    else
        UpdateSourceItem("Product", null);
}
}
private Product _AssociatedProduct;
private PropertyChangedEventArgs _AssociatedProductChangedEventArgs = new PropertyChangedEventArgs("AssociatedProduct");

public DateTime? LegalApprovedDate
{
get { return _LegalApprovedDate; }
set
{
    _LegalApprovedDate = value;
    NotifyPropertyChanged(_LegalApprovedDateChangedEventArgs);
    UpdateSourceItem("LegalApprovedDate", value);
}
}
private DateTime? _LegalApprovedDate;
private PropertyChangedEventArgs _LegalApprovedDateChangedEventArgs = new PropertyChangedEventArgs("LegalApprovedDate");

public DateTime? MarketingApprovedDate
{
get { return _MarketingApprovedDate; }
set
{
    _MarketingApprovedDate = value;
    NotifyPropertyChanged(_MarketingApprovedDateChangedEventArgs);
    UpdateSourceItem("MarketingApprovedDate", value);
}
}
private DateTime? _MarketingApprovedDate;
private PropertyChangedEventArgs _MarketingApprovedDateChangedEventArgs = new PropertyChangedEventArgs("MarketingApprovedDate");
}

For editable properties, a call is available to update the source item in the setter for the property. This call updates the underlying ListItem object. Although excluded for brevity here, an ‘undo’ pattern is also implemented in the setter for the editable properties (see in the downloadable source code). Notice that with the FieldLookupValue object, shown in the next code section, the code sets the value to the underlying Id value. The client object model API uses that Id value to correctly create the link to the associated product as a look-up field.

The last step that is required to transform the ListItem objects to strongly-typed objects is to build the logic to hydrate the properties. For a larger application, this would be a good use of reflection, and could easily be achieved by storing the FieldName property from SharePoint in a custom attribute that decorates each property. However, because this article focuses on explaining how the API works, the following example provides a simpler version that hydrates the entity in the constructor.

public ProductAsset(ListItem source, IEnumerable<Product> products)
{
IsLoading = true;
SourceItem = source;

if (SourceItem != null && SourceItem["FileRef"] != null)
    FileName = SourceItem["FileRef"].ToString();

if (SourceItem != null && SourceItem["File_x0020_Type"] != null)
    FileType = SourceItem["File_x0020_Type"].ToString();

if (SourceItem != null && SourceItem["LegalApprovedDate"] != null)
    LegalApprovedDate = (DateTime)SourceItem["LegalApprovedDate"];

if (SourceItem != null && SourceItem["MarketingApprovedDate"] != null)
    MarketingApprovedDate = (DateTime)SourceItem["MarketingApprovedDate"];

if (SourceItem != null && SourceItem["Product"] != null && products != null)
{
    FieldLookupValue productValue = (FieldLookupValue)SourceItem["Product"];
    AssociatedProduct = products.Where(p => p.Id == productValue.LookupId).FirstOrDefault();
}

IsLoading = false;
}

The constructor for the class takes two parameters, the ListItem object to which the item should be associated, and an IEnumerable list of products. Using the fields for each property in ProductAsset, go through and set the public property to the value from the ListItem object. To use the ability of Silverlight to bind a selected value directly to an object, the lookup uses a LINQ to Objects query to select the matching product from a referenced list. The object is selected by using the LookupId property, which is found in the FieldLookupValue object that is returned in the ListItem object. Reviewing the setter for the Product property in the ProductAsset class, the update call for the SourceItem property probably makes more sense now. This class abstracts the underlying ListItem object and updates the Id property from the source of the related product. This abstraction removes the need for the ViewModel to update the underlying SharePoint objects when the ListItem fields change. As long as you use two-way binding, any edits to the user interface automatically update the ListItem fields so that it can make the update call to SharePoint.

Binding a View to the ViewModel

Consider the layout of the list and details view from Figure 1. The interface has two areas, the list of product assets at the bottom, represented as a grid, and a set of editable form fields at the top. When an item is selected in the bottom grid, the fields at the top are bound automatically to provide an edit form for the selected item.

The ViewProductAssetsViewModel serves the view in Figure 1. In the XAML, the ViewModel is added as a StaticResource markup extension for the View. This calls the constructor and creates an instance of the ViewModel to which the View can bind. The following code shows the ViewModel constructor.

<UserControl.Resources>
<ViewModels:ViewProductAssetsViewModel x:Key="ViewProductAssetsViewModelResource"></ViewModels:ViewProductAssetsViewModel>
</UserControl.Resources>

Now that a ViewModel instance is created and added to the resources of the control, the following markup sets the DataContext property to the instance of the class.

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewProductAssetsViewModelResource}}">
...

By setting the DataContext property of the parent object to the instance of the class that was created in the Resources object for the user control, any dependency property of an element in the View element can now be bound to a property in the ViewModel. The following markup defines the edit form for the ProductAsset object.

<TextBlock Text="Associated Product"></TextBlock>
<ComboBox ItemsSource="{Binding Path=Products}"
SelectedItem="{Binding SelectedProductAsset.AssociatedProduct, Mode=TwoWay}"></ComboBox>

<TextBlock Text="Marketing Approved Date"></TextBlock>
<sdk:DatePicker SelectedDate="{Binding SelectedProductAsset.MarketingApprovedDate, Mode=TwoWay}"/>

<TextBlock Text="Legal Approved Date"></TextBlock>
<sdk:DatePicker SelectedDate="{Binding SelectedProductAsset.LegalApprovedDate, Mode=TwoWay}" />

<Button Content="Save" Command="{Binding SaveCommand}"></Button>
<Button Content="Cancel" Command="{Binding CancelCommand}"></Button>

The control’s editable values are two-way bound to the SelectedProductAsset property of the ViewModel element that is set by selecting an item in the DataGrid object. Because the binding mode is set to TwoWay, any edits to the value in the DatePicker or the ComboBox directly set the property of the selected ProductAsset object. By calling the setter of those properties, the underlying ListItem fields are updated by using the pattern shown earlier in this article.

Submitting Changes to Data

To finish the edit form for the product assets, a save command must submit any changes back to SharePoint. Now the that fields are updated in the SourceItem property, the SharePoint ClientContext object must be called to submit the changes back to SharePoint and commit the edits for the ListItem object back to the site. The following code shows the method that is bound to the command for the Save button.

private void SaveProductAsset(object param)
{
if (SelectedProductAsset != null)
{
AppViewModel.Instance.OperationIsInProgress = true;
SelectedProductAsset.SourceItem.Update();
AssetsContext.ExecuteQueryAsync(onSaveSucceeded, onSaveFailed);
}
}

private void onSaveSucceeded(object sender, ClientRequestSucceededEventArgs args)
{
AppViewModel.Instance.AppDispatcher.BeginInvoke(
() =>
{
    SelectedProductAsset = null;
    AppViewModel.Instance.OperationIsInProgress = false;
});
}

When the SaveProductAsset method is called, the method accesses the underlying SourceItem property to call the UpdateObject method, which was exposed from the original ListItem object that was returned from the query for this item from SharePoint. The Update method is committed by invoking the ExecuteQueryAsync call, and then the edit form is reset by clearing the SelectedProductAsset object.

In this example, each update is committed individually, but this is not the only use of the ClientContext object. The ClientContext object supports batching calls. That means that this form could be refactored to submit all updates with one save call. Consider the following example.

foreach (ProductAsset item in ProductAssets)
{
   bool operationHasChangesToCommit = false;
   if (item.HasChanges)
   {
      item.SourceItem.Update();
operationHasChangesToCommit = true;
   }
   if(operationHasChangesToCommit)
      AssetsContext.ExecuteQueryAsync(onSaveSucceeded, onSaveFailed);
}

About the Author

Ryan Morgan is a managing partner and senior architect at Arrow Consulting & Design in West Palm Beach, Florida. At Arrow, Ryan focuses on large-scale business process automation and business intelligence in the areas of finance and master data management using SharePoint with Silverlight and ASP.NET. Ryan is an active member of the .NET and SharePoint community speaking at Code Camps, SharePoint Saturdays and at DevConnections in Las Vegas. Ryan co-wrote Professional DotNetNuke 5 for Wrox. Ryan’s company is on the web at www.ArrowDesigns.com and www.ArrowNuke.com.

The following were tech editors on Microsoft SharePoint 2010 articles from Wrox:

  • Matt Ranlett is a SQL Server MVP who has been a fixture of the Atlanta .NET developer community for many years. A founding member of the Atlanta Dot Net Regular Guys, Matt has formed and leads several area user groups. Despite spending dozens of hours after work on local and national community activities, such as the SharePoint 1, 2, 3! series, organizing three Atlanta Code Camps, working on the INETA board of directors as the vice president of technology, and appearing in several podcasts such as .Net Rocks and the ASP.NET Podcast, Matt recently found the time to get married to a wonderful woman named Kim, whom he helps to raise three monstrous dogs. Matt currently works as a senior consultant with Intellinet and is part of the team committed to helping people succeed by delivering innovative solutions that create business value.

  • Jake Dan Attis. When it comes to patterns, practices, and governance with respect to SharePoint development, look no further than Jake Dan Attis. A transplant to the Atlanta area from Moncton, Canada, Dan has a degree in Applied Mathematics, but is 100% hardcore SharePoint developer. You can usually find Dan attending, speaking at, and organizing community events in the Atlanta area, including code camps, SharePoint Saturday, and the Atlanta SharePoint User Group. When he's not working in Visual Studio, Dan enjoys spending time with his daughter Lily, watching hockey and football, and sampling beers of the world.

  • Kevin Dostalek has over 15 years of experience in the IT industry and over 10 years managing large IT projects and IT personnel. He has led projects for companies of all sizes and has participated in various roles including Developer, Architect, Business Analyst, Technical Lead, Development Manager, Project Manager, Program Manager, and Mentor/Coach. In addition to these roles, Kevin also managed a Solution Delivery department as a Vice President for a mid-sized MS Gold Partner from 2005 through 2008 and later also served as a Vice President of Innovation and Education. In early 2010 Kevin formed Kick Studios as a company providing consulting, development, and training services in the specialized areas of SharePoint and Social Computing. Since then he has also appeared as a speaker at numerous user group, summit, and conference type events across the country. You can find out more about Kevin on his blog, The Kickboard.

  • Larry Riemann has over 17 years of experience architecting and creating business applications for some of the world’s largest companies. Larry is an independent consultant who owns Indigo Integrations and does SharePoint consulting exclusively through SharePoint911. He is an author, writes articles for publication and occasionally speaks at conferences. For the last several years he has been focused on SharePoint, creating and extending functionality where SharePoint leaves off. In addition to working with SharePoint, Larry is an accomplished .Net Architect and has extensive expertise in systems integration, enterprise architecture and high availability solutions. You can find him on his blog.

  • Sundararajan Narasiman is a Technical Architect with Content Management & Portals Group of Cognizant Technology Solutions, Chennai, with more than 10 years of Industry Experience. Sundararajan is primarily into the Architecture & Technology Consulting on SharePoint 2010 Server stack and Mainstream .NET 3.5 developments. He has passion for programming and also has interest for Extreme Programming & TDD.

Additional Resources

For more information, see the following resources: