Click here to Skip to main content
Click here to Skip to main content

Silverlight 4 OData Paging with RX Extensions

, 29 May 2010
Rate this:
Please Sign up or sign in to vote.
An example of Silverlight 4 OData Paging with RX Extensions.

Silverlight 4, OData Paging, RX Extensions, Using View Model

Live example

This article picks up from where the Simple Silverlight 4 Example Using oData and RX Extensions article ends. This article implements paging. It also showcases some fancy RX Extensions code created by Dave Sexton (http://davesexton.com). However, it is a bit ironic that Dave Sexton was not that happy with the code (even though I felt it was so perfect it was suitable for framing).

The issue Dave Sexton had with the code was that he felt that there were alternative ways of writing the code that are more extensible. He provides examples of the alternate versions, as well as a full explanation, in the postings at this link. My goal for this article is to implement paging, using "View Model Style", using the smallest amount of code as possible. While the code in the previous article works, the code by Dave Sexton reduces the code significantly, as well as simplifies the call from the View Model to the Model, to one line of code.

If you are new to View Model Style it is suggested that you read Silverlight View Model Style: An (Overly) Simplified Explanation for an introduction.

Simple Silverlight 4 Example Using OData and RX Extensions

We are starting with the project created in Simple Silverlight 4 Example Using OData and RX Extensions. That tutorial explains how to create the OData service and to register the assemblies for the RX Extensions.

Commanding

Because we will enable paging, we will need to raise an event in the View Model from the View (the UI). We do this using Commanding. To support Commanding, we add the DelegateCommand.cs file to the project. The use of that file is covered in this article by John Papa: http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/.

The Model

In the Simple Silverlight 4 Example Using oData and RX Extensions tutorial, we created a Model that connected to the OData service. We will open it up and replace all the code with the following code:

using System;
using System.Collections.ObjectModel;
using System.Data.Services.Client;
using System.Linq;
using SilverlightODataSample.wsSampleCustomerData;

namespace SilverlightODataSample
{
    public class Model
    {
        public static ObservableCollection<customerrecord> 
               GetCustomers(int intPage, int intRecordsPerPage)
        {
            // Adapted from code by Dave Sexton http://davesexton.com/
            // From: http://social.msdn.microsoft.com/Forums/
            //     en-US/rx/thread/5b12668e-680f-436d-82e6-ebf1e0be66f5/

            // Collection to hold the final results
            var collection = new ObservableCollection<customerrecord>();

            // Create a URI to the service
            var uri = new Uri(GetBaseAddress(), UriKind.RelativeOrAbsolute);

            // Set-up call to OData WCF Service
            var sds = new SampleDataSource(uri);

            // Compute the CurrentPage
            int CurrentPage = ((intPage * intRecordsPerPage) - intRecordsPerPage);

            // Create the query
            var query = (from data in sds.SampleCustomerData
                         select data).Skip(CurrentPage).Take(intRecordsPerPage);

            // A collection to hold the results that the oData Service will return
            var results = new DataServiceCollection<customerrecord>();

            // A variable to represent the Iobservable event 
            var whenLoaded = 
              Observable.FromEvent<loadcompletedeventargs>(results, "LoadCompleted");

            // Subscribe to the Event "LoadCompleted" with "disposable" (IObvervable)
            var disposable = whenLoaded.Subscribe(value =>
            {
                // If we have an error, report it
                if (value.EventArgs.Error != null)
                {
                    throw value.EventArgs.Error;
                }
                else
                {
                    // results contains the values
                    foreach (var item in results)
                    {
                        // Add each element from results 
                        // to the final collection
                        collection.Add(item);
                    }
                }
            });

            // This actually calls the OData WCF Service
            // And kicks everything off
            results.LoadAsync(query);

            // Return the final collection
            return collection;
        }

        #region GetBaseAddress
        private static string GetBaseAddress()
        {
            // This gets the address of the webservice by 
            // getting the AbsoluteUri and then stripping out the 
            // name of the .xap file
            string strXapFile = @"/ClientBin/SilverlightODataSample.xap";
            string strBaseWebAddress =
                App.Current.Host.Source.AbsoluteUri.Replace(strXapFile, "");
            return string.Format(@"{0}/{1}", strBaseWebAddress, @"Service.svc");
        }
        #endregion
    }
}

The View Model

We will also alter the View Model. First, let's look at how simple the code required to call the Model from the View Model is:

#region GetCustomers
private void GetCustomers()
{
    // Fill the colCustomerRecord collection 
    colCustomerRecord = Model.GetCustomers(CurrentPage, RecordsPerPage);
}
#endregion

The DataGrid (or any UI element that can hold a collection that the Designer decides to use) will use colCustomerRecord as its source.

#region CustomerRecord
private ObservableCollection<customerrecord> _colCustomerRecord
    = new ObservableCollection<customerrecord>();
public ObservableCollection<customerrecord> colCustomerRecord
{
    get { return _colCustomerRecord; }
    private set
    {
        if (colCustomerRecord == value)
        {
            return;
        }

        _colCustomerRecord = value;
        this.NotifyPropertyChanged("colCustomerRecord");
    }
}
#endregion

This collection is an ObservableCollection, so it automatically updates the UI element bound to it, whenever it is changed.

The GetCustomers method requires the page requested, and the number of records per page. We create properties in the View Model to hold these values:

#region CurrentPage
private int _CurrentPage = 1;
public int CurrentPage
{
    get
    {
        return this._CurrentPage;
    }
    set
    {
        this._CurrentPage = value;
        this.NotifyPropertyChanged("CurrentPage");
    }
} 
#endregion

#region RecordsPerPage
private int _RecordsPerPage = 10;
public int RecordsPerPage
{
    get
    {
        return this._RecordsPerPage;
    }
    set
    {
        this._RecordsPerPage = value;
        this.NotifyPropertyChanged("RecordsPerPage");
    }
} 
#endregion

Next, we create ICommands to allow the View to page back and forth:

#region PreviousPageCommand
public ICommand PreviousPageCommand { get; set; }
public void PreviousPage(object param)
{
    CurrentPage--;
    GetCustomers();
}

private bool CanPreviousPage(object param)
{
    // Must not already be on the first page
    return (CurrentPage > 1);
}
#endregion

#region NextPageCommand
public ICommand NextPageCommand { get; set; }
public void NextPage(object param)
{
    CurrentPage++;
    GetCustomers();
}

private bool CanNextPage(object param)
{
    // There must be records to move to the next page
    return (colCustomerRecord.Count > 0);
}
#endregion

We also create an ICommand to allow the records per page to be set:

#region SetRecordsPerPageCommand
public ICommand SetRecordsPerPageCommand { get; set; }
public void SetRecordsPerPage(object param)
{
    ContentControl Element = (ContentControl)param;
    RecordsPerPage = Convert.ToInt32(Element.Content);
    GetCustomers();
}

private bool CanSetRecordsPerPage(object param)
{
    return (param != null);
}
#endregion

We implement INotifyPropertyChanged to support automatic notifications when properties are changed. For example, so the UI can be automatically updated when a property such as current page is changed.

It is not really needed in this example, because the only control bound to the property is the one actually changing the value. However, if we had a text box that displayed the current page, we would need to implement this to enable automatic update notification:

#region INotifyPropertyChanged
// This is a supporting method to raise a notification for any
// Element that is subscribed to a Property that implements
// NotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(String info)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}
#endregion

Finally, this is the constructor for the View Model. It sets up the ICommands and calls GetCustomers to load the first page:

public MainPageModel()
{
    // Set the command properties
    PreviousPageCommand = 
      new DelegateCommand(PreviousPage, CanPreviousPage);
    NextPageCommand = new DelegateCommand(NextPage, CanNextPage);
    SetRecordsPerPageCommand = 
      new DelegateCommand(SetRecordsPerPage, CanSetRecordsPerPage);

    // Get the Customers
    GetCustomers();
}

Creating the View

We complete the example by re-creating the View.

In the previous article, it simply pulled up the first page.

Now we add paging buttons, and a combo-box to allow the records per page to be selected. The diagram above shows what is wired to what.

Note: For the diagram, I enabled design time sample data. You can learn how to do that in this article: Silverlight MVVM: Enabling Design-Time Data in Expression Blend When Using Web Services.

The steps to create the View were covered in: Simple Silverlight 4 Example Using oData and RX Extensions. However, the buttons and the combo-box are new, so let's walk through the steps the Designer would use to implement them.

The Paging Buttons

Using Expression Blend 4+, we first drop two buttons on to the page.

Next, we drop an InvokeCommandAction Behavior on each button.

In the Properties for each Behavior, we select Click for the EventName and we click the Advanced Options box next to Command.

We select Data Binding...

And we bind to the appropriate command.

The Combo Box

We also drop an InvokeCommandAction Behavior on the combobox.

In the Properties for the Behavior, we set EventName to SelectionChanged.

We bind the Command to SetRecordsPerPageCommand.

However, in this case, we also need to pass the number of records selected, so we click the Advanced Options box next to CommandParameter.

We then bind CommandParameter to SelectedValue.

"View Model Style", Simple - Not a Lot of Code

Hopefully, you find this example simple and easy to understand. "View Model Style" can be used for 100% of your Silverlight projects. The amount of code required to use a View Model is about the same amount that would be required if you simply used code-behind. The reason for using View Model vs. code-behind is that you allow your UI to be completely designed by a Designer who does not know how to code. You may find that this is vital to producing professional looking applications. It is also testable, see: Unit Testing a Silverlight View Model Style Modal Popup.

If you require a more complex architecture, you can implement MVVM by adding additional abstraction. For example, instead of actually calling the OData service from the Model, you would create a "service class" that actually made the call to the service and filled the Model. The View, however, would not require any changes.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and C# programmer.
 
He is the founder of http://LightSwitchHelpWebsite.com
 
He has a son, Zachary and resides in Los Angeles with his wife Valerie.
Follow on   Twitter

Comments and Discussions

 
GeneralAnother Nice one PinmentorKunalChowdhury31-May-10 17:31 
GeneralRe: Another Nice one Pinmemberdefwebserver31-May-10 17:37 
GeneralNetflix Coupon Code Pinmemberadorina31-May-10 1:47 
GeneralRe: Netflix Coupon Code PinmentorKunalChowdhury31-May-10 17:28 
GeneralGreat article Pinmemberlinuxjr29-May-10 17:14 
GeneralRe: Great article Pinmemberdefwebserver29-May-10 18:37 
GeneralI'm sure that DataGrid is getting uglier!!! PinmemberAlan Beasley29-May-10 8:02 
GeneralRe: I'm sure that DataGrid is getting uglier!!! Pinmemberdefwebserver29-May-10 11:56 
GeneralRe: I'm sure that DataGrid is getting uglier!!! PinmemberAlan Beasley29-May-10 12:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140718.1 | Last Updated 29 May 2010
Article Copyright 2010 by defwebserver
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid