Develop and deploy Silverlight + SharePoint 2010 Solutions
/I had the pleasure of presenting at the Australian SharePoint Conference on Develop and deploy Silverlight + SharePoint 2010 Solutions. I had a blast, thank you guys for all the work organizing everything.
While the PowerPoint slides will be made available shortly, I just want to take the time and share the key points of the presentation in detail for people to reference:
Download the zipped solution of what I built on stage here
Part 1 is about getting started. How do you start if you have some dev skills and aren't afraid of code. Where do you start? You'll see how things begin to come together.
WHAT DO YOU NEED?
- Visual Studio .NET 2010 (you can use Visual Web Developer 2010 Express)
- Silverlight 4 runtime (you probably already have this via Windows Update)
- Silverlight 4 SDK included in Silverlight 4 Tools for Visual Studio 2010 (free)
- Silverlight 4 Toolkit April (free)
- Of course, it would be quite nice to actually have SharePoint 2010 somewhere so you can test this :-) You can do this demo with SharePoint Foundation 2010 (free), but you will need to have a Windows Server 2008 / 2008 R2 (not free) as well as the x64 hardware to run this server (not free)
- For IT PRO, it is possible to compile the Silverlight application with the above tools and deploy this to your SharePoint 2010 my-site server just to see how it works. Let me know how this goes!
If you aren't sure where to grab these, use the Microsoft Web Platform Installer 2.0 - it figures out everything for you.
THE GOAL
The goal here is to show a Silverlight application working with SharePoint by consuming SharePoint data.
Here's a Silverlight web part, using the Chart from Silverlight toolkit, but pulling data from a SharePoint list live.
SETTING UP THE PROJECTS
In VS.NET 2010, create a Silverlight project, accept the suggestion that it wants to add an additional ASP.NET Web Application project for hosting the Silverlight project.
I specifically targets Silverlight 4 because I have the Silverlight 4 toolkit installed on my machine. I've previously build this demo with Silverlight 3 and Silverlight 3 toolkit.
XAML
- Add a Silverlight User Control to your project SharePointChart.xaml, compile project so VS.NET recognizes there's a new user control.
- Drag and drop the SharePointChart.xaml component from the toolbox into the MainPage.xaml
- Remove any positioning or alignment attributes, so that the SharePointChart snaps to the width and height of the parent (MainPage.xaml).
- I then add a file to the project, this file contains two classes:
an Entity that implements INotifyChanged and a Collection that inherits from ObservableCollection - The reason that I create these two classes isn't particularly clear - but basically, I wanted to:
- Keep the example simple
- I don't want to have ANY UI-update code - want to utilize Silverlight databinding
- I can quickly get the Silverlight part up and running with mock data
- These simple objects can be changed to your strongly typed entity and lends well to a REST-based method call as well.
- Go back to the SharePointChart.xaml - I gutted the XAML and replaced it with:
- The key points in the XAML are:
- Namespace in Silverlight (xmlns)
- Variables in Silverlight (UserControl.Resources)
- Databinding of ItemSource, and set Idenpendent/DependantValueBinding
- It is also VERY IMPORANT that I didn't want to show any more XAML after this point. SharePoint developers are easily scared by scary looking XML-like code. May be because it reminds them of CAML.
- Time to write some code - in the user control's Loaded event, I add a method call to GetData(), then UpdateCollection();
- Notes for this step:
- I split the data call into two functions GetData and UpdateData - this is important because the SharePoint client object model (as well as most ways Silverlight actually calls a web service) is always asynchronous, so you prepare the request and make the service call, then expect a callback to fire when it is successful. This is not just in SharePoint, but with REST, WCF, RIA as well.
- Still doing sample set up right now - add a couple of items to the collection
- Notice there are no UI update code - F5, and Silverlight will run. Silverlight automatically binds to the mock data collection.
using System.Collections.ObjectModel; using System.ComponentModel; //REMEMBER: namespace namespace SLSPTest { /// <summary> /// Two interfaces for easy databinding in Silverlight: /// INotifyPropertyChanged - for entity - raise event when a property is updated and tells the UI /// ObservableCollection - for collections - raises event(s) when elements add/removed, provides enumerator /// </summary> public class PollItem : INotifyPropertyChanged { private string surveyChoice = ""; private int surveyCount = 0; public PollItem(string Choice, int Count) { surveyChoice = Choice; surveyCount = Count; } /// <summary> /// A Choice in this survey /// </summary> public string SurveyChoice { get { return surveyChoice; } set { surveyChoice = value; OnPropertyChanged("SurveyChoice"); } } /// <summary> /// Number of votes for this choice /// </summary> public int SurveyCount { get { return surveyCount; } set { surveyCount = value; OnPropertyChanged("SurveyCount"); } } #region Implement INotifyPropertyChanged interface public event PropertyChangedEventHandler PropertyChanged; void OnPropertyChanged(string propertyName) { // check event handlers exists if (PropertyChanged != null) { // raise event PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion } public class PollCollection : ObservableCollection<PollItem> { // just a strongly typed observable collection } }
<UserControl x:Class="SLSPTest.SharePointChart" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:SLSP="clr-namespace:SLSPTest" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"> <UserControl.Resources> <SLSP:PollCollection x:Name="Collection" /> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" Loaded="LayoutRoot_Loaded"> <toolkit:Chart Name="pollChart" Title="Favourite Languages" > <toolkit:Chart.Axes> <toolkit:LinearAxis Interval="1" Orientation="X" /> </toolkit:Chart.Axes> <toolkit:BarSeries Title="Poll" IndependentValueBinding="{Binding Path=SurveyChoice}" DependentValueBinding="{Binding Path=SurveyCount}" ItemsSource="{Binding Source={StaticResource Collection}}" /> </toolkit:Chart> </Grid> </UserControl>
using System.Windows; using System.Windows.Controls; namespace SLSPTest { public partial class SharePointChart : UserControl { public SharePointChart() { InitializeComponent(); } private void LayoutRoot_Loaded(object sender, RoutedEventArgs e) { GetData(); } private void GetData() { UpdateData(); } private void UpdateData() { PollCollection collection = this.Resources["Collection"] as PollCollection; collection.Add(new PollItem("C#", 1)); collection.Add(new PollItem("VB.NET", 10)); collection.Add(new PollItem("SQL", 5)); } } }
BRIEF BREAK IN SHAREPOINT
- Back in SharePoint, create a list "Football", add 1 column "FavouriteCountry"
- Add a few rows of data, otherwise it won't be much fun
BACK TO VS.NET
- We currently have a running Silverlight application, but it is just displaying mock data.
- Add the Microsoft official Silverlight Client Object Model from the 14Hive/SharePoint root.
\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ClientBin\
- Modify the code to
- Notes:
- Client Object Model uses the ClientContext class from Microsoft.SharePoint.Client - this is where all the magic happens. Start from: ClientContext.Current
- If we don't have a current context - use our mock data, otherwise query SharePoint
- Use the various methods on the ClientContext to get to the list or list item that you want, then call ClientContext.Load
- Client object model does not actually talk to the server until you call ExecuteQueryAsync
- Need 2 callbacks - I don't take care of the failure
- On success callback - need to redirect back to the UI thread. Very common in Silverlight and WinForm multi-threading applications. SharePoint or ASP.NET developers might have never seen a dispatcher in their life!
- Back on the UI thread, and updating the collection. My long-winded version does:
- count the list items into a dictionary of team/votes
- Go through each item of the current collection (remember, already databound), and update the numbers - the cool effect of this is that when the number changes the chart will respond and grow automatically via the animation storyboard.
- Finally add any new dictionary pair to the collection (will appear on chart)
using System.Windows; using System.Windows.Controls; using Microsoft.SharePoint.Client; using System; using System.Collections.Generic; namespace SLSPTest { public partial class SharePointChart : UserControl { ClientContext sharepointContext; public SharePointChart() { InitializeComponent(); sharepointContext = ClientContext.Current; } private void LayoutRoot_Loaded(object sender, RoutedEventArgs e) { if (sharepointContext == null) { GetData(); } else { QueryDataFromSharePoint(); } } private void GetData() { UpdateData(); } private void UpdateData() { PollCollection collection = this.Resources["Collection"] as PollCollection; collection.Add(new PollItem("C#", 1)); collection.Add(new PollItem("VB.NET", 10)); collection.Add(new PollItem("SQL", 5)); } private const string ListName = "Football"; private const string InternalFieldName = "FavouriteCountry"; private ListItemCollection listItems; private void QueryDataFromSharePoint() { Web web = sharepointContext.Web; // get our list List list = web.Lists.GetByTitle(ListName); // get items in our list listItems = list.GetItems(CamlQuery.CreateAllItemsQuery()); // tell context we want to load them // we don't actually need to load the list - just need a reference to get to the list item // sharepointContext.Load(list); sharepointContext.Load(listItems); // execute the load asynchronously sharepointContext.ExecuteQueryAsync(SucceededCallback, FailedCallback); } private void FailedCallback(object sender, ClientRequestFailedEventArgs e) { throw new NotImplementedException(); } private void SucceededCallback(object sender, ClientRequestSucceededEventArgs e) { this.Dispatcher.BeginInvoke(UpdateDataFromSharePoint); } private void UpdateDataFromSharePoint() { // loop through the list from sharepoint - update my dictionary with count Dictionary<string, int> summary = new Dictionary<string, int>(); foreach (var listitem in listItems) { string language = listitem[InternalFieldName].ToString(); if (!summary.ContainsKey(language)) { summary[language] = 1; } else { summary[language]++; } } PollCollection collection = this.Resources["Collection"] as PollCollection; // loop through collection - update any count foreach (var item in collection) { if (summary.ContainsKey(item.SurveyChoice)) { item.SurveyCount = summary[item.SurveyChoice]; summary.Remove(item.SurveyChoice); } else { item.SurveyCount = 0; } } // add any new entries foreach (var item in summary) { collection.Add(new PollItem(item.Key, item.Value)); } } } }
EASIEST DEPLOYMENT EVER
- Compile your Silverlight application, look for the compiled XAP file in the web application's clientbin folder. This XAP file is a zip file of all the Silverlight dll and XAML files in one package.
- To deploy, upload this into a document library in your SharePoint, copy the URL
- Create a new page, insert a Silverlight Web Part, which will ask you for the URL to a XAP file, paste the URL from your earlier uploaded XAP file (in the document library).
RE-DEPLOYMENT(S)
- If you build a new version of your Silverlight XAP file, just upload over the old version in the same document library
- Go back to the pages with the Silverlight Web Part and refresh (F5), the browser will reload the new version of your Silverlight
NEXT UP
This is getting pretty long and I'll stop for now. I'll continue later with:
- Re-query
- Debugging
- Tunning the performance of your client object query calls
- Further discussions regarding XAP-deployment, Sandbox Solutions or Farm Solutions
- REST interface
- Silverlight initParam
- Out of Browser