Skip to main content

Karine Bosch

Go Search
Karine Bosch
  

The Silverlightness of SharePoint

  U2U Home
  U2U Course Calendar

 Other Belgian Bloggers

  Kris Vandermotten
  Peter Himschoot
  Serge Luca
  Bart De Smet
  Joris Poelmans
  David Boschmans
  Tom Mertens
  Stefaan Rillaert
  Yves Hanoulle
  Roy Dictus
The Silverlightness of SharePoint
Enabling access to SharePoint data to a Silverlight application running outside the SharePoint context

In previous posts about Silverlight integration with SharePoint I always used Silverlight applications that are deployed to the _LAYOUTS folder, or uploaded to a document library, or even compiled as an embedded resource of a web part. In all these examples the Silverlight application runs within the context of SharePoint.

In this post I will explain how you can use SharePoint data from within a Silverlight applications that runs within a normal ASP.NET web application and thus not in the context of SharePoint.

Create a sub folder with the name ClientBin and copy the .xap file to this directory. Check the web.config of your ASP.NET web site must be Silverlight enabled.

Embed a Silverlight application in an ASP.NET web page as follows:

<form id="form1" runat="server" style="height:100%;">
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <div  style="height:100%;">
        <asp:Silverlight ID="Xaml1" runat="server"
            Source="~/ClientBin/SL.XAML.AdventureWorksProducts.xap"
            MinimumVersion="2.0.31005.0" Width="100%" Height="100%"
            InitParameters="siteurl=
http://wss.u2ucourse.com,listname=AdventureWorks Products" />
    </div>
</form>

 

Notice that you have to add a script manager tag and a Silverlight tag. The Silverlight control sets properties like Width, Height, Source and InitParameters. One of the initial parameters is the URL of the SharePoint site.

Run the web site. You will see that it generates a security error.

image

If you click the yellow warning sign in the status bar of the browser, you would see that a network error was generated. This is because you want to make a cross-domain call. To solve this issue you have to create a clientaccesspolicy.xml file that contains the following xml:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 

This allows all calls coming from other domains. If you want to restrict the access to a well-defined domain, you have to specify that URL in the <domain uri=”…” /> node. Read more on the subject in this MSDN article.

This file must be copied to the root of the web application, in this case the SharePoint web application. You can try to copy it to the IIS web application directory but this will not help. The best way to install this file in the root is to open SharePoint Designer and to open the SharePoint web application from there. Copy the file into the root:

image

Run your web application again and it will work like a charm!

image

Creating Search Content Sources with a PowerShell CmdLet

In this blog I already posted many tips on how to use PowerShell on SharePoint. PowerShell has been developed by Microsoft for performing Windows Administration tasks. You can even automate tasks by writing PowerShell scripts. PowerShell works with the .NET object model. This means that you need an in-depth knowledge of the object model of the technology you want to manage with PowerShell. In the case of SharePoint this is a really big beast with a lot of facets. As you or your administrator wants to perform more advanced management tasks your PowerShell commands and scripts will become a lot more complicated. In that case you can extend PowerShell functionality with PowerShell CmdLets.

In PowerShell version 1.0 CmdLets are written in managed code, meaning in languages like VB.NET and C#. There exist Visual Studio templates that can used to develop your custom PowerShell CmdLets.

Now I hear you think: “Wait! Why bother writing PowerShell CmdLets if you can solve this issue by developing custom STSADM commands?”. At this point you can start a discussion on what the best option is. I advise the following rule of thumb: if you or your administrator only has to manage SharePoint, than you go for custom STSADM commands because in that case STSADM is part of your daily job. If you or your administrator uses PowerShell to perform administrative tasks on different technologies (like SQL Server, Exchange and SharePoint), your best option is PowerShell CmdLets.

The base principles of writing PowerShell CmdLets are explained in this MSDN article.

In this post I will explain how you can manage Search content sources using PowerShell and how you can develop custom PowerShell CmdLets to create a new content source.

You can download the source code for the PowerShell CmdLet here. Be aware that this is demo code and that it need to be extended with solid exception handling.

When you want to work with Search content sources you first have to load the necessary assemblies:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Search")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Server.Search")

 

Then you have to retrieve an instance of the Shared Service Provider. As PowerShell doesn’t run in the context of SharePoint, you have to pass the name of the Shared Service Provider to the static method GetContext of the Microsoft.Office.Server.ServerContext class. In PowerShell you achieve this as follows:

#Instantiate Shared Service Provider
$serverctx = [Microsoft.Office.Server.ServerContext]::GetContext(“U2USharedServices”)

 

Once you have retrieved an instance of the Shared Service Provider you have access to the services that it hosts. The Search service is one of these services. You can instantiate the Search service by executing the GetContext static method of the Microsoft.Office.Server.Search.Administration.SearchContext class.

#instantiate the Search Service

£
$searchctx = [Microsoft.Office.Server.Search.Administration.SearchContext]::GetContext ($serverctx)

#display the name of the search service instance
$searchctx.Name

 

You can retrieve and display the Search content sources as follows:

#show the available content sources
$content = new-object Microsoft.Office.Server.Search.Administration.Content($searchctx)

foreach ($cs in $content.ContentSources)
{
    Write-Host "NAME: ", $cs.Name, " - ID: ", $cs.Id, " - CrawlStatus: ", $cs.CrawlStatus
    Write-Host "Full Crawl Schedule: ", $cs.FullCrawlSchedule.Description
    Write-Host "Incremental Crawl Schedule: ", $cs.IncrementalCrawlSchedule.Description
}

 

The trouble starts when you now want to create a new content source using PowerShell because you need to specify the type of content source you want to create. As you already know, you can create content sources of the following types:

  • WebContentSource                          
  • SharePointContentSource
  • FileShareContentSource
  • ExchangePublicFolderContentSource
  • BusinessDataContentSource

If you want to create a FileShare content source you can write this as follows in C#:

 

ContentSource newContentSource = content.ContentSources.Create(
     typeof(FileShareContentSource), nameContentSource);
newContentSource.StartAddresses.Add(new Uri(url));
FileShareContentSource fileShareContentSource =
     (FileShareContentSource)newContentSource;
fileShareContentSource.FollowDirectories = true;
fileShareContentSource.Update();

 

As you can see, the Create method asks for the type of the content source. The second parameter is the name for the new content type. You also have to specify the start address URL. In case of a file share  content source you can also specify whether you want to index the sub folders or not. After having set the  necessary properties you have to execute the Update method.h

Executing the correct Create method is rather difficult to achieve in PowerShell. It would be easier if you could execute something like this:

set-createcontentsource “name of the SSP” “name new content source” “type of content source” “start address”

 

In this case you can opt to write a PowerShell CmdLet to extend PowerShell functionality. PowerShell CmdLets are written in managed code. There exist a couple of Visual Studio project templates that you can download and use. (The link was down this weekend but I hope it will be restored).

Open Visual Studio 2005 and choose Windows PowerShell as project type in the left pane and the Windows PowerShell template in the right pane.

image

This creates a project skeleton consisting of a  PSSnapIn file and a class inheriting from. The project contains a reference to the System.Management.Automation assembly. This assembly comes with the PowerShell SDK.

Right-click the PSSnapIn file to view the code. The code contains 5 properties that need to be set by you. The most important one is  the Name property which will be used when you register your PowerShell CmdLet. The class inherits from PSSnapin which is the base class for creating snap-ins. Snap-ins are the deployment unit of Windows PowerShell and are tagged with a RunInstaller attribute for registering the Snap-in with PowerShell.

[RunInstaller(true)]
public class CreateContentSourceSnapIn : PSSnapIn
{
    public override string Name
    {
        get { return "CreateContentSourceCmdLet"; }
    }
    public override string Vendor
    {
        get { return "U2U"; }
    }
    public override string VendorResource
    {
        get { return "CreateContentSourceCmdLet,U2U"; }
    }
    public override string Description
    {
        get { return "Registers the CmdLets and Providers in this assembly"; }
    }
    public override string DescriptionResource
    {
        get { return "CreateContentSource,Registers the CmdLets and Providers in this assembly"; }
    }
}

 

The name will be used to register the CmdLet.

A Snap-in contains one or more CmdLets. A CmdLet is a class that inherits from the base class CmdLet residing in the System.Management.Automation namespace.

The class is preceded by the name of the CmdLet and consists of:

  • a verb that indicates the action of the CmdLet (get or set)
  • a noun that must be specific and describe what the action will perform

In this case the name of the CmdLet looks as follows:

[Cmdlet(VerbsCommon.Set, "CreateContentSource", SupportsShouldProcess = true)]
public class CreateContentSource : Cmdlet
{

}

 

You can define a number of parameters if the CmdLet needs some exra information before it can execute its extra functionality. As this sample CmdLet is going to create a new content source, it defines 4 parameters:

  • name shared service provider
  • name content source
  • type of the content source
  • start address of the content source

The first incoming parameter is the SharedServiceProvider name and is defined as follows:

private string nameSharedServiceProvider;

[Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true,
    HelpMessage = "The Name of the Shared Service Provider")]
[ValidateNotNullOrEmpty]
public string SharedServiceProviderName
{
    get { return nameSharedServiceProvider; }
    set { nameSharedServiceProvider = value; }
}

 

The Position attribute indicates that this parameter is the first one that need to be specified when the CmdLet is executed. The Mandatory attributes indicates that this attributes is required. The ValueFromPipelineByPropertyName attribute means that the value for this attribute can come from a property in the incoming pipeline object that has the same name as the parameter. You can specify a HelpMessage attribute that will display when help is asked for the CreateContentSource command. The ValidateNotNullOrEmpty is a validation rule indicating that the incoming value cannot be null or empty.

The other parameters are defined in a similar way.

There is one member to override: the ProcessRecord method. In this method you need to write the implementation code of your extra functionality. The implementation in the sample looks as follows:

protected override void ProcessRecord()
{
    Microsoft.Office.Server.ServerContext serverctx = null;
    SearchContext searchctx = null;

    try
    {
        // open the context of the Shared Service Provider
        serverctx = ServerContext.GetContext(nameSharedServiceProvider);
        if (serverctx != null)
        {
            searchctx = SearchContext.GetContext(serverctx);
            WriteObject("Connected to search context " + searchctx.Name);

            if (searchctx != null)
            {
                Content content = new Content(searchctx);

                switch (typeContentSource)
                {
                    case "sharepoint":
                        CreateSharePointContentSource(content);
                        break;

                    case "web":
                        CreateWebContentSource(content);
                        break;

                    case "fileshare":
                        CreateFileShareContentSource(content);
                        break;

                    case "exchange":
                        CreateExchangeContentSource(content);
                        break;

                    case "bdc":
                        CreateBDCContentSource(content);
                        break;
                }

            }
        }
    }
    catch (Exception ex)
    {
        WriteObject("ERROR: " + ex.Message);
    }
}

 

Each type of content source is created in a separate private method. When an exception occurs the error message is written to the console.

This is the sample code for the creation of the file share content source:

private void CreateFileShareContentSource(Content content)
{
    ContentSource newContentSource = content.ContentSources.Create(
        typeof(FileShareContentSource), nameContentSource);
    newContentSource.StartAddresses.Add(new Uri(url));
    FileShareContentSource fileShareContentSource =
        (FileShareContentSource)newContentSource;
    fileShareContentSource.FollowDirectories = true;
    fileShareContentSource.Update();
    fileShareContentSource.StartFullCrawl();
}

 

When the content source is created and the properties set, a full crawl on the new content source is started.

When the code is ready to deploy, build your project. To register the CmdLet with PowerShell open a Visual Studio command prompt, navigate to the bin\debug folder of your project and execute the installer by running the InstallUtil tool.

Installutill CreateContentSourceCmdLet.dll

 

Open Windows PowerShell and load the SharePoint assemblies if this is not yet done by your profile. Load also the PowerShell snap-in by executing the following:

Add-PSSnapIn CreateContentSourceCmdLet

 

This is the name you specified in the PSSnapIn installer class.

I can now create a new content source in PowerShell by executing the following:

set-createcontentsource “U2USharedServiceProvider” “Office Documents” “fileshare” “\\springfield\DocsToIndex”

 

When this command has executed successfully, the new content source will appear in the Search Settings pages of the SharePoint Central Administration.

image

image

A bit besides the subject of this post, but I read an excellent post on how to index and search your .NET source code and PowerShell scripts here.  

Creating a Calendar View with PowerShell

Some days ago someone asked me if it is possible to create a calendar view on a SharePoint list. And yes, it is possible.

First let’s take a look at the .NET code to create a calendar view.

using (SPSite site = new SPSite("http://wss.u2ucourse.com"))
{
    using (SPWeb web = site.OpenWeb())
    {
        SPList sourcelist = web.Lists["Course Calendar"];

        string querystring =
            "<OrderBy><FieldRef Name='Title' /></OrderBy>"
          + "<Where><DateRangesOverlap><FieldRef Name=\"EventDate\" />"
          + "<FieldRef Name=\"EndDate\" /><FieldRef Name=\"RecurrenceID\" />"
          + "<Value Type=\"DateTime\"><Week /></Value></DateRangesOverlap></Where>";

        SPView newview = sourcelist.Views.Add("DemoCalView", null,
               querystring, 3, false, false,
               SPViewCollection.SPViewType.Calendar, false);
                        

        newview.ViewFields.Add(sourcelist.Fields["Title"]);
        newview.ViewFields.Add(sourcelist.Fields["Start Time"]);
        newview.Update();

    }
}

 

If you go to your SharePoint site and inspect this new view you will see that only the entries for this week are visible because of the DataRangesOverlap query that is set to weekly, but that it shows the data in a monthly view. This is because of the calendar scope that you can set in the user interface, but how to create a calendar view with a weekly scope programmatically?

Now to PowerShell. To create that same view with PowerShell you have to execute the following commands:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$siteUrl = "
http://wss.u2ucourse.com"
$site = new-object Microsoft.SharePoint.SPSite($siteurl)
$web = $site.OpenWeb()
$list = $web.Lists["Course Calendar"]
$querystring = "<OrderBy><FieldRef Name='Title' /></OrderBy>"
$querystring += "<Where><DateRangesOverlap>"
$querystring += "<FieldRef Name='EventDate' /><FieldRef Name='EndDate' />"
$querystring += "<FieldRef Name='RecurrenceID' />"
$querystring += "<Value Type='DateTime'><Week /></Value>"
$querystring += "</DateRangesOverlap></Where>"
$newview = $list.Views.Add("DemoCalView", $null, $querystring, 3, $false, $false, "CALENDAR", $false)
$titlefield = $list.Fields["Title"]
$newview.ViewFields.Add($titlefield)
$datefield = $list.Fields["Start Time"]
$newview.ViewFields.Add($datefield)
$newview.Update()

I hope to have helped someone out :)

Displaying a Silverlight Application in a SharePoint Application Page

In previous posts I explained how you can host a Silverlight application from within a SharePoint web part and how you have to configure SharePoint before you are able to do so:

- hosting a Silverlight application from within a web part.

If you want to host a Silverlight application from within a SharePoint application page, you have to perform a few additional steps.

In your web.config you have to add an extra tag for the silverlight control in the <system.web><pages><controls> section. This section must at least contain the following:

<controls>
    <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    <add tagPrefix="asp" namespace="System.Web.UI.SilverlightControls" assembly="System.Web.Silverlight, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</controls>

The following code snippet is a very basic application page that hosts a Silverlight control. The page does not contain any code behind. You page has to reference the Microsoft.SharePoint.dll and the System.Web.Silverlight.dll. Import the according namespaces. Within the Main content control first place a script manager because this is required by the Silverlight control. Then add the Silverlight control using the <asp:Silverlight> tag. The Silverlight control renders the Hello Silverlight application from the Silverlight BluePrint for SharePoint.

Pay attention to the Source property. If you deploy your Silverlight application in the same sub directory as your as your application page (12\TEMPLATE\LAYOUTS\…) you can set the Source property as in the sample code below. But if your Silverlight application is deployed in the ClientBin directory within your SharePoint web application or within a SharePoint document library, your Source property will look a bit different.

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="System.Web.Silverlight, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master"
         Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase"  %>

<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="System.Web.UI.SilverlightControls" %>

<asp:Content ID="Main" contentplaceholderid="PlaceHolderMain"
             runat="server">
    <asp:ScriptManager runat="server" ID="ScriptManager1" />
    <asp:Silverlight ID="HelloSL" runat="server"
    MinimumVersion="2.0.31005.0"
    Source="SL.XAML.HelloSilverlight20.xap"
    Width="400" Height="300" />
</asp:Content>

<asp:Content ID="PageTitle" contentplaceholderid="PlaceHolderPageTitle"
             runat="server">
    Hello Silverlight
</asp:Content>

<asp:Content ID="PageTitleInTitleArea" runat="server"
             contentplaceholderid="PlaceHolderPageTitleInTitleArea" >
    The Hello Silverlight Application Page
</asp:Content>

 

This is the sample application page:

image

You can download the source code here.

Silverlight Data Binding Sample upgraded to Silverlight 2 Final Release

Recently I wrote an article for an online SharePoint magazine. It’s about hosting a Silverlight 2 application in a a SharePoint web part. You can also read the article on the U2U web site. I upgraded the sample to Silverlight 2 final release. You can download the upgraded code and the list templates here.

It is a tutorial in which I explain how you can host a Silverlight 2 application from within a SharePoint Web Part. The Web Part will pass the URL of the SharePoint site together with the name of the list for which the Silverlight application will show the data. The retrieval of the data will be done by the Silverlight application using the HttpWebRequest technique for calling the SharePoint web services. As the SharePoint web services return a chunk of XML the XML will be handled by using LINQ for XML. The data will be bound to the Silverlight controls.

There are a few changes to take into account when working with Silverlight 2 final release.

The only thing to change in the SharePoint web part is in the CreateChildControl method: you have to set the MinimumVersion property of the Silverlight control to 2.0.30923.0.

protected override void CreateChildControls()
{
    base.CreateChildControls();

    // instantiation of the silverlight control
    silverlightControl =
      new System.Web.UI.SilverlightControls.Silverlight();
    silverlightControl.ID = "SLAdventureWorks";
    silverlightControl.MinimumVersion = "2.0.30923.0";
    silverlightControl.Width = new Unit(750);
    silverlightControl.Height = new Unit(600);
    silverlightControl.Source =
       this.Page.ClientScript.GetWebResourceUrl(this.GetType(),
        "SL.AdventureWorksProducts.Resources.SL.XAML.AdventureWorksProducts.xap");

    // parameters passed are the URL of the SharePoint site and
    //the name of the list containing the AdventureWorks Products
    silverlightControl.InitParameters = "siteurl=" +
       SPContext.Current.Web.Url +
       ",listname=AdventureWorks Products";

    this.Controls.Add(silverlightControl);
}

 

The major change lies in the silverlight application, specifically in the functioning of the HttpWebRequest object.

1. The HttpWebRequest object works completely asynchronous and on different threads as the main thread. This means that the Silverlight application continues processing while the request to the Web Service is executed. Before you start a request to a Web Service, in this case a SharePoint web service, you have to grab the context of the UI thread.

syncContext = SynchronizationContext.Current;

The syncContext variable is a class-level variable:

SynchronizationContext syncContext;

 

2. Create the HttpWebRequest object passing it the URL of the SharePoint web service and specify a callback method on which the HttpWebRequest can come back after processing the request. The request will come back on a background thread.

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
        new Uri(siteUrl + "/_vti_bin/Lists.asmx", UriKind.Absolute));
request.Method = "POST";
request.BeginGetRequestStream(new AsyncCallback(RequestCallback),
        request);

 

3. The RequestCallback method contains the code for the soap envelope. The body is retrieved from the request stream by calling the EndGetRequestStream method of the HttpWebRequest and completed with the soap envelope. Then a callback method is defined for the response to come back on. Also the response will come back on a different background thread. The request stream must be closed before the BeginGetResponse method of the HttpWebRequest object is called.

private void RequestCallback(IAsyncResult asyncResult)
{
   try
   {
      string envelope = @"<?xml version=""1.0"" encoding=""utf-8""?>
        <soap12:Envelope xmlns:xsi=""
http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap12=""http://www.w3.org/2003/05/soap-envelope"">
        <soap12:Body>
        <GetListItems xmlns=""
http://schemas.microsoft.com/sharepoint/soap/"">
              <listName>{0}</listName>
              <query><Query xmlns="""">{1}<OrderBy><FieldRef Name=""Title"" /></OrderBy></Query></query>
              <viewFields><ViewFields xmlns="""">
                    <FieldRef Name=""ID"" />
                    <FieldRef Name=""Title"" />
                    <FieldRef Name=""ProductName"" />
                    <FieldRef Name=""ListPrice"" />
                    <FieldRef Name=""Thumbnail"" />
                    <FieldRef Name=""Color"" />
                    <FieldRef Name=""Weight"" />
                    <FieldRef Name=""Size"" />
                    <FieldRef Name=""Description"" />
                 </ViewFields>
              </viewFields>
             <queryOptions><QueryOptions xmlns=""""><IncludeMandatoryColumns>False</IncludeMandatoryColumns></QueryOptions></queryOptions>
        </GetListItems>
        </soap12:Body>
      </soap12:Envelope>";

     string query = string.Empty;
     if (!string.IsNullOrEmpty(searchstring) && searchstring != "Topic...")
     {
        query = "<Where><Contains><FieldRef Name=\"Title\" />" +
           ";<Value Type=\"Text\">{0}</Value></Contains></Where>";
        query = string.Format(query, searchstring);
     }
     envelope = string.Format(envelope, listName, query);
     HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
     request.ContentType = "application/soap+xml; charset=utf-8";
     request.Headers["ClientType"] = "Silverlight";

     Stream requestStream = request.EndGetRequestStream(asyncResult);
     StreamWriter body = new StreamWriter(requestStream);
     body.Write(envelope);
     body.Close();

     request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
  }
  catch (WebException ex)
  {
     responsestring = ex.Message;
  }

        }

 

3. When the response comes back on a background thread, the response is retrieved. If an error occurred along the way, it is captured in a class-level string variable. The UI thread is invoked by calling the Post method on the context variable, passing the address of the ExtractResponse method to it.

private void ResponseCallback(IAsyncResult asyncResult)
{
     HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
     WebResponse response = null;

     try
     {
         response = request.EndGetResponse(asyncResult);
     }
     catch (WebException we)
     {
         responsestring = we.Status.ToString();
     }
     catch (System.Security.SecurityException se)
     {
         responsestring = se.Message;
         if (responsestring == "")
            responsestring = se.InnerException.Message;
     }
     syncContext.Post(ExtractResponse, response);             
}

 

4. The ExtractResponse method executes the GetResponseStream method on the response and retrieves the resulting xml from the stream.

private void ExtractResponse(object state)
{
    HttpWebResponse response = state as HttpWebResponse;

    if (response != null && response.StatusCode == HttpStatusCode.OK)
    {
       using (StreamReader reader = new StreamReader(response.GetResponseStream()))
       {
           responsestring = reader.ReadToEnd();
           ProcessResponse();
       }
    }
    else
       ProcessMessage();
}

 

5. The rest of the code remains the same. The ProcessResponse method parses the resulting XML with LINQ to XML into objects and the object collection is bound to the ListBox.

The update of the list price in the product detail box works in a similar way, except that it executes the UpdateListItems method of the Lists.asmx web service.

If you are configuring the web.config of your SharePoint web applications the “lazy” way please verify the HttpHandlers section. It should look like the following:

  <httpHandlers>
      <remove verb="GET,HEAD,POST" path="*" />
      <add verb="GET,HEAD,POST" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <add verb="OPTIONS,PROPFIND,PUT,LOCK,UNLOCK,MOVE,COPY,GETLIB,PROPPATCH,MKCOL,DELETE,(GETSOURCE),(HEADSOURCE),(POSTSOURCE)" path="*" type="Microsoft.SharePoint.ApplicationRuntime.SPHttpHandler, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <add verb="*" path="Reserved.ReportViewerWebControl.axd" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false" />
      <remove verb="*" path="*.asmx" />
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false" />
  </httpHandlers>

 

Otherwise SharePoint web services and the Silverlight HttpWebRequest object will NOT work.

Silverlight 2 Final Release is out!

Silverlight 2 RTM is out and ready to download. If you want to integrate Silverlight 2 with SharePoint you have to install the following:

  • SP1 for WSS if you have a WSS installation
  • SP1 for MOSS if you have a MOSS installation
  • SP1 for Visual Studio 2008
  • If you are a SharePoint developer you can also install the Visual Studio 2008 extensions for WSS 3.0 (VseWSS 1.2)
  • The Silverlight 2 run-time
  • The Silverlight 2 SDK
  • The Visual Studio 2008 Tools for Silverlight 2

My colleague Jan Tielens wrote a great post on how to upgrade the web.config of your SharePoint site(s) the “lazy way”. It is a great way to quickly upgrade the web.config files of your SharePoint sites without any chances on typos. I tested it out: it still works with Silverlight 2 RTM.

The only thing that need to be added manually to each web.config is the reference to the Silverlight assembly in the <system.web><compilation><assemblies> section:

<add assembly="System.Web.Silverlight, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

Two other things need to be done to get Silverlight working with SharePoint:

  • drop the System.Web.Silveright.dll into the Global Assembly Cache. After the installation of the Silverlight SDK, this assembly can be found in the C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Server directory.
  • If you already worked with the betas of Silverlight 2, you had to define a MIME type on each IIS SharePoint Web Application (or on the root if IIS). You have to change this MIME type to application/x-silverlight-app.

If you are using the System.Web.UI.SilverlightControls.Silverlight control in your SharePoint web parts and application pages, you have to set the MinimumVersion property to “2.0.30923.0

I will soon post my updated data binding sample.

Creating a SharePoint Web Part that hosts a data binding Silverlight 2 application

Recently I wrote an article for an online SharePoint magazine. It’s about hosting a Silverlight 2 beta 2 application in a a SharePoint web part. You can also read the article on the U2U web site.

It is a tutorial in which I explain how you can host a Silverlight 2 beta 2 application from within a SharePoint Web Part. The Web Part will pass the URL of the SharePoint site together with the name of the list for which the Silverlight application will show the data. The retrieval of the data will be done by the Silverlight application using the HttpWebRequest technique for calling the SharePoint web services. As the SharePoint web services return a chunk of XML the XML will be handled by using LINQ for XML. The data will be bound to the Silverlight controls.

By clicking one of the products you can view its details and update the list price. The modified price is stored in the SharePoint list using the HttpWebRequest technique that calls the UpdateListItems method of the Lists.asmx.

Adventureworks listbox

You can download the source code and the sample list templates here.

As we will soon start upgrading the Silverlight Blueprint for SharePoint to Silverlight RTM, this sample will also be upgraded in the coming weeks.

Developing a List Definition with the Visual Studio 2008 extensions for WSS

Developing a list definition for a custom list using the Visual Studio 2008 extensions for WSS is not that hard because there is an existing project template for it. But having the custom fields displayed in the several forms (edit form, new form, display form, etc) can be a challenge. This post offers you a walkthrough on how you can proceed.

Lets say you want to develop a list definition for a custom list having fields like CustomerCode, Name, City, Country and StartDate. The custom list must also contain a customer ID but as the list item will inherit from the standard list item, the ID column will automatically be created. The Name column will be the standard Title column. When a new customer is entered, you also want to check if the customer code is not already used for another customer, so you will need to add an event receiver to the custom list.

Open Visual Studio 2008 and choose to create an empty SharePoint project. Add a new list item of type SharePoint List Definition. In the dialog that appears, select Custom List and check both the check boxes.

image

This is the structure that is created by the Visual Studio extensions for WSS. Notice that the necessary forms like the AllItems.aspx, DispForm.aspx, etc are all there. They will need no modification. The schema.xml file contains the definition of the list template and it is in here that you will have to do the work. The instance.xml file will create a list instance based on this custom list definition upon activation of the feature. The ItemEventReceiver.cs file will contain the necessary code for checking the existence of the customer code. The ItemEventReceiver.xml file on its turn will install the event receiver upon activation of the feature.

image

As we want to develop an item event receiver, we can remove the ListEventReceiver.cs and the ListEventReceiver.xml file from the project.

First we are going to define the schema of the Customer list. Open the schema.xml file and locate the <Fields> node.

image

Enter a field element for each column in the custom list. If you copy/paste, don't forget to change the GUID of the field ID because the ID must be unique.

<Field ID="{7809EF2B-C225-4459-BC81-50F9CBF1A244}" DisplayName="Code"
       Type="Text" Required="TRUE" MaxLength="15" StaticName="Code"
       Name="Code" RowOrdinal="0"
       SourceID="http://schemas.microsoft.com/sharepoint/v3" />

This Field element should be added for each column needed in the custom list. Also pay attention to attributes like

- Name: this is the internal name of the field. This field doesn't allow spaces. In the case of Company Name you can use CompanyName or Company_x0020_Name.

- DisplayName: this is the user friendly name of the field and allows spaces.

- StaticName: give it the same value as the Name attribute. 

- Type: this indicates the data type of the field. Possible values are the SharePoint data types like Text, Note, DateTime, Currency, Number,... 

- Required: indicates wether a value is required or not.

At the top of the schema definition you find a ContentTypes element containing one or more ContentTypeRef elements. Remove these elements and add a new ContentType element. This is the definition of the Customer content type.

The ID attribute shows the inheritance: this content type inherits from the Item content type which has ID 0x01. The ID should then be followed by 00 and a new GUID.

It is necessary to define a content type to make the fields appear in the New, Edit and Display form.

<ContentTypes>
      <ContentType
        ID="0x0100247971ABF81E4ac9B96C7C6287D18772"
        Name="Customer Item"
        Group="U2U Content Types"
        Description="Customer item content type."
        Version="0">

        <FieldRefs>

               <!-- Here comes the reference to the fields -->
        </FieldRefs>

      </ContentType>

</ContentTypes>

The FieldRefs element of the content type contains the field references to the fields you defined in the Fields element. The first field is the reference to the Title field which is part of the Item content type.

<FieldRefs>
  <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" DisplayName="Company Name" />

  <FieldRef
    ID="{7809EF2B-C225-4459-BC81-50F9CBF1A244}"
    Name="Code"
    Required="TRUE"
    ShowInNewForm="TRUE"
    ShowInEditForm="TRUE"/>

  <FieldRef
    ID="{F5BC3F73-88E0-436e-8C2C-C9819FFE2FE3}"
    Name="City"
    Required="FALSE"
    ShowInNewForm="TRUE"
    ShowInEditForm="TRUE"/>

  <FieldRef
    ID="{D5877137-AB9C-4474-93BD-F99B734436D8}"
    Name="Country"
    Required="FALSE"
    ShowInNewForm="TRUE"
    ShowInEditForm="TRUE"/>

  <FieldRef
    ID="{242BF2C4-6459-45f2-B61C-EF7280A701C6}"
    Name="StartDate"
    Required="FALSE"
    ShowInNewForm="TRUE"
    ShowInEditForm="TRUE"/>
</FieldRefs>

 

The content type also contains a XmlDocuments element which is a collection of XmlDocument elements. An XmlDocument element can contain custom information. In this case it defines the form templates to use when displaying the content type.

<XmlDocuments>
    <XmlDocument NamespaceURI="
http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
       <FormTemplates xmlns="
http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
            <Display>ListForm</Display>
            <Edit>ListForm</Edit>
            <New>ListForm</New>
        </FormTemplates>
    </XmlDocument>
</XmlDocuments>

 

Then you have a number of View definitions. To have your fields displayed in the different views, for example the All Items view when viewing the list content, you have to add your custom fields to the ViewFields element of each view.

<ViewFields>
     <FieldRef Name="LinkTitleNoMenu">
     </FieldRef>
     <FieldRef Name="Code" />
     <FieldRef Name="City" />
     <FieldRef Name="Country" />
     <FieldRef Name="StartDate" />
</ViewFields>

 

Now that the list definition is set and done, we can start with the code for the event receiver. When a new customer is entered, you want to check whether the customer code is already used for another customer or not. Open the ItemEventReceiver.cs file and uncomment the ItemAdding event handler:

public override void ItemAdding(SPItemEventProperties properties)
{
    // Check here if the code already exists
    DisableEventFiring();

    string code = null;
    if (properties.AfterProperties["Code"] != null)
    {
        code = properties.AfterProperties["Code"].ToString();
    }

    SPList list = properties.OpenWeb().Lists[properties.ListId];

    if (!ValidateData(list, code))
    {
        properties.Cancel = true;
        properties.ErrorMessage = "This customer code is already in use.";
    }

    EnableEventFiring();
}

 

The ValidateData method is a private method that executes a CAML Query to check whether the customer code already exists or not.

private bool ValidateData(SPList list, string email)
{
   if (email != null)
   {
      SPQuery qry = new SPQuery();
      qry.Query = "<Where><Eq><FieldRef Name='Code' /><Value Type='Text'>"
            + email + "</Value></Eq></Where>";
      SPListItemCollection results = list.GetItems(qry);
      if (results.Count > 0)
          return false;
   }
   return true;
}

 

Time to deploy the list definition. The ListDefinition.xml file contains the definition of the list template. Open the file to modify some metadata. You can change the display name of the list template and set the type to a custom number.

<ListTemplate Name="CustomerListTemplate"
      DisplayName="Customer List Template"
      Description=""
      BaseType="0"
      Type="700"
      OnQuickLaunch="TRUE"
      SecurityBits="11"
      Sequence="410"
      Image="/_layouts/images/itgen.gif" />

 

The instance.xml file defines the list instance that will be created based on your custom list template. Also here you can change f.e. the Title attribute.

<ListInstance FeatureId="bc53ee2c-46b3-41ea-8deb-e75013c92eee"
      Title="Customer List Template instance"
      Url="Lists/CustomerListTemplate">
</ListInstance>

 

Open the project Properties and set the Start browser with URL on the Debug tab to your SharePoint site where you want to deploy your custom list template. Press F5 to start the deploy.

Open an internet browser and navigate to your SharePoint site. The list instance is already created for you. Add a new customer and fill out some data.

image

Add another customer with the same code and you will get an error message indicating that this customer code is already used.

You can download the code here.

Deploying a Silverlight application as an Embedded Resource

While my world stopped turning 10 days ago, technology has not. The coming weeks I will post things I stopped working on because my work for Patrick took too much of my (free) time. By keeping up the work I hope to honor him. Thanks for the many words of support and the condolences.

When integrating Silverlight applications with SharePoint custom development, you have different possibilities where to deploy your silverlight application:

- To the ClientBin folder of your IIS web application: If you deploy in this location, it means that the Silverlight application can be picked up by any SharePoint code that runs in the site collections and sites hosted on the IIS Web Application.

- To the 12\Template\Layouts or in the 12\Template\ControlTemplates folder: deploying here means that you can include all of the deployment steps in your SharePoint Solution.

- To a document library: you can create one central document library within your site collection (or if you want a more narrow scope, for your site) where to drop the XAP files.

I refer to the post of Patrick Tisseghem for more details, advantages and disadvantages of each possibility.

In this post I will show you how you can deploy your Silverlight application as an embedded resource of your web part. In that case the Silverlight application will only be available to that web part.

The first step is to build your silverlight application. I used the Hello Silverlight 2.0 application from the Silverlight BluePrint for SharePoint.

Create your web part

Then create your web part. I used the SharePoint WebPart template for Visual Studio 2008.

Add a reference to the following DLLs:

- System.Web.Silverlight.dll (version 2.0.5.0)

- System.Web.Extensions.dll (version 3.5.0.0)

Add your silverlight application (.xap) to your Visual Studio project. I created a Resources directory for it. Set the properties of the xap to Embedded Resource. Don't forget this because otherwise the Silverlight application will not be compiled as embedded resource.

image 

Open the Web Part class. Before the namespace declaration add the metadata attribute that enables an embedded resource in an assembly:

[assembly: WebResource("EmbeddedSilverlightWebPart.Resources.SL.XAML.HelloSilverlight20.xap", "application/x-silverlight-app")]

Check the correct namespace with Reflector.

Add a class level variable for the Silverlight control. This control resides in the System.Web.Silverlight namespace.

Silverlight silverlightControl;

Override the OnLoad event to check whether a Script Manager already lives on the Web Part page.

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    // Script manager instance may appear only once on a page
    ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
    if (scriptManager == null)
    {
        scriptManager = new ScriptManager();
        this.Controls.AddAt(0, scriptManager);
    }
}

Instantiate the Silverlight control in the CreateChildControls method and add it to the Controls collection. The Source property of the silverlight control need to be set to the location of the embedded resource. This location can be detected by using the  this.Page.ClientScript.GetWebResourceUrl method:

protected override void CreateChildControls()
{
    base.CreateChildControls();

    this.Width = new Unit("400");
    this.Height = new Unit("200");

    string xapUrl = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), "EmbeddedSilverlightWebPart.Resources.SL.XAML.HelloSilverlight20.xap");
    silverlightControl = new System.Web.UI.SilverlightControls.Silverlight()
    {
        ID = "EmbeddedSlCtl",
        MinimumVersion = "2.0.30523",
        Source = xapUrl,
        Width = new System.Web.UI.WebControls.Unit(400),
        Height = new System.Web.UI.WebControls.Unit(200)
    };
    this.Controls.Add(silverlightControl);
}

Override the RenderContents method:

protected override void RenderContents(HtmlTextWriter writer)
{
    EnsureChildControls();
    if (silverlightControl != null)
        silverlightControl.RenderControl(writer);
}

That's it. Build your code.

Deploy the web part

Make the necessary changes to the .webpart file and the .xml file if you want to. Patrick learned me to always add the web part to a significant group in the Web Part Gallery:

<Elements Id="1eba021c-8a0f-4bf2-8f2e-1c9d8f36bf7f" xmlns="http://schemas.microsoft.com/sharepoint/" >
  <Module Name="WebParts" List="113" Url="_catalogs/wp">
    <File Path="EmbeddedSilverlight.webpart" Url="EmbeddedSilverlight.webpart" Type="GhostableInLibrary">
      <Property Name="Group" Value="Silverlight" />
    </File>
  </Module>
</Elements>

Deploy the web part to your SharePoint site. If you use the Visual Studio 2008 extensions for SharePoint, you only have to enter the URL of your SharePoint site in the project properties and press F5.

Don't forget to configure your SharePoint sites for enabling Silverlight if not yet done.

Add the Web Part to a Web Part page

image

You can download the sample web part here.

Thanks to Stefaan Rillaert for pointing me in the right direction.

Using LINQ to XML in combination with the Lists.asmx

Yesterday I was learning how to work with LINQ. I tried to use LINQ to XML to parse the XML that results from calling methods on the Lists.asmx.

I made a small windows application with Visual Studio 2008 to fill a combo box with lists. When you select a list, a grid view is populated with the some information of the fields of the selected list.

image

The combo box is populated as follows:

XmlNode listsResult = listService.GetListCollection();

// Get the Title and the ID from the xml
XDocument results = XDocument.Parse(listsResult.OuterXml);

var lists = from item in results.Descendants(XName.Get("List",         "http://schemas.microsoft.com/sharepoint/soap/"))
            select new
            {
                Title = item.Attribute("Title").Value,
                Id = item.Attribute("ID").Value
            };

ListsComboBox.DataSource = lists.ToList();
ListsComboBox.DisplayMember = "Title";
ListsComboBox.ValueMember = "Id";

The grid is populated as follows:

XmlNode result = listService.GetList(ListsComboBox.Text);
XDocument results = XDocument.Parse(result.OuterXml);

var fields = from item in results.Descendants(XName.Get("Field", "http://schemas.microsoft.com/sharepoint/soap/"))
             where item.Attribute("ID") != null
            select new
            {
                DisplayName = item.Attribute("DisplayName").Value,
                InternalName = item.Attribute("Name").Value,
                TypeAsString = item.Attribute("Type").Value,
                Id = item.Attribute("ID").Value ?? "empty"
            };

FieldsDataGridView.DataSource = fields.ToList();

What a delight not to have to parse all that XML again!

1 - 10 Next
Silverlight BluePrint for SharePoint

 Download my Features

  U2U CAML Query Builder (feature version) version 3.1.0.0
  Site Properties feature version 1.0.0.0
  List Properties feature version 1.0.0.0
  U2U CAML Query Builder version 3.1.0.0 (windows version)
  U2U Job Definition Configurator feature version 1.0.0.0

 Download my Code Samples

CreateContentSourceCmdLet source code
Customer List Definition

 Recommended Books

Thumbnail
Thumbnail
Thumbnail
Thumbnail
Thumbnail
Thumbnail
Thumbnail

 ‭(Hidden)‬ Admin Links