
Wesley Witt
Top achievements
Rank 1
Wesley Witt
asked on 16 Dec 2010, 06:46 PM
I have an implementation of IPagedCollectionView that I'm using for custom paging of SQL data. Things seem to be working almost perfectly, but I have one issue that I need help with.
When I change pages in the RadDataPager my IPagedCollectionView is called appropriately, MoveToNextPage() is called. My code responds by retrieving the next page of data and changing the current page index value. The problem is that the RadGridView does not update its display with the result being that the new page of data is not displayed in the control. In debugging this I can see that the control is not calling GetEnumerator() after the page change. My suspicion is that the grid control is assuming that the enumerator will not change, just the contents of the collection will change. In my case I am changing the collection on each page change an expecting the grid control to perform a new enumeration by first calling GetEnumerator().
Comments?
*** Actually, changing the code to use the same collection for the enumeration does not improve things. Another data point is that the data pager does not update its display to indicate that a page change has happened. The data pager control always says that it is on page #1.
When I change pages in the RadDataPager my IPagedCollectionView is called appropriately, MoveToNextPage() is called. My code responds by retrieving the next page of data and changing the current page index value. The problem is that the RadGridView does not update its display with the result being that the new page of data is not displayed in the control. In debugging this I can see that the control is not calling GetEnumerator() after the page change. My suspicion is that the grid control is assuming that the enumerator will not change, just the contents of the collection will change. In my case I am changing the collection on each page change an expecting the grid control to perform a new enumeration by first calling GetEnumerator().
Comments?
*** Actually, changing the code to use the same collection for the enumeration does not improve things. Another data point is that the data pager does not update its display to indicate that a page change has happened. The data pager control always says that it is on page #1.
2 Answers, 1 is accepted
0
Hi Wesley Witt,
Are you raising the following events when the page changes:
- CollectionChanged with action Reset: this should make the grid re-enumerate the collection.
- PropertyChanged with property name PageIndex: this should make the pager update its stuff.
By the way, I will post the source code of a dummy implementation of IPagedCollectionView from our example called "Endless Paging". This implementation can help you see what events should be called:
Your other source of info can be the Silverlight class (I know its not WPF but this does not matter -- it's absolutely the same) PagedCollectionView. PagedCollectionView is Microsoft's default implementation of the IPagedCollectionView interface and does everything correctly. You can check its source code with Reflector.
Here is another blog that discusses what needs to be done to implement the interface correctly.
Finally, if you have our source code available, you can dig into Telerik.Windows.Data.QueryableCollection view which also implements Telerik.Windows.Data.IPagedCollectionView (there is no built-in IPagedCollection view in WPF and that is why we have created our own that is absolutely the same as the one found in SL).
Since we share our code-base between Silverlight and WPF, if you implement Telerik.Windows.Data.IPagedCollectionView like in the SL examples above, RadDataPager will work correctly.
I hope this helps. Let me know if there are problems.
Regards,
Ross
the Telerik team
Are you raising the following events when the page changes:
- CollectionChanged with action Reset: this should make the grid re-enumerate the collection.
- PropertyChanged with property name PageIndex: this should make the pager update its stuff.
By the way, I will post the source code of a dummy implementation of IPagedCollectionView from our example called "Endless Paging". This implementation can help you see what events should be called:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Collections;
using
Telerik.Windows.Data;
using
System.ComponentModel;
using
System.Collections.Specialized;
using
System.Windows.Data;
using
System.Globalization;
namespace
Telerik.Windows.Examples.GridView
{
/// <summary>
/// Endless paged collection view for testing purposes.
/// </summary>
public
class
EndlessPagedCollectionView : 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;
/// <summary>
/// We always can. We are endless.
/// </summary>
bool
IPagedCollectionView.CanChangePage
{
get
{
return
true
; }
}
bool
IPagedCollectionView.IsPageChanging
{
get
{
return
this
.isPageChanging; }
}
/// <summary>
/// Visual Basic is so lame!
/// </summary>
private
void
SetIsPageChanging(
bool
value)
{
if
(
this
.isPageChanging != value)
{
this
.isPageChanging = value;
this
.OnPropertyChanged(
"IsPageChanging"
);
}
}
/// <summary>
/// Since we are endless, the item count is
/// the amount of items currently known to exist.
/// In other words the item count is whatever we
/// have reached.
/// </summary>
int
IPagedCollectionView.ItemCount
{
get
{
return
(((IPagedCollectionView)
this
).PageIndex + 1) * ((IPagedCollectionView)
this
).PageSize;
}
}
bool
IPagedCollectionView.MoveToFirstPage()
{
return
((IPagedCollectionView)
this
).MoveToPage(0);
}
bool
IPagedCollectionView.MoveToLastPage()
{
//We cannot move to the last page since we are endless :)
return
false
;
}
bool
IPagedCollectionView.MoveToNextPage()
{
return
((IPagedCollectionView)
this
).MoveToPage(((IPagedCollectionView)
this
).PageIndex + 1);
}
bool
IPagedCollectionView.MoveToPage(
int
pageIndex)
{
if
(
this
.OnPageChanging(pageIndex) && pageIndex != -1)
{
return
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();
return
true
;
}
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();
}
}
}
/// <summary>
/// We don't know this. Remember we are endless.
/// </summary>
int
IPagedCollectionView.TotalItemCount
{
get
{
return
-1; }
}
public
System.Collections.IEnumerator GetEnumerator()
{
// The best part -- true endlessness ;)
// Now this might crash if we overflow Int32, but in a
// real scenario new "items" will be returned forever.
IEnumerable<Order> items = from id
in
Enumerable.Range(((IPagedCollectionView)
this
).PageIndex * ((IPagedCollectionView)
this
).PageSize, ((IPagedCollectionView)
this
).PageSize)
select
new
Order(id);
return
items.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);
}
}
/// <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
;
}
}
}
}
}
Your other source of info can be the Silverlight class (I know its not WPF but this does not matter -- it's absolutely the same) PagedCollectionView. PagedCollectionView is Microsoft's default implementation of the IPagedCollectionView interface and does everything correctly. You can check its source code with Reflector.
Here is another blog that discusses what needs to be done to implement the interface correctly.
Finally, if you have our source code available, you can dig into Telerik.Windows.Data.QueryableCollection view which also implements Telerik.Windows.Data.IPagedCollectionView (there is no built-in IPagedCollection view in WPF and that is why we have created our own that is absolutely the same as the one found in SL).
Since we share our code-base between Silverlight and WPF, if you implement Telerik.Windows.Data.IPagedCollectionView like in the SL examples above, RadDataPager will work correctly.
I hope this helps. Let me know if there are problems.
Regards,
Ross
the Telerik team
Browse the videos here>> to help you get started with RadControls for WPF
0

Wesley Witt
Top achievements
Rank 1
answered on 17 Dec 2010, 05:21 PM
Thanks a lot for the detailed post. Prior to your post I did dig into your implementation of QueryableCollection and saw exactly what you describe here. Using that information I was able to make it work. It does seem strange that the grid view requires a CollectionChanged with action Reset. This seems strange because the collection really hasn't been reset, just the view onto the virtual collection has changed. I just feels like a weird way for this to work, but thanks for your help.