This is a migrated thread and some comments may be shown as answers.

RadDataPager, RadGridView and IPagedCollection - Approach to implementing IPagedCollection

10 Answers 174 Views
GridView
This is a migrated thread and some comments may be shown as answers.
Tim
Top achievements
Rank 1
Tim asked on 17 Sep 2010, 09:28 PM
First of all, I have read the blog posts (http://blogs.telerik.com/rossenhristov/posts/10-03-10/q1_2010_new_feature_paging_with_radgridview_for_silverlight_and_wpf.aspx) and I am not using RIA services.

Second, I have a working paged implementation using QueryableCollectionView to wrap my ObservableCollection.  The problem is that I know how big the total dataset is, but I'm not allowed to set TotalItemCount.  I want to return 500 objects at a time from a WCF service, so as the user pages, I would like to fill in 500 new objects - BUT I want the pager to say I am on "Page 1 of 200" or "Items 1-100 of 1000" and when I get to the end of the 500 objects, go get the next 500 (I.e., the IPagedCollectionView interface.)  If I update the collection in the Paging event handlers, then the "of x" part goes up, as expected, but unless you page "off the end" it gives the illusion there are only 500 objects.

From searching, the solution seems to be that I must implement the IPagedCollectionView interface myself.

Does anyone have a recommended approach or complete example?  Should I extend ObservableCollection and implement IPagedCollectionView there, for example?

I studied the section from the post above

IV. Paging collections implementing IPagedCollectionView


And it would seem that if both my RadDataPager.Source and RadGridView.ItemsSource are bound to a QueryableCollectionView, it should work, but it doesn't - I need to point the RadDataPager's source at the ElementName=RadGridView, Path=Items for it to work.

In short, think of it as a lazy-load, x objects at a time out of a known total of y objects, where the IPagedCollectionView members notice the holes and fetch the data on-demand.

Thanks,
Tim

10 Answers, 1 is accepted

Sort by
0
Rossen Hristov
Telerik team
answered on 22 Sep 2010, 08:23 AM
Hello Tim,

I have good news for you. Due to similar requests we have developed a new feature of RadDataPager called Unbound Mode. Basically, you do not set any Source for the pager, i.e.e you do not need to implement the IPagedCollectionView interface. Instead you can set the ItemCount property manually. Once you set the ItemCount and PageSize the pager does the math and know the number of pages.

You can think of the Unbound Mode as of using the pager solely for its UI. Nothing more. When the user goes to a page, the PageIndexChanged event will be raised. By attaching to this event, in the event handler you will get the page index that user has requested. Knowing this page index, you can call any kind of service and return the appropriate data to the client.

This approach is way more easier than implementing the whole IPagedCollectionView interface.

You will need to upgrade to the Service Pack version coming out tomorrow in order to have this feature.

I hope this helps.

Best wishes,
Ross
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tim
Top achievements
Rank 1
answered on 22 Sep 2010, 01:52 PM
That's good news, thanks.  I can't upgrade just yet unfortunately.  I modified your EndlessPagedCollectionView example to suit my needs for now.  I've included it for reference.  I called it BoundedPagedCollectionView since it knows the upper bound in advance, but pages in chunks on-demand.  The WCF service call is simulated with a DispatchTimer and completed asynchronously when the timer fires.

Tim

using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows.Threading;
using Telerik.Windows.Controls;
using System.ComponentModel;
using System.Collections.Specialized;
 
namespace SilverlightApplication1
{
    /// <summary>
    ///
    /// </summary>
    public class BoundedPagedCollectionView : IEnumerable
        , IPagedCollectionView
        , INotifyPropertyChanged
        , INotifyCollectionChanged
    {
        public event EventHandler<EventArgs> PageChanged;
        public event EventHandler<PageChangingEventArgs> PageChanging;
        public event PropertyChangedEventHandler PropertyChanged;
        public event NotifyCollectionChangedEventHandler CollectionChanged;
  
        private int pageIndex = -1;
        private bool isPageChanging;
        private int pageSize;
        private int MaxOrders;
        private RadGridView GridView;
  
        private readonly List<Order> Orders = new List<Order>();
 
        public BoundedPagedCollectionView(int maxOrders, RadGridView gridView)
        {
            MaxOrders = maxOrders;
            GridView = gridView;
            PageChanging += BoundedPagedCollection_PageChanging;
        }
 
        bool IPagedCollectionView.CanChangePage
        {
            get { return true; }
        }
  
        bool IPagedCollectionView.IsPageChanging
        {
            get { return this.isPageChanging; }
        }
  
        private void SetIsPageChanging(bool value)
        {
            if (this.isPageChanging != value)
            {
                this.isPageChanging = value;
                this.OnPropertyChanged("IsPageChanging");
            }
        }
  
        int IPagedCollectionView.ItemCount
        {
            get
            {
                return MaxOrders;
            }
        }
  
        bool IPagedCollectionView.MoveToFirstPage()
        {
            return ((IPagedCollectionView)this).MoveToPage(0);
        }
  
        bool IPagedCollectionView.MoveToLastPage()
        {
            return ((IPagedCollectionView)this).MoveToPage(((IPagedCollectionView)this).TotalItemCount / ((IPagedCollectionView)this).PageSize);
        }
  
        bool IPagedCollectionView.MoveToNextPage()
        {
            return ((IPagedCollectionView)this).MoveToPage(((IPagedCollectionView)this).PageIndex + 1);
        }
 
        private int __pageIndex;
        bool IPagedCollectionView.MoveToPage(int pageIndex)
        {
            if (this.OnPageChanging(pageIndex) && pageIndex != -1)
            {
                return false;
            }
 
            __pageIndex = pageIndex;
            if (Orders.Count < (__pageIndex + 1) * ((IPagedCollectionView)this).PageSize)
            {
 
                //
                // This simulates a WCF callback in that the update comes in on a different thread
                // at some later point - 500 milliseconds.
                //
 
                DispatcherTimer pageTimer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 0, 0, 500)};
                pageTimer.Tick += CompletionCallback;
                pageTimer.Start();
                GridView.IsBusy = true;
            }
            else
            {
                this.SetIsPageChanging(true);
                this.pageIndex = __pageIndex;
                this.SetIsPageChanging(false);
 
                this.OnPropertyChanged("PageIndex");
                this.OnPropertyChanged("ItemCount");
                this.OnPageChanged();
 
                // Call this to inform all listeners that data has changed.
                this.OnCollectionChanged();
            }
            return true;
        }
  
        private void CompletionCallback(object sender, EventArgs e)
        {
            (sender as DispatcherTimer).Stop();
 
            if (Orders.Count < (__pageIndex + 1) * ((IPagedCollectionView)this).PageSize)
            {
                for (int i = Orders.Count; i < (__pageIndex + 1) * ((IPagedCollectionView)this).PageSize && i < MaxOrders; i++)
                {
                    Orders.Add(new Order(i));
                }
            }
 
            GridView.IsBusy = false;
            this.SetIsPageChanging(true);
            this.pageIndex = __pageIndex;
            this.SetIsPageChanging(false);
 
            this.OnPropertyChanged("PageIndex");
            this.OnPropertyChanged("ItemCount");
            this.OnPageChanged();
 
            // Call this to inform all listeners that data has changed.
            this.OnCollectionChanged();
        }
         
        bool IPagedCollectionView.MoveToPreviousPage()
        {
            return ((IPagedCollectionView)this).MoveToPage(((IPagedCollectionView)this).PageIndex - 1);
        }
  
        int IPagedCollectionView.PageIndex
        {
            get { return this.pageIndex; }
        }
  
        int IPagedCollectionView.PageSize
        {
            get
            {
                return this.pageSize;
            }
            set
            {
                if (value < 1)
                {
                    throw new ArgumentOutOfRangeException("value", "The PageSize of an endless collection should be positive.");
                }
  
                if (this.pageSize != value)
                {
                    this.pageSize = value;
                    this.OnPropertyChanged("PageSize");
                    this.OnPropertyChanged("ItemCount");
  
                    // Behave like good collections do.
                    ((IPagedCollectionView)this).MoveToFirstPage();
                }
            }
        }
  
        int IPagedCollectionView.TotalItemCount
        {
            get
            {
                return MaxOrders;
            }
        }
  
        public System.Collections.IEnumerator GetEnumerator()
        {
            // No orders, return empty Enumerator
            if (Orders.Count == 0)
            {
                return Orders.GetRange(0, 0).GetEnumerator();
            }
 
            // Not a full last page?  Return partial Enumerator
            if (((IPagedCollectionView)this).PageIndex * ((IPagedCollectionView)this).PageSize + pageSize > Orders.Count)
            {
                return
                    Orders.GetRange(((IPagedCollectionView) this).PageIndex*((IPagedCollectionView) this).PageSize,
                                    Orders.Count -
                                    ((IPagedCollectionView) this).PageIndex*((IPagedCollectionView) this).PageSize).
                        GetEnumerator();
            }
 
            // Return a full page
            return
                Orders.GetRange(((IPagedCollectionView) this).PageIndex*((IPagedCollectionView) this).PageSize,
                                ((IPagedCollectionView) this).PageSize).GetEnumerator();
        }
  
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }
  
        private bool OnPageChanging(int newPageIndex)
        {
            PageChangingEventArgs e = new PageChangingEventArgs(newPageIndex);
            if (this.PageChanging != null)
            {
                this.PageChanging(this, e);
            }
  
            return e.Cancel;
        }
  
        private void OnPageChanged()
        {
            EventArgs e = EventArgs.Empty;
            if (this.PageChanged != null)
            {
                this.PageChanged(this, e);
            }
        }
  
        private void OnCollectionChanged()
        {
            NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            if (this.CollectionChanged != null)
            {
                this.CollectionChanged(this, e);
            }
        }
 
        private void BoundedPagedCollection_PageChanging(object sender, PageChangingEventArgs e)
        {
            if (e.NewPageIndex > MaxOrders / pageSize)
            {
                e.Cancel = true;
            }
        }
  
        /// <summary>
        /// Sample Order
        /// </summary>
        public class Order
        {
            private int id;
            private DateTime date;
            private double amount;
            private bool confirmed;
            private string code;
            private string country;
  
            /// <summary>
            /// Gets the ID.
            /// </summary>
            /// <value>The ID.</value>
            public int ID
            {
                get { return this.id; }
            }
  
            /// <summary>
            /// Gets or sets the date.
            /// </summary>
            /// <value>The date.</value>
            public DateTime Date
            {
                get
                {
                    return this.date;
                }
                private set
                {
                    this.date = value;
                }
            }
  
            /// <summary>
            /// Gets or sets the amount.
            /// </summary>
            /// <value>The amount.</value>
            public double Amount
            {
                get
                {
                    return this.amount;
                }
                private set
                {
                    this.amount = value;
                }
            }
  
            /// <summary>
            /// Gets or sets a value indicating whether this <see cref="Order"/> is confirmed.
            /// </summary>
            /// <value><c>true</c> if confirmed; otherwise, <c>false</c>.</value>
            public bool Confirmed
            {
                get
                {
                    return this.confirmed;
                }
                private set
                {
                    this.confirmed = value;
                }
            }
  
            /// <summary>
            /// Gets or sets the code.
            /// </summary>
            /// <value>The code.</value>
            public string Code
            {
                get
                {
                    return this.code;
                }
                private set
                {
                    this.code = value;
                }
            }
  
            /// <summary>
            /// Gets or sets the country.
            /// </summary>
            /// <value>The country.</value>
            public string Country
            {
                get
                {
                    return this.country;
                }
                private set
                {
                    this.country = value;
                }
            }
  
            /// <summary>
            /// Initializes a new instance of the <see cref="Order"/> class.
            /// </summary>
            /// <param name="id">The id.</param>
            public Order(int id)
            {
                this.id = id;
                int random = new Random(id).Next(-100, 100);
                this.Date = DateTime.Now.AddDays(random);
                this.Amount = Math.Abs(random);
                this.Confirmed = random % 2 == 0;
  
                int someRandom = Math.Abs(this.id.GetHashCode()) / 10
                    + Math.Abs(this.Date.GetHashCode()) / 10
                    + Math.Abs(this.Amount.GetHashCode()) / 10
                    + Math.Abs(this.Confirmed.GetHashCode()) / 10;
                this.Code = someRandom.ToString() + someRandom.ToString();
  
                switch (random % 5)
                {
                    case 0:
                        this.Country = "USA";
                        break;
                    case 1:
                        this.Country = "UK";
                        break;
                    case 2:
                        this.Country = "Germany";
                        break;
                    case 3:
                        this.Country = "Spain";
                        break;
                    case 4:
                        this.Country = "France";
                        break;
                    default:
                        this.Country = "Other";
                        break;
                }
            }
        }
    }
}
0
Veselin Vasilev
Telerik team
answered on 22 Sep 2010, 02:49 PM
Hello Tim,

This is fantastic!

Thank you for sharing the code with the community.

You can contribute even more if you create a sample runnig project which demonstrates your accomplishment and upload it to our code-libraries

As a small benefit  you can get up to 10 000 Telerik Points.

Thank you in advance!

All the best,
Veselin Vasilev
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tim
Top achievements
Rank 1
answered on 22 Sep 2010, 05:29 PM
Done.  I presume it must be approved before it appears because it does not seem to be there at the moment.

Tim
0
Accepted
Veselin Vasilev
Telerik team
answered on 24 Sep 2010, 03:38 PM
Hi Tim,

I have modified it a bit (adding Trial Telerik assemblies and updating the references) and it is already live at
http://www.telerik.com/community/code-library/silverlight/gridview/a-paged-on-demand-ipagedcollectionview-example-where-chunks-of-a-known-total-are-loaded-as-requested.aspx

I have updated your Telerik points.

Thank you once again for your efforts.

Kind regards,
Veselin Vasilev
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Tim
Top achievements
Rank 1
answered on 24 Sep 2010, 03:41 PM
Cool, I am glad to help.  You have a fine product suite and an excellent relationship with the community.

Tim
0
Tim
Top achievements
Rank 1
answered on 28 Sep 2010, 03:58 PM
Suppose I wanted to augment the RadDataPager (see attached image) so that included the # of items loaded out of a known total, a page size control, and a button which would export to CSV.  Is there a sample out there on modifying or extending the control template?  I'd like to include the extra bits INSIDE the RadDataPager itself.

Thanks,
Tim
0
Vlad
Telerik team
answered on 29 Sep 2010, 07:15 AM
Hello,

 Yes, you can restyle the pager completely with Blend since the control is completely lookless. You can check this blog post for more info. 

Kind regards,
Vlad
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
0
Missing User
answered on 14 Dec 2011, 11:55 AM
Hello!

Sorry to bring up this old topic again, but i have the same issue.

I implement the indicated approach supplied by Ross using the DataPager in it's unbound mode. Everything is working as i expect but now i lost the Filter abilities. Example: If I'm in the Page 1, only people with "A" name appears, and if the user try to use the RadGridView Filter and choose to show people with Name with "B" nothing will show of course because the GridView does not have the collection yet.

Is there a way to workaround this? I mean implement a way when the user use the filter i call me service and return a new collection to the GridView?

Thanks a lot!
0
Rossen Hristov
Telerik team
answered on 14 Dec 2011, 03:52 PM
Hi Joao Paulo Grassi,

You can bind RadGridView and RadDataPager to one of our data source controls. In this way you will have both filtering and paging on the server.

We have:

- RadDomainDataSource for WCF RIA Services.
- RadDataServiceDataSource for WCF Data Services.

Please, explore their online demos. They show the integration between the three controls.

Kind regards,
Ross
the Telerik team

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

Tags
GridView
Asked by
Tim
Top achievements
Rank 1
Answers by
Rossen Hristov
Telerik team
Tim
Top achievements
Rank 1
Veselin Vasilev
Telerik team
Vlad
Telerik team
Missing User
Share this question
or