SPDataSource – every SharePoint developer’s friend (part 1)

Sooner or later, nearly every SharePoint developer needs to write code to retrieve all the items from a list and display them – either on the page, or often in a control like a dropdown list control. An example could be retrieving a list of countries, or perhaps person titles for a form:

DropdownFromList

Clearly there are benefits from storing such values in a list, since we (or the client) can then add/edit/delete items easily. To implement this, the developer’s first thought might be to write code like this:

private void bindPersonTitles()
{
// the name of the DropDownList control we are populating is ddlPersonTitles..
using (SPWeb configWeb = SPContext.Current.Site.AllWebs["configuration"])
{
SPList titlesList = configWeb.Lists["PersonTitles"];

foreach (SPListItem titleItem in titlesList.Items)
{
ddlPersonTitles.Items.Add(new ListItem(titleItem.Title, titleItem.ID.ToString()));
}
}
}

Which is fine. Except if we want to filter or sort the list items, we really should use a CAML query instead of iterating through all the items – the code below shows this (sorting on a column called ‘SortOrder’), and also has a slight twist on the previous example in that I’m using data-binding rather than looping through the items ‘manually’:

private void bindPersonTitlesByCamlQuery()
{
using (SPWeb configWeb = SPContext.Current.Site.AllWebs["configuration"])
{
SPList titlesList = configWeb.Lists["PersonTitles"];
SPQuery query = new SPQuery();
query.Query = "<OrderBy><FieldRef Name=\"SortOrder\" /></OrderBy>";
SPListItemCollection titlesItems = titlesList.GetItems(query);

// showing alternative of using data-binding rather iterating through items..
ddlPersonTitles.DataSource = titlesItems;
ddlPersonTitles.DataTextField = "Title";
ddlPersonTitles.DataValueField = "ID";
ddlPersonTitles.DataBind();
}
}

Fine again. Except I can’t help thinking "all this just to take the items from a list and put them in a dropdown??" Surely there must be a better way. And what did Microsoft do every time they needed to do this, surely they didn’t repeat the above code in every place in SharePoint? The answer is no, they used this:

SPDataSource

In short, SPDataSource is a web control which implements IDataSource and saves you writing code like the above. The great thing is that it is extremely flexible, and as we’ll see, can be used for more than you might think. The best thing though, is that being a control it can be used declaratively, so I can bind my dropdown to the list without writing a single line of C# or VB.Net code – all I have to do is set properties correctly. We’ll go through the detail in a second, but the markup to replace the last code sample would look like:

<SPWebControls:SPDataSource runat="server" ID="dsPersonTitles" DataSourceMode="List" 
SelectCommand="<Query><OrderBy><FieldRef Name='SortOrder' Ascending='true' /></OrderBy></Query>"
<SelectParameters>
<asp:Parameter Name="WebUrl" DefaultValue="/configuration/" />
<asp:Parameter Name="ListName" DefaultValue="PersonTitles" />
</SelectParameters>
</SPWebControls:SPDataSource>

<asp:DropDownList runat="server" ID="ddlPersonTitles" CssClass="title" DataSourceID="dsPersonTitles" DataTextField="Title" DataValueField="ID">
</asp:DropDownList>

So we’re doing the following:

  • setting the ‘DataSourceMode’ for the SPDataSource is set to ‘List’ – we’ll examine other possible values shortly
  • setting the ‘SelectCommand’ to the CAML query we want to use – here we’re just sorting on the ‘SortOrder’ field once more
  • telling the SPDataSource which list we want to use by supplying ASP.Net 2.0 parameter objects named ‘WebUrl’ and ‘ListName’ – we’ll dive more into this later
  • finally we bind the dropdown to the data by specifying the DataSourceID to the ID we gave our SPDataSource, and also say which fields in the resultset we want to use for the ‘Text’ and ‘Value’ of the dropdown. Incidentally I recommend using the ID for the value and the Title for the text (as shown above) so that it’s easy to create an SPLookupValue to update a list item – more on this in part 2

We’re only just starting to see the power of SPDataSource, but I love this approach for a couple of reasons:

  • Details of my query aren’t specified in compiled code, so if anything about the list changes (e.g. we restructure our site) I don’t have to recompile and redeploy assemblies
  • Less custom code, less bugs!
  • Setting properties is arguably simpler than writing code

So let’s dive deeper into what we can do – we’ll cover ‘modes’ of SPDataSource in this article and how to dynamically pass parameters to control the query in part 2.

The different ‘modes’ of SPDataSource

SPDataSource isn’t just limited to fetching the items from a list (DataSourceMode = ‘List’). Other possibilities are:

  • CrossList – similar to doing a query with SPSiteDataQuery across all lists in a site collection (for a sample of this see the SharePoint Designer Team blog post linked at the end of this article)
  • ListItem – show field values from a single list item
  • Webs – lists all webs in a site collection
  • ListOfLists – lists all lists in a web

For the last 2 modes I was able to bind but had some difficulty working out values to use for the ‘DataTextField’/’DataValueField’ of my control. I was trying to simply get the web or list name, but none of the obvious values such as ‘Title’, ‘ListName’ etc. were in the resultset. I was unable to find any other information on this but I’m sure some more trial and error would solve it. The ‘ListItem’ mode can be interesting – in the following sample I’m binding a single list item to a DataGrid to show selected fields from the item, which itself is selected by using the ‘ListItemID’ property:

<SPWebControls:SPDataSource runat="server" ID="dsPeople" DataSourceMode="ListItem" UseInternalName="true"> 
<SelectParameters>
<asp:Parameter Name="WebUrl" DefaultValue="/configuration/" />
<asp:Parameter Name="ListID" DefaultValue="34F91B0C-FCF2-455A-ABBA-67724FB4024A" />
<asp:Parameter Name="ListItemID" DefaultValue="1" />
</SelectParameters>
</SPWebControls:SPDataSource>

<asp:GridView ID="grdPeople" runat="server" DataSourceID="dsPeople"
AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="FullName" HeaderText="Blogger name" />
<asp:BoundField DataField="WorkCity" HeaderText="City" />
<asp:BoundField DataField="Blog_x0020_URL" HeaderText="Blog URL" />
</Columns>
</asp:GridView>

This would give something like this, based on an underlying list item which has these fields (no formatting yet applied):

SPDataSource_ListItem_DataGrid

I found I had to specify UseInternalName = "true" and use the ListID rather than ListName parameter in this mode.

Next time – passing parameters to SPDataSource

So far we’ve looked at supplying parameters by using a standard ASP.Net parameter control and specifying the value in the DefaultValue’ property. In fact, ASP.Net has a whole range of parameter controls which can do the work of retrieving a parameter from somewhere and passing it to the SPDataSource, so that’s what we’ll look at next time. Additionally, since we’re often using SPDataSource to bind data to form controls, we’ll look at the common scenario of getting the selected item out of the form control to save back to a lookup field in SharePoint.

<updated>That link to the SPD blog article I mentioned but forgot to link to is http://blogs.msdn.com/sharepointdesigner/archive/2007/04/24/spdatasource-and-rollups-with-the-data-view.aspx – the focus here is mainly on using SPDataSource with a DataView, but there’s some great info and samples.</updated>

Leave a Reply

Your email address will not be published. Required fields are marked *