After a little research and trial, I figured out that the basics of RadDataPager really just loads everything from a source and page it. Given I use WCF Data Services, I want to lazy load additional pages when needed. As far as I understand, I need to implement IPagedCollectionView to get the functionality I want, as Unbound Mode doesn't cut it. I want to show how many pages are available to the user, and I want to cache already retrived items. Now, I found an example around here which I have adapted, but it still doesn't work properly and as expected. My implementation is as follows:
Note that I have a RadGridView and a RadDataPager. They both correctly bound to this "collection" in Xaml. Basically like this where PagedCollection equals a LazyPagedCollectionView:
There's a few things that don't really work here.
1. Max amount of pages only shows as the amount of pages you have lazily loaded. TotalItemCount are correctly updated and notified via PropertyChanged event, but it doesn't seem like RadDataPager is really listening to this change. I would assume that the mathematics behind the scene that displays this number would be tied to this property, but I may be wrong? If I'm required to know the total amount of items beforehand, it would be in my opinion a serious flaw.
2.When next is clicked in the RadDataPager, the next page of data is correctly loaded into the wrapped collection, and CollectionChanged is called and PropertyChanged is called for "ItemCount" if appropriate, followed by PageChanged event. RadDataGrid however doesn't change the page(!). To get that to work, I have to call CollectionChanged event with NotifyCollectionChangedAction.Reset instead of just forwarding the correct change event of the wrapped collection. Doesn't seems like RadDataGrid has any concept of the PageChanged event. Binding RadGridView to the RadDataPager's PagedSource doesn't help either.
Those are the 2 major issues I can't really wrap my head around right...it just seems illogical to me, but would be nice if someone could either point out errors or confirm any flaws in the control.
public
class
LazyPagedCollectionWrapper<T> : IPagedCollectionView, INotifyPropertyChanged, INotifyCollectionChanged, IEnumerable<T> where T:
new
()
{
public
event
EventHandler<EventArgs> PageChanged;
public
event
EventHandler<PageChangingEventArgs> PageChanging;
public
event
PropertyChangedEventHandler PropertyChanged;
public
event
NotifyCollectionChangedEventHandler CollectionChanged;
private
readonly
IRepository<T> repository;
private
readonly
Expression<Func<T,
object
>>[] eagerProperties;
private
readonly
DataServiceCollection<T> dataServiceCollection;
private
int
nextNewPageIndex;
public
LazyPagedCollectionWrapper(
int
pageSize, IRepository<T> repository,
params
Expression<Func<T,
object
>>[] eagerProperties)
{
PageSize = pageSize;
this
.repository = repository;
this
.eagerProperties = eagerProperties;
this
.dataServiceCollection = repository.Result
as
DataServiceCollection<T>;
this
.dataServiceCollection.CollectionChanged += OnDataServiceCollectionChanged;
this
.dataServiceCollection.LoadCompleted += OnDataServiceLoadCompleted;
}
public
bool
MoveToFirstPage()
{
return
MoveToPage(0);
}
public
bool
MoveToLastPage()
{
var lastIndex = TotalItemCount / PageSize;
return
MoveToPage(TotalItemCount % PageSize == 0 ? lastIndex - 1 : lastIndex);
}
public
bool
MoveToNextPage()
{
return
MoveToPage(PageIndex + 1);
}
public
bool
MoveToPreviousPage()
{
return
MoveToPage(PageIndex - 1);
}
public
bool
MoveToPage(
int
newPageIndex)
{
if
(OnPageChanging(newPageIndex) && newPageIndex > -1 && newPageIndex < TotalItemCount / PageSize)
return
false
;
nextNewPageIndex = newPageIndex;
if
(ItemCount < (newPageIndex + 1) * PageSize)
repository.LoadPaged(newPageIndex, PageSize, eagerProperties);
else
SwitchPage(newPageIndex);
return
true
;
}
public
bool
CanChangePage
{
get
{
return
true
; }
}
private
bool
isPageChanging;
public
bool
IsPageChanging
{
get
{
return
isPageChanging; }
set
{
isPageChanging = value;
OnPropertyChanged(
"IsPageChanging"
);
}
}
public
int
ItemCount
{
get
{
return
dataServiceCollection.Count; }
}
private
int
pageIndex;
public
int
PageIndex
{
get
{
return
pageIndex; }
set
{
pageIndex = value;
OnPropertyChanged(
"PageIndex"
);
}
}
private
int
pageSize;
public
int
PageSize
{
get
{
return
pageSize; }
set
{
pageSize = value;
OnPropertyChanged(
"PageSize"
);
}
}
private
int
totalItemCount;
public
int
TotalItemCount
{
get
{
return
totalItemCount; }
set
{
totalItemCount = value;
OnPropertyChanged(
"TotalItemCount"
);
}
}
protected
virtual
void
OnPageChanged()
{
if
(PageChanged !=
null
)
PageChanged(
this
, EventArgs.Empty);
}
protected
virtual
bool
OnPageChanging(
int
newPageIndex)
{
var e =
new
PageChangingEventArgs(newPageIndex);
if
(PageChanging !=
null
)
PageChanging(
this
, e);
return
e.Cancel;
}
protected
virtual
void
OnPropertyChanged(
string
propertyName)
{
if
(PropertyChanged !=
null
)
PropertyChanged(
this
,
new
PropertyChangedEventArgs(propertyName));
}
protected
virtual
void
OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if
(CollectionChanged !=
null
)
CollectionChanged(
this
, e);
}
private
void
SwitchPage(
int
newPageIndex)
{
IsPageChanging =
true
;
PageIndex = newPageIndex;
IsPageChanging =
false
;
OnPageChanged();
}
private
void
OnDataServiceCollectionChanged(
object
sender, NotifyCollectionChangedEventArgs e)
{
OnCollectionChanged(e);
switch
(e.Action)
{
case
NotifyCollectionChangedAction.Add:
case
NotifyCollectionChangedAction.Remove:
case
NotifyCollectionChangedAction.Reset:
OnPropertyChanged(
"ItemCount"
);
break
;
}
}
private
void
OnDataServiceLoadCompleted(
object
sender, LoadCompletedEventArgs e)
{
TotalItemCount = (
int
)e.QueryOperationResponse.TotalCount;
SwitchPage(nextNewPageIndex);
}
public
IEnumerator<T> GetEnumerator()
{
if
(ItemCount == 0)
return
dataServiceCollection.Take(0).GetEnumerator();
if
((PageIndex + 1) * PageSize > TotalItemCount)
return
dataServiceCollection.
Skip(PageIndex * PageSize).
Take(((PageIndex + 1) * PageSize) - TotalItemCount).
GetEnumerator();
return
dataServiceCollection.Skip(PageIndex * PageSize).Take(PageSize).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return
GetEnumerator();
}
}
Note that I have a RadGridView and a RadDataPager. They both correctly bound to this "collection" in Xaml. Basically like this where PagedCollection equals a LazyPagedCollectionView:
<
telerik:RadGridView
ItemsSource
=
"{Binding PagedCollection}"
/>
<
telerik:RadDataPager
Source
=
"{Binding PagedCollection}"
/>
There's a few things that don't really work here.
1. Max amount of pages only shows as the amount of pages you have lazily loaded. TotalItemCount are correctly updated and notified via PropertyChanged event, but it doesn't seem like RadDataPager is really listening to this change. I would assume that the mathematics behind the scene that displays this number would be tied to this property, but I may be wrong? If I'm required to know the total amount of items beforehand, it would be in my opinion a serious flaw.
2.When next is clicked in the RadDataPager, the next page of data is correctly loaded into the wrapped collection, and CollectionChanged is called and PropertyChanged is called for "ItemCount" if appropriate, followed by PageChanged event. RadDataGrid however doesn't change the page(!). To get that to work, I have to call CollectionChanged event with NotifyCollectionChangedAction.Reset instead of just forwarding the correct change event of the wrapped collection. Doesn't seems like RadDataGrid has any concept of the PageChanged event. Binding RadGridView to the RadDataPager's PagedSource doesn't help either.
Those are the 2 major issues I can't really wrap my head around right...it just seems illogical to me, but would be nice if someone could either point out errors or confirm any flaws in the control.