In part 1 you learned how to get SharePoint 2010 and Astoria working together, how to add a Service Reference to your Client Application and how to do a basic insert.

In this installment you will see full CRUD, so you’ll learn how to do queries, data binding, updates and deletes.

The Problem:

In part 1 we created a ‘Make a Suggestion’ feature for a department Line of Business application, by storing suggestions in SharePoint 2010.

Now we need a little Silverlight application for reviewing all those suggestions and making comments on them. So we need to display interesting suggestions, to allow the user to provide a comment, or make edits and deletes.

The Solution:

Step 1: Displaying suggestions

Assuming you’ve set-up your service reference (see part 1) the next step is to create XAML to display the Suggestions:

<UserControl x:Class="DataServicesOverSharepoint.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"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
    <Grid x:Name="LayoutRoot" ShowGridLines="False">
        <Grid.RowDefinitions>
            <RowDefinition Height="5"/>
            <RowDefinition Height="400"/>
            <RowDefinition Height="300"/>
            <RowDefinition Height="30" />
            <RowDefinition Height="5" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition Width="500"/>
            <ColumnDefinition Width="5"/>
        </Grid.ColumnDefinitions>
        <ListBox ItemsSource="{Binding TrackedSuggestions, Mode=TwoWay}" Name="ListBox" Grid.Row="1" Grid.Column="1" ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5">
                        <StackPanel Orientation="Vertical" Margin="5">
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Width="400" Text="{Binding Title}" FontWeight="Bold"/>
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Width="400" Text="{Binding Description}" TextWrapping="Wrap"/>
                            </StackPanel>
                        </StackPanel>
                        <Button Content="Delete" Click="DeleteSuggestion" Tag="{Binding}" Width="50" Height="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <StackPanel Orientation="Vertical" Margin="5" Grid.Column="1" Grid.Row="2">
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="100" Text="Title:" FontWeight="Bold"/>
                <TextBox Width="300" Text="{Binding ElementName=ListBox, Path=SelectedItem.Title, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="100" Text="Description:" FontWeight="Bold"/>
                <TextBox Width="300" Text="{Binding ElementName=ListBox, Path=SelectedItem.Description, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="100" Text="Created:" FontWeight="Bold"/>
                <TextBox Width="300" Text="{Binding ElementName=ListBox, Path=SelectedItem.Created, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="100" Text="Modified:" FontWeight="Bold"/>
                <TextBox Width="300" Text="{Binding ElementName=ListBox, Path=SelectedItem.Modified, Mode=TwoWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Width="100" Text="Comments:" FontWeight="Bold"/>
                <TextBox Width="300" Height="150" Text="{Binding ElementName=ListBox, Path=SelectedItem.Comments, Mode=TwoWay}" AcceptsReturn="True" />
            </StackPanel>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="1" Margin="5">
            <Button Width="100" Name="btnNewSuggestion" HorizontalAlignment="Left" Content="New Suggestion"/>
            <Button Width="100" Name="btnSaveChanges" HorizontalAlignment="Right" Content="Save Changes"/>
        </StackPanel>
    </Grid>
</UserControl>

This XAML code, displays a ListBox (of suggestions) and an series of textboxes for editing the currently selected suggestion.

When running it should looks something like this:

SuggestionsVisualizer

To make this work we have a very simple class to encapsulate Data Services and the Data:

public class SuggestionContext
{
    TeamSiteDataContext _ctx;
    DataServiceCollection<SuggestionsItem> _suggestions;
    public TeamSiteDataContext DataContext
    {
        get
        {
            if (_ctx == null)
            {
                _ctx = new TeamSiteDataContext(
                    new Uri(
http://mflasko-dev/_vti_bin/listdata.svc,
                        UriKind.Absolute
                    )
                 );
            }
            return _ctx;
        }
    }
    public DataServiceCollection<SuggestionsItem> TrackedSuggestions
    {
        get
        {
            if (_suggestions == null)
                _suggestions = DataServiceCollection.CreateTracked<SuggestionsItem>(DataContext);
            return _suggestions;
        }
    }
}

This class has two properties, both evaluated on first access.

One gets the DataServiceContext and the other gets a DataServiceCollection which will hold, and track changes to, a collection of Suggestions.

The SuggestionContext is then exposed as a property of our Silverlight UserControl:

private SuggestionContext _currentContext;
public SuggestionContext CurrentContext {
    get {
        if (_currentContext == null)
            _currentContext = new SuggestionContext();
         return _currentContext;
    }
}

Next in the constructor of our UserControl we use this as the DataContext for our layout root, we call GetSuggestionsWithNoComments() to retrieve the interesting suggestions, and finally we hookup some event handlers for our buttons, so we can add a new suggestion and save any changes we make back to the database.

public MainPage()
{
    InitializeComponent();
    GetSuggestionsWithNoComments();
    LayoutRoot.DataContext = CurrentContext;
    this.btnSaveChanges.Click += new RoutedEventHandler(SaveChanges_Click);
    this.btnNewSuggestion.Click += new RoutedEventHandler(AddNewSuggestion);
}

The GetSuggestionsWithNoComments() method looks like this:

public void GetSuggestionsWithNoComments()
{
    var query = (
           from suggestion in CurrentContext.DataContext.Suggestions
           where suggestion.Comments == null
           select suggestion
    ) as DataServiceQuery<SuggestionsItem>;

    query.BeginExecute(
        (IAsyncResult asyncResult) => Dispatcher.BeginInvoke(() =>
        {
            DataServiceQuery<SuggestionsItem> queryResults = asyncResult.AsyncState as DataServiceQuery<SuggestionsItem>;
            if (queryResults != null)
            {
                CurrentContext.TrackedSuggestions.Clear(true);
                CurrentContext.TrackedSuggestions.Load(
                     queryResults.EndExecute(asyncResult)
                );
            }
        })
        , query
     );
}

It executes an asynchronous query to retrieve the interesting suggestions and load them into the CurrentContext.TrackedSuggestions collection. Which in turn causes the ListBox to fill, because of the data binding we’ve set up.

Step 2: Inserting, Editing and Deleting suggestions

Now in our XAML we’ve already setup two way data binding from our textboxes back to the suggestion selected in the ListBox, so as we make changes in the TextBoxes the Suggestion is modified, and those changes are tracked by the DataServiceCollection.

So that covers Editing.

Insert is handled by the ‘New Suggestion’ button. The event handler looks like this:

void AddNewSuggestion(object sender, RoutedEventArgs e)
{
    CurrentContext.TrackedSuggestions.Add(
        new SuggestionsItem {
            Title = "{Enter a title}",
            Description = "",
            Modified = DateTime.Now,
            Created = DateTime.Now
        }
    );
    ListBox.SelectedIndex = CurrentContext.TrackedSuggestions.Count - 1;
}

As you can see this creates a new Suggestion, adds it to the DataServiceCollection, and then tells the ListBox to select the new Suggestion so it can be modified via our TextBoxes.

In our XAML we added a Delete button to each ListBoxItem, this event is bound to those buttons:

private void DeleteSuggestion(object sender, RoutedEventArgs e)
{
    Button b = sender as Button;
    SuggestionsItem toDelete = b.Tag as SuggestionsItem;
    CurrentContext.TrackedSuggestions.Remove(toDelete);
}

This relies on the fact that when during DataBinding we put the Suggestion in the button’s Tag property, so all we do when the button is clicked, is retrieve the current Suggestion and remove the suggestion from the TrackedSuggestions collection. This is interpreted as a delete.

Step 3: Submitting your changes

Because we are using a DataServiceCollection, every Insert, Edit and Delete is automatically tracked for us.

But as yet they haven’t been committed to the server.

To do that you have to click the “Save Changes” button, which calls this code:

void SaveChanges_Click(object sender, RoutedEventArgs e)
{
    CurrentContext.DataContext.BeginSaveChanges(
        (IAsyncResult asyncResult) =>
            Dispatcher.BeginInvoke(
                () =>
                {
                    CurrentContext.DataContext.EndSaveChanges(asyncResult);
                    GetSuggestionsWithNoComments();
                }
            )
        , CurrentContext
    );
}

As per usual the request is invoked asynchronously, in this case by calling BeginSaveChanges.

One thing to note is that once we’ve actually saved the changes we refresh the list of Suggestions by calling GetSuggestionsWithNoComments() again.

The idea here is that once we’ve edited some suggestions they may not longer be interesting for us, or there maybe some new suggestions on the server.

Summary:

As you can see it is really easy to create a Silverlight application that uses Data Services to query and manipulate list data.

In Part 3 we will cover how to interact with blobs (i.e. Documents, Images etc), and look out for an upcoming blog post covering X-Domain Policy which is important if your Silverlight application isn’t hosted on the same web-server as Sharepoint.

As always don’t hesitate to leave a comment.

Alex James
Program Manager Microsoft