Develop a Sandboxed Silverlight 3.0 Web part for SharePoint 2010

I recently had a chance to play around with the Silverlight Bing map control  v1 SDK and thought of integrating the map functionality with SharePoint  as a web part. In this post I am going to through the steps I’ve taken to accomplish this task. Basically we are going to develop a Silverlight 3.0 web part  as a sandbox solution for SharePoint 2010. Our web part will be interacting with SharePoint 20101 new Client object model to access data stored in a custom list to render the map with locations as pin points. The end functionality will looks as below. Out custom SharePoint list stores the locations with their Latitude and Longitudes.

image

You need to have SharePoint 2010 dev box, Visual Studio 2010 and Expression Blend 3, Silverlight Bing map control library installed for this task. I have outlined the steps required to setup a development environment for SharePoint 2010 in my previous blog post.

Let’s get started.

Step1: Create a new Silverlight 3.0 application in Visual Studio 2010

image

Step2: Choose to host your SilverLight application in a ASP.NET Web Application project.

image

Step3: Add the SharePoint 2010 reference DLL’s to interact with Client object model  into the Silverlight project that you have created. The two dll’s that you need to reference are located in the folder 14\TEMPLATE\LAYOUTS\ClientBin

image

Step 4: Download and install  Bing Maps Silverlight Control SDK. The interactive live SDK can be found here. You will also need a developer key to assess Bing maps which can be obtain from here.  Once the SDK is installed reference the Bing map dll’s (2) in your silverlight project. by default the those dll’s are present in C:\Program Files (x86)\Bing Maps Silverlight Control\V1

Once all necessary dll’s references are added our Silverlight project looks as below.

image

Step 5: Now its time to design the user interface with XAML for our web part. Import the xml namespace for Bing map control in the in MainPage.xaml file.

xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

once the above name space is imported all you need is  <m:Map/> to place the map control in your user interface. I’ve used expression Blend 3 to author XAML and output looks as below. Basically I got a simple Grid layout with TextBlock and Map control with the default map location points to central Australia with “Road” mode and  with zoom level 4.3. You can set these values as per your requirements. The CredentialsProvider key is what you get when you create a developer account for Bing map control.

<UserControl x:Class="SLFactoryMap.MainPage"
    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:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
    mc:Ignorable="d">
    <UserControl.Resources>
        <Style x:Key="HeaderStyle" TargetType="TextBlock">
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="FontStyle" Value="Normal"/>
            <Setter Property="FontSize" Value="13.333"/>
            <Setter Property="Margin" Value="0,0,0,0"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
            <Setter Property="Foreground">
                <Setter.Value>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="Black" Offset="0"/>
                        <GradientStop Color="#FF725E5E" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="Margin" Value="5,0,0,0"/>
        </Style>
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" Background="#FF64D3DC" Width="600" Height="490">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="0.04*"/>
            <RowDefinition Height="0.92*"/>
        </Grid.RowDefinitions>
        <TextBlock Height="20" Style="{StaticResource HeaderStyle}" Grid.Row="0" Grid.Column="0"  d:LayoutOverrides="Height" Grid.ColumnSpan="2"> Production Sites </TextBlock>
        <m:Map x:Name="factorymap" Grid.Row="1" Grid.Column="0" CredentialsProvider="{your key}" Mode="Road" ZoomLevel="4.3" Center="-25.7000,133.8833"/>
    </Grid>
</UserControl>

Above XAML code will render out UI in visual studio canvas designer as shown below.

image

Step 6: Now its time to create our code behind class MainPage.xaml.cs  in C#

First import the following names spaces

using SP = Microsoft.SharePoint.Client;
using Microsoft.Maps.MapControl;

then create the following class level members

SP.ClientContext _context;
SP.Web _currentWeb;
SP.List _locationList;
SP.ListItemCollection _locations;

Then we need to create the UI loaded routed event handler and obtain the SharePoint client context reference so that we can access  sharepoint client object model. Once we get a reference to the object model (web) which corresponds to the SPWeb in server context, we are obtaining a client reference to the “factoylocations” custom list which stores our locations. We are going to access all the list items in that list. Therefore we need a CAML query with no filters. To reduce server calls we batch up all the server requests and make one single call to the server to obtain list data. In the end we make a Asynchronous call to the server by calling _context.ExecuteQueryAsync . At this point SharePoint Client object model calls WCF Services perform authentication,  calls service methods and obtain the results for us which frees us from lot of plumbing work. This is a massive developer productivity feature comes with SharePoint 2010.

void MainPage_Loaded(object sender, RoutedEventArgs e)
{    // get the current client context
    _context = SP.ClientContext.Current;
    // get a reference to the current web
    _currentWeb = _context.Web;
    //get a reference to the list that contains locations
    _locationList = _currentWeb.Lists.GetByTitle("FactoryLocations");
    //we need to obtain all items in the location list.
    SP.CamlQuery query = new SP.CamlQuery();
    query.ViewXml = "<View></View>";
    _locations = _locationList.GetItems(query);
    //batch up the work to perform
    _context.Load(_locationList);
    _context.Load(_locations);
    _context.ExecuteQueryAsync(clientRequestSucceeded, clientRequestFailed);
}

Once the sever request is completed we need a call back method which needs to run in the UI thread. Our clientRequestSucceeded callback method looks as below. Here we iterate through the listitem collection and create Pushpin for each location listitem.

Dispatcher.BeginInvoke make sure that our anonymous method get executed in the client UI thread.

/// <summary>
/// Event handler for get request succeeded
/// </summary>
private void clientRequestSucceeded(object sender, SP.ClientRequestSucceededEventArgs e)
{
    //execute the code in the UI thread as a anonymous method
    Dispatcher.BeginInvoke(() =>
    {
      try
        {
          foreach (SP.ListItem loc in _locations)
           {
             Location location = new Location((Double)loc["Latitude"], (Double)loc["Longitude"]);
             // The pushpin to add to the map.
             Pushpin pin = new Pushpin();
             pin.Location = location;
             // Adds the pushpin to the map.
             factorymap.Children.Add(pin);
           }
       }
      catch (Exception ex)
       {
            MessageBox.Show(ex.ToString());
       }
   });
}

Failed request handler looks as below.

/// <summary>
/// Event handler for get request failed
/// </summary>
private void clientRequestFailed(object sender, SP.ClientRequestFailedEventArgs e)
 {   //execute the code in the UI thread as a anonymous method
   Dispatcher.BeginInvoke(() =>
   { MessageBox.Show(string.Format("Error occured in displaying factory locations:  {0}", e.Message)); }
   );
 }

Finally we need to make sure that loaded event is wired up to the main page constructor as shown below

public MainPage()
{
    InitializeComponent();
    Loaded += new RoutedEventHandler(MainPage_Loaded);
}

Step 7: At this point we can build the project and we should be able to see the SilverLight .xap file get generated. Next Add a empty SharePoint project to the solution. Point the URL to the site collection you are working with and Select Deploy as a SandBoxed solution option. SandBoxed SharePoint Solution Package files (WSP files) that are limited in what they can do and in the server resources they can use. What they can do is limited using Process Isolation and Code Access Security limited to the SharePoint Site. The resources they can use are limited by process monitoring, logging and log aggregation. Executing Code in the Sandbox Executing Code in the Sandbox The Sandboxed Solutions Service provides for a complete isolation system that ensures code running in a sandboxed solution cannot reach out to access information beyond the scope of the deployment. Specifically, sandboxed solutions will not be able to make updates to the SharePoint object model beyond the scope of the SPSite object. Farm level and web application level changes are allowed only for read operations. This gives best option for developers as well as farm administrators.

image

Step 8: Add a module a New Item to the SharePoint Project. In this case I call it FactoryMapModule. This module item is used to include the .xap file into the SharePoint solution. Therefore we need to tell the module, the location of our .xap file. Right click the module item –> go to properties > Select Output References. In the Dialog box Click Add button. Select your SilverLight Project Name. In this case it was SLFactoryMap. Also select the deployment type as ElementFile. Finally Click OK.

Now we have our .xap file generated by the SilverLight project included as a dependency for our module.

image

Now our solution structure should look as shown below.

image

Step 9: Update element.xml file of the module item. add a File section and include the XAP file path (source path) and Url (relative URL after deployment)

image

Step 10: At this point we are ready to build the project and deploy the SharePoint solution. Right Click the SharePoint project and Select “Deploy” option. This action will build our project, Create a .wsp Solution file and deploy the solution into SharePoint solution gallery in the specified site collection and activate the feature. Now we can go to the solution galley as the Site Collection administrator and view our solution in the solution store.

Go to Site Settings –> Galleries –> Solutions

image

image

Step 11: Create a custom list in the site with the name “FactoryLocations” with two columns named “Longitude” and “Latitude” and fill some sample data init. Our web part is going to consume this data when rendering the map.

Step 12: Add the Silverlight web part to a web part page. SharePoint 2010 provides a wrapper web part (under media and content category) to host Silverlight .xap files. The concept  is similar to SmartPart approach we have in WSS3/MOSS 2007.  Therefore we need to add this wrapper  web part first into the page that you want to place the Silverlight web part as shown below.

image

Step 13: Specify the relative path of the .xap file to the Silverlight web part. In this sample the .xap file is located in the root of the site collection.

Note: Also set the Chrome type to None in the Silverlight web part properties.(This is a workaround for a bug present in Beta 2 in rendering the title of Silverlight web part)

image

Now we should be able to visualize the map control in our Silverlight Webpart with locations specified in our list marked as Pinpoints. The sandbox worker process is where your actual code runs! This is in contrast to having the code run inside of w3wp.exe. This is why you don’t have to restart the application pool every time your redeploy an sandbox solution. If you wish to debug a sandbox solution, and for any reason why F5 debugging is not working, you will need to attach your visual studio debugger to the SPUCWorkerprocess.exe process.

As we have seen the power of Silverlight 3.0 along with SharePoint 2010 client object model enhance developer productivity and open up lot of opportunities to build rich interactive applications leverage on SharePoint 2010 platform.


Setting up a SharePoint 2010 Beta 2 Development Environment

In this post I will go though my SharePoint 2010 Beta 2 setup experience as development environment. Here I am setting up a single server farm with AD, SharePoint and SQL sitting in a single development box. I am using Windows 2008 R2  (64bit) as the OS and following is the high level initial steps that I‘ve taken to configure the base OS. The full list of Hardware and Software Requirements can be found in Determine hardware and software requirements (SharePoint Server 2010) and Jeremy Thake has a excellent Wiki page outlining the detail steps on SharePointDevWiki.

1) Add Active Directory Domain Services server role and Configured the Domain using Active Directory Domain services installation wizard. Also add add Web Server (IIS) role and Application Serve role.

2) Create Service Accounts

It is strongly recommended to create domain accounts and use them as service accounts. You need to create at least the following accounts in Active Directory. I will be using local Administrator as the setup account for this dev environment and the following accounts.

Account type Account name
Farm Account or DB Access Account SpSqlService
Service Account SpService

For detail description of required accounts and permissions refer to Administrative and service accounts required for initial deployment .Additionally you should create for every service a separate service account in order to meet least-privilege security best practice.

3) Disable the loop back adapter check using this PowerShell script  written by  Michael Blumenthal. You might have to temporary change the power shell execution policy  from remotesigned (Default) to unrestricted using “set-executionpolicy unrestricted” before attempting to run the the script. 

Why we need this?

Recently Microsoft released an update, which prevents that you can log on locally to a website which has a FQDN. The following  KB article describes the issue. You receive error 401.1 when you browse a Web site that uses Integrated Authentication and is hosted on IIS 5.1 or a later version

To resolve you can disable the loopback check completely, or just for the used FQDNs. Alternatively If you have more then one server in you farm a better way to do so is a custom stsadm extension. Gary Lapointe has written such an extension. Take a look at it: Setting Back Connection Host Names for SharePoint 2007 Using STSADM. I used the PowerShell script.

4) Install SQL Server 2008 with windows authentication and set all services to run under SpSqlService dedicated account. Also Install SQL Server 2008 SP1 and apply the following patch : KB976761 

5) Download and Install Microsoft Chart Controls for Microsoft .NET Framework 3.5

6) Download and install  Microsoft "Geneva" Framework Runtime (2008R2\x64\AdfsSetup.exe)

7) Download and Install ADO.NET Data Services v1.5 CTP 2 

Note: If you are using Windows Server 2008 you will need to download and install following additional components (Windows Server 2008 SP 2, Windows Management Framework With PowerShell 2 and WCF Fix )

8 ) Run SharePoint Server Setup and Select “Install Software prerequisites” option

 image

One all software requirements are satisfied you should see the following screen.

image

 

9) Now we are ready to Install SharePoint Server 2010 and setup the farm environment. Run the Setup and follow the Wizard. When you come to Installation Type “Server Farm” option.

image

10) Select “Complete” option from for the Server Type

 image

 

11). At the end of the installation Check Run Configuration Wizard option and Click Close button.

image

12) This will open Run Confuguration Wizard. When you come to Connect to a server farm Select “Create a new server farm option”

image

13) Specify the Database server name and specify the domain sql service user account that we have created earlier.

image

14) Specify a passphrase

image

15) Accept the defaults for the Central Administration Web Application

image

16) Verify the Configuration details and proceed

 image

 

Now we have finished running SharePoint Configuration Wizard. This has created core SharePoint Databases,Web Apps and Associated App pools and Services.

Following are the application pools that has been created at this point.

App pool Identity Description
SharePoint Central Administration v4 SharePoint Farm Account It’s responsible for Central Administration on the machines hosting it
The SharePoint Topology Service Application SharePoint Farm Account The app pool name in IIS is a GUID, in SharePoint it is SharePoint Web Services System.This hosts the Topology Service Application, which is known as the Application Discovery and Load Balancer Service Application
The SharePoint Security Token Service Application SharePoint Farm Account The app pool name in IIS is a GUID, in SharePoint it is SecurityTokenServiceApplicationPool
SharePoint Web Services Root LocalService Status: Stopped This hosts the SharePoint Web Services IIS Web Site and is on every machine in the farm. This is the host web site for Service Applications. Service Applications WCF end points  are hosted in here.

 

17) Launch Farm Configuration Wizard from Central Administration. This will provision Service Applications, and Allows you to create top level content web application and site collection.

image

Specify the service account for running farm services.

image

Create a root level Site Collection

clip_image001

 

After farm configuration wizard is finished will we have the following additional Application Pools.

App pool Identity Description
SharePoint Web Services Default SharePoint Services Account (spService) Hosts all the other Service Applications
SharePoint – 80 SharePoint Services Account (spService) This is the default application pool used to host end user Content Web Application

 

Now we have working site collection for development purposes. Additionally if you are going to develop sandbox solutions you will need to start SharePoint Foundation User code Service as described in Configure a farm for sandboxed solutions.

Hope this helps!