さて、私はこれと一緒に雑草で数日過ごしました、そして私はそれを理解していると思います。
まず、重要な魔法です。ページングが正しく機能するためには、現在のクエリによって返されたアイテムの数に関係なく、ページャーはアイテムの総数を知っている必要があります。クエリがすべてを返す場合、アイテム数は明らかに返されたアイテムの数です。スマートページングの場合、クエリは表示されたものだけを返しますが、アイテム数は引き続き使用可能なアイテムの合計です。フィルタリングを使用すると、フィルターが変更されるたびに、使用可能なアイテムの合計も変更されます。
Silverlight Datapagerコントロールには、ItemCountというプロパティがあります。読み取り専用であり、XAMLでデータバインドしたり、コードで直接設定したりすることはできません。ただし、ページャーを含むユーザーコントロールにIPagedCollectionViewを実装するDataContextがある場合、データコンテキストオブジェクトはPropertyChanged通知を使用してItemCountプロパティを実装する必要があり、DataPagerはこれを自動的に取得するようです。
次に、RIAサービスに関するBrad Abramsの優れた一連のブログ投稿、特にViewModelに関するこのブログ投稿を強くお勧めします。アイテム数の管理に関する重要な部分が欠落していますが、ページングとフィルタリングを機能させるために必要なもののほとんどが含まれています。彼のダウンロード可能なサンプルには、ModelViewViewModel(MVVM)を実装するための非常に優れた基本的なフレームワークも含まれています。ありがとう、ブラッド!
アイテムカウントを機能させる方法は次のとおりです。(このコードはカスタムORMを参照していますが、BradのコードはEntity Frameworkを使用しています。2つの間で、環境に必要なものを把握できます。)
まず、ORMは、フィルターの有無にかかわらず、レコード数の取得をサポートする必要があります。カウントをRIAサービスで利用できるようにする私のドメインサービスコードは次のとおりです。
[Invoke]
public int GetExamCount()
{
return Context.Exams.Count();
}
[Invoke]
public int GetFilteredExamCount(string descriptionFilter)
{
return Context.Exams.GetFilteredCount(descriptionFilter);
}
[Invoke]属性に注意してください。これは、エンティティまたはエンティティコレクションを返さないDomainServiceメソッドに必要です。
次に、ViewModelコードについて説明します。もちろん、ItemCountが必要です。(これはブラッドの例からです。)
int itemCount;
public int ItemCount
{
get { return itemCount; }
set
{
if (itemCount != value)
{
itemCount = value;
RaisePropertyChanged(ItemCountChangedEventArgs);
}
}
}
LoadDataメソッドはクエリを実行して、DataGridに表示する現在の行のセットを取得します。(これはまだカスタムソートを実装していませんが、簡単に追加できます。)
EntityQuery<ExamEntity> query =
DomainContext.GetPagedExamsQuery(PageSize * PageIndex, PageSize, DescriptionFilterText);
DomainContext.Load(query, OnExamsLoaded, null);
The callback method then runs the query to get the counts. If no filter is being used, we get the count for all rows; if there's a filter, then we get the count for filtered rows.
private void OnExamsLoaded(LoadOperation<ExamEntity> loadOperation)
{
if (loadOperation.Error != null)
{
//raise an event...
ErrorRaising(this, new ErrorEventArgs(loadOperation.Error));
}
else
{
Exams.MoveCurrentToFirst();
if (string.IsNullOrEmpty(DescriptionFilterText))
{
DomainContext.GetExamCount(OnCountCompleted, null);
}
else
{
DomainContext.GetFilteredExamCount(DescriptionFilterText, OnCountCompleted, null);
}
IsLoading = false;
}
}
There's also a callback method for counts:
void OnCountCompleted(InvokeOperation<int> op)
{
ItemCount = op.Value;
TotalItemCount = op.Value;
}
With the ItemCount set, the Datapager control picks it up, and we have paging with filtering and a smart query that returns only the records to be displayed!
LINQ makes the query easy with .Skip() and .Take(). Doing this with raw ADO.NET is harder. I learned how to do this by taking apart a LINQ-generated query.
SELECT * FROM
(select ROW_NUMBER() OVER (ORDER BY Description) as rownum, *
FROM Exams as T0 WHERE T0.Description LIKE @description ) as T1
WHERE T1.rownum between @first AND @last ORDER BY rownum
The clause "select ROW_NUMBER() OVER (ORDER BY Description) as rownum" is the interesting part, because not many people use "OVER" yet. This clause sorts the table on Description before assigning row numbers, and the filter is also applied before row numbers are assigned. This allows the outer SELECT to filter on row numbers, after sorting and filtering.
So there it is, smart paging with filtering, in RIA Services and Silverlight!