Thursday, May 17, 2012    
Blog  

OpenLight Blog

Using The Silverlight Data Pager Control With View Model / MVVM

Oct 10

Written by:
10/10/2010 1:07 PM  RssIcon

image

Live Sample: at this link

I really like the Silverlight Data Pager Control. It has a lot of cool features. It is primarily designed to be used with a PagedCollectionView, and supports the following modes:

image

(image © Microsoft Corporation)

The problem with the Data Pager is that it only works when you have all the data loaded. If you load all your records into a DataGrid, and hook up the Data Pager, it will easily page the contents of the DataGrid if you wrap the items in a PagedCollectionView, but, if you want to pull up each page from the server (using web services), it wont work. Your only option is to use it with the Domain Data Source control and WCF RIA.

Faking A Collection For The Data Pager

The issue is that the Data Pager Control, or the PagedCollectionView that it is hooked up to, needs to have access to the entire collection to figure out how many pages to display links for.

The sample code creates a “fake collection” of items, tells the PagedCollectionView how many items per page, and the Data Pager Control happily shows everything correctly. When the Data Pager is clicked, a Silverlight behavior detects that the Data Pager has changed the page (in it’s fake collection of items), and a web service call is made to actually get the page requested.

When the page comes back from the web service, the “fake collection” is updated and the PagedCollectionView is refreshed. This is done because in the case of a search, the collection may change from 100 to 50 items. The records to display per page is also updated.

The Web Service

This is the web service method that provides one page of records (colCustomerRecord). However, notice the one page of records is wrapped in an object (objCustomerSearchResult), that returns the total records, and the number of records per page, as well as the current page the collection represents. These are the items that the PagedCollectionView will need to provide to the Data Pager Control, for it to properly work:

    #region GetCustomers
    [WebMethod]
    public CustomerSearchResult GetCustomers(int intPage, int intRecordsPerPage)
    {
        CustomerSearchResult objCustomerSearchResult = new CustomerSearchResult();
        objCustomerSearchResult.colCustomerRecord = new List<CustomerRecord>();
        var colCustomers = from Customers in SampleDataSource()
                            select Customers;
        // Compute the CurrentPage
        int CurrentPage = ((intPage * intRecordsPerPage) - intRecordsPerPage);
        // Implement paging
        objCustomerSearchResult.colCustomerRecord = colCustomers.Skip(CurrentPage).Take(intRecordsPerPage).ToList();
        objCustomerSearchResult.CurrentPage = CurrentPage;
        objCustomerSearchResult.RecordsPerPage = intRecordsPerPage;
        objCustomerSearchResult.TotalPages = (SampleDataSource().Count / intRecordsPerPage);
        return objCustomerSearchResult;
    }
    #endregion

 

In the Silverlight project, here is the Model:

 

    #region GetCustomers
    public static void GetCustomers(int intPage, int intRecordsPerPage, 
        EventHandler<GetCustomersCompletedEventArgs> eh)
    {
        // Set up web service call
        WebServiceSoapClient WS = new WebServiceSoapClient();
        // Set the EndpointAddress
        WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
        WS.GetCustomersCompleted += eh;
        WS.GetCustomersAsync(intPage, intRecordsPerPage);
    }
    #endregion

 

This is the method in the ViewModel,that calls the method in the Model:

 

    #region GetCustomers
    private void GetCustomers()
    {
        // Call the Model to get the collection 
        Model.GetCustomers(CurrentPage, RecordsPerPage, (Sender, EventArgs) =>
        {
            if (EventArgs.Error == null)
            {
                CustomerSearchResult objCustomerSearchResult = EventArgs.Result;
                // Clear the current Customer records
                colCustomerRecord.Clear();
                // loop thru each item
                foreach (var Customer in objCustomerSearchResult.colCustomerRecord)
                {
                    // Add to the collection
                    colCustomerRecord.Add(Customer);
                }
                // Update FakeEnumerableCollection
                FakeEnumerableCollection.Clear();
                for (int i = 1; i <= (objCustomerSearchResult.RecordsPerPage * 
                    objCustomerSearchResult.TotalPages); i++)
                {
                    FakeEnumerableCollection.Add(i);
                }
                CustomerRecordPCV.PageSize = objCustomerSearchResult.RecordsPerPage;
                CustomerRecordPCV.Refresh();
            }
        });
    }
    #endregion

 

Notice that the PagedCollectionViewCustomerRecordPCV is refreshed. The Data Pager Control is bound to this collection. When the collection is refreshed, the Data Pager control will be refreshed.

All we needed to do to hook up the PagedCollectionView to the FakeEnumerableCollection, was to put one line (CustomerRecordPCV = new PagedCollectionView(FakeEnumerableCollection)), in the constructor of the View Model:

 

    public MainPageModel()
    {
        // Set the command properties
        SetRecordsPerPageCommand = new DelegateCommand(SetRecordsPerPage, CanSetRecordsPerPage);
        SetRecordsByPagerCommand = new DelegateCommand(SetRecordsByPager, CanSetRecordsByPager);
        // Get the Customers
        GetCustomers();
        // Attach CustomerRecordPCV (PagedCollectionView) to FakeEnumerableCollection
        CustomerRecordPCV = new PagedCollectionView(FakeEnumerableCollection);
    }

 

Paging

image

The diagram above shows how most of the elements are hooked up. Note:

  • CurrentPagerPage supports two way binding. The code sample does not set it from the View Model, but it could be set from the View Model if needed. In the sample code we use this value to determine what page the Data Pager is on.
  • The Data Grid is only bound to ColCustomerRecord. it has no direct connection to the Data Pager, or any collection the Data Pager is connected to.

 

We use a behavior to trigger an ICommand whenever the DataPager changes. This is the ICommand that is called:

 

    #region SetRecordsByPagerCommand
    public ICommand SetRecordsByPagerCommand { get; set; }
    public void SetRecordsByPager(object param)
    {
        CurrentPage = (CurrentPagerPage + 1);
        GetCustomers();
    }
    private bool CanSetRecordsByPager(object param)
    {
        return true;
    }
    #endregion

 

Faking It? Is It Worth It?

You may not like that the Data Pager is bound to “fake data” and wonder if it is worth the effort? In a project that I am working on it is. The Data Pager is a powerful control that is easily styled. I will allow the end user to switch between the different paging styles dynamically.

For the functionality, it is not a lot of code. I have covered simpler paging in Using The Silverlight DataGrid with View Model, but it does not compare to look and feel of the Data Pager Control.

 

Download

Download the code at this link:

http://silverlight.adefwebserver.com/ViewModelPager/ViewModelPager_Source.zip

12 comment(s) so far...


Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

that's very good article! thanks!

By Calabonga on   10/11/2010 3:23 PM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

There is no way to just set the total count of the pagedCollectionView instead of filling it with fake data?

By dsoltesz on   10/28/2010 8:22 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

@dsoltesz - No other way that I have been able to find so far :)

By Michael Washington on   10/28/2010 8:23 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

Ask and ye shall receive. Outstanding.

By Richard Waddell on   11/1/2010 4:56 PM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

Hello Michael

Can this be implemented using WCF RIA Service? I have doubt on the whether model can be able to access the domain service that holds the method to return page collection value via domain context. Please clarify.


Best Regards
Dhinesh Kumar

By Dhinesh Kumar on   1/11/2011 5:15 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

@Dhinesh Kumar - I don't know, sorry.

By Michael Washington on   1/11/2011 5:28 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

Thanks Michael, you might want to use this instead of filling your memory with numbers. imagine 2 million records.

warning not tested.

public class PageEnumerable : IEnumerable
{
private int _max;

public PageEnumerable(int Lenght)
{
_max = Lenght;
}

public IEnumerator GetEnumerator()
{
return new PageEnumerator(_max);
}
}

public class PageEnumerator : IEnumerator
{
int position = -1;
private int _max;
public PageEnumerator(int Lenght)
{
_max = Lenght;
}

public bool MoveNext()
{
position++;
return (position < _max);
}

public void Reset()
{
position = -1;
}

public object Current
{
get { return position + 1; }
}

}

By Andres Bueno on   2/7/2011 6:04 PM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

after playing with this and some other things, I realized that the best approach is to do something like what is implemented on weblogs.asp.net/manishdalal/archive/2009/10/01/silverlight-3-custom-sorting-with-paging-support.aspx

what is unique about this is that you get notified OnRefresh(one time, when needed) from the Paged Sortable CollectionView, that way you can bypass in memory(sorting/paging) logic from the ms PagedCollectionView. You also use the same source for both the pager and the grid. By the way I am using the MVVM model on SL 4 with llblgen 3.1 Beta.

By Andres Bueno on   2/11/2011 1:17 PM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

Awesome!
I was looking to using datapager without having to provide to it a full enumerable dataview of my datas.
You gave the way!
Greets also to Andres Bueno for his good "fake enumerable" idea.

By blaise braye on   3/8/2011 5:08 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

I am getting the following the following error. Please help me...

Message: Unhandled Error in Silverlight Application
Code: 4004
Category: ManagedRuntimeError
Message: System.ArgumentOutOfRangeException: The PageIndex property can only be set to -1 when the Source property is null or the PageSize property is 0.
Parameter name: value
at System.Windows.Controls.DataPager.OnPageIndexPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.RaisePropertyChangeNotifications(DependencyProperty dp, Object oldValue, Object newValue)
at System.Windows.DependencyObject.UpdateEffectiveValue(DependencyProperty property, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, ValueOperation operation)
at System.Windows.DependencyObject.RefreshExpression(DependencyProperty dp)
at System.Windows.Data.BindingExpression.SendDataToTarget()

By Avinash on   7/5/2011 4:25 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

@Avinash - Download my code and compare it to your own.

By admin account on   7/5/2011 4:27 AM
Gravatar

Re: Using The Silverlight Data Pager Control With View Model / MVVM

Here is a better solution concerning the virtua paging:
www.craigwardman.com/blog/index.php/2011/08/virtual-paging-with-silverlightwcf-services/

By Christophe on   9/29/2011 3:14 AM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
  
Copyright 2009 by OpenLightGroup.net   |  Privacy Statement  |  Terms Of Use