SharePoint for Developers Part 4 – Consuming SharePoint Web Services from Silverlight

Part 4 of the SharePoint for Developers screencast series has been posted to Channel9… this one focusing on calling SharePoint Web Services from a Silverlight client application.

Show Me the Code!

I started by creating a list in SharePoint based off the Links template called “SharePointSites”.  I added 2 links to it.

image

That gives us some data, the next step is to build a UI.  The XAML for the application is really simple, we use databinding in a ListBox to display some items.

 <UserControl
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SLScreencastDemo.Page"
    Width="Auto" Height="Auto" >

    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox x:Name="myList">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

We are going to do most of the work here in the code-behind.  Add a service reference, appending “_vti_bin/Lists.asmx” to your site’s URL. 

image

This is an important point to watch, as I have seen many developers spend hours debugging this issue.  The web services operate relative to your site’s URL.  If your list resides in a subsite, then make sure to access it from the subsite and not the root web.  If you are using WCF as a client, it will sometimes change the URL in the app.config to point to the root web… if you encounter weird issues, this should be the first thing to check.

Once we have the service reference, we write some code to call the service and query the results.  Make sure you have a reference to System.Xml.Linq in your project for this to work as expected.  Also make sure to add using statements for System.Linq and System.Xml.Linq in your code.

 using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

using SilverlightApplication4.SharePointListService;
using System.Linq;
using System.Xml.Linq;

namespace SLScreencastDemo
{
    public partial class Page : UserControl
    {        
        public Page()
        {
            // Required to initialize variables
            InitializeComponent();
            ListsSoapClient proxy = new ListsSoapClient();
            proxy.GetListItemsCompleted += new 
                EventHandler<GetListItemsCompletedEventArgs>(proxy_GetListItemsCompleted);

            //Define the parameters for the service call
            XElement query = new XElement("Query");
            XElement queryOptions = new XElement("QueryOptions");
            XElement viewFields = new XElement("ViewFields");

            proxy.GetListItemsAsync("SharePointSites",
                null,
                query,
                viewFields,
                null,
                queryOptions,
                null);
        }

        void proxy_GetListItemsCompleted(object sender, GetListItemsCompletedEventArgs e)
        {
            XNamespace ns = "#RowsetSchema";

            var query = from x in e.Result.Descendants()
                        where x.Name == ns + "Row"
                        select x.Attribute("ows_URL").Value;

            myList.ItemsSource = query;
        }

    }
}

The LINQ statement here is very easy to understand, the only part that bears explanation is the use of namespaces.  If you look at the XML below (the entire SOAP payload of calling the Lists.asmx SharePoint web service against a Links list with 2 items in it), you can see that the “Row” element uses the “z” namespace prefix, which is bound to the namespace “#RowsetSchema”.  You can also see where I got the “ows_URL” attribute name from in the payload below.  Make sure to scroll down, there’s more after this :)

 <soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="https://www.w3.org/2001/XMLSchema">
  <s:Header xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">    
  </s:Header>
  <soap:Body>
    <GetListItemsResponse xmlns="https://schemas.microsoft.com/sharepoint/soap/">
      <GetListItemsResult>
        <listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
                   xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
                   xmlns:rs="urn:schemas-microsoft-com:rowset"
                   xmlns:z="#RowsetSchema">
          <rs:data ItemCount="2">
            <z:row ows_ContentTypeId="0x010500822AC2A958633F4BBC75D0C99D37B085"
                   ows_URL="https://msdn.microsoft.com/sharepoint, https://msdn.microsoft.com/sharepoint"
                   ows_ID="1"
                   ows_ContentType="Link"
                   ows_Modified="2009-04-28 12:29:21"
                   ows_Created="2009-04-28 12:29:21"
                   ows_Author="8;#Kirk Evans"
                   ows_Editor="8;#Kirk Evans"
                   ows_owshiddenversion="1"
                   ows_WorkflowVersion="1"
                   ows__UIVersion="512"
                   ows__UIVersionString="1.0"
                   ows_Attachments="0"
                   ows__ModerationStatus="0"
                   ows_SelectTitle="1"
                   ows_Order="100.000000000000"
                   ows_GUID="{A3BA6DAF-A8CD-4157-A569-9F0943D07A68}"
                   ows_FileRef="1;#sites/csnadpe/Lists/SharePointSites/1_.000"
                   ows_FileDirRef="1;#sites/csnadpe/Lists/SharePointSites"
                   ows_Last_x0020_Modified="1;#2009-04-28 12:29:21"
                   ows_Created_x0020_Date="1;#2009-04-28 12:29:21"
                   ows_FSObjType="1;#0"
                   ows_PermMask="0x7fffffffffffffff"
                   ows_FileLeafRef="1;#1_.000"
                   ows_UniqueId="1;#{10BAC731-C403-4F35-BF2D-1F60AAA28A71}"
                   ows_ProgId="1;#"
                   ows_ScopeId="1;#{9F0E48CE-69DB-4ED7-A236-7E3BF5D5DF94}"
                   ows__EditMenuTableStart="1_.000"
                   ows__EditMenuTableEnd="1"
                   ows_LinkFilenameNoMenu="1_.000"
                   ows_LinkFilename="1_.000"
                   ows_ServerUrl="/sites/csnadpe/Lists/SharePointSites/1_.000"
                   ows_EncodedAbsUrl="https://sharepoint/sites/csnadpe/Lists/SharePointSites/1_.000"
                   ows_BaseName="1_"
                   ows_MetaInfo="1;#"
                   ows__Level="1"
                   ows__IsCurrentVersion="1"
                   ows_URLwMenu="https://msdn.microsoft.com/sharepoint, https://msdn.microsoft.com/sharepoint"
                   ows_URLNoMenu="https://msdn.microsoft.com/sharepoint, https://msdn.microsoft.com/sharepoint">              
            </z:row>
            <z:row ows_ContentTypeId="0x010500822AC2A958633F4BBC75D0C99D37B085"
                   ows_URL="https://mssharepointdeveloper.com, https://mssharepointdeveloper.com"
                   ows_ID="2"
                   ows_ContentType="Link"
                   ows_Modified="2009-04-28 12:29:32"
                   ows_Created="2009-04-28 12:29:32"
                   ows_Author="8;#Kirk Evans"
                   ows_Editor="8;#Kirk Evans"
                   ows_owshiddenversion="1"
                   ows_WorkflowVersion="1"
                   ows__UIVersion="512"
                   ows__UIVersionString="1.0"
                   ows_Attachments="0"
                   ows__ModerationStatus="0"
                   ows_SelectTitle="2"
                   ows_Order="200.000000000000"
                   ows_GUID="{D9A4ABC2-B5FC-41AA-8C8A-4D344636BB74}"
                   ows_FileRef="2;#sites/csnadpe/Lists/SharePointSites/2_.000"
                   ows_FileDirRef="2;#sites/csnadpe/Lists/SharePointSites"
                   ows_Last_x0020_Modified="2;#2009-04-28 12:29:32"
                   ows_Created_x0020_Date="2;#2009-04-28 12:29:32"
                   ows_FSObjType="2;#0"
                   ows_PermMask="0x7fffffffffffffff"
                   ows_FileLeafRef="2;#2_.000"
                   ows_UniqueId="2;#{C1E4E433-2C2B-4AF1-B84D-60BEF4FB78F3}"
                   ows_ProgId="2;#"
                   ows_ScopeId="2;#{9F0E48CE-69DB-4ED7-A236-7E3BF5D5DF94}"
                   ows__EditMenuTableStart="2_.000"
                   ows__EditMenuTableEnd="2"
                   ows_LinkFilenameNoMenu="2_.000"
                   ows_LinkFilename="2_.000"
                   ows_ServerUrl="/sites/csnadpe/Lists/SharePointSites/2_.000"
                   ows_EncodedAbsUrl="https://sharepoint/sites/csnadpe/Lists/SharePointSites/2_.000"
                   ows_BaseName="2_"
                   ows_MetaInfo="2;#"
                   ows__Level="1"
                   ows__IsCurrentVersion="1"
                   ows_URLwMenu="https://mssharepointdeveloper.com, https://mssharepointdeveloper.com"
                   ows_URLNoMenu="https://mssharepointdeveloper.com, https://mssharepointdeveloper.com">              
            </z:row>
          </rs:data>
        </listitems>
      </GetListItemsResult>
    </GetListItemsResponse>
  </soap:Body>
</soap:Envelope>

In our LINQ statement, we used the XNamespace type to create the namespace, and then simply concatenate the XNamespace to the element name to query an element in a given namespace.  The other thing to point out is the naming of attributes.  The value we were going after here is “ows_URL”, which contains the URL value in a list based on the Links template. 

Configure the clientaccesspolicy.xml and crossdomain.xml Files with SharePoint Designer 2007

By this point, you are probably getting a warning in the Error List window in Visual Studio 2008 that says something like:

Warning    1    Could not locate cross-domain policy at 'sharepoint': Web services might not be accessible at runtime by your Silverlight application. For more details please contact the web server administrator or press F1 after selecting this warning        0    0   

For Silverlight to be able to call web services in another domain, the server hosting the services must expose the clientaccesspolicy.xml and crossdomain.xml files from its root.  Simply open SharePoint Designer and open the root web site and add the crossdomain.xml and clientaccesspolicy.xml files to the site.

image

Once you do that, you should now be able to hit F5 and run the application, seeing the 2 list items show up in your Silverlight application.  In future screencasts in this series, we’ll show how to deploy the Silverlight application within SharePoint using VSeWSS.

For More Information

SharePoint for Developers Part 4 (screencast) – Calling SharePoint Web Services from Silverlight

Network Security Access Restrictions in Silverlight 2

Enabling access to SharePoint data to a Silverlight application running outside the SharePoint context

Calling SharePoint Lists Service using WCF

SharePoint Web Services

SharePoint DevWiki - SharePoint Web Services