カスタマイズされた並べ替えユーザー インターフェイスを作成する (C#)

作成者: Scott Mitchell

PDF のダウンロード

並べ替えられたデータの長いリストを表示する場合は、区切り文字行を導入して関連データをグループ化すると非常に役立ちます。 このチュートリアルでは、このような並べ替えユーザー インターフェイスを作成する方法について説明します。

はじめに

並べ替えられた列に少数の異なる値しかない、並べ替えられたデータの長いリストを表示する場合、エンド ユーザーは、差の境界が発生する場所を正確に識別するのが難しい場合があります。 たとえば、データベースには 81 個の製品がありますが、カテゴリの選択肢は 9 つだけです (8 つの一意のカテゴリと NULL オプション)。 シーフードカテゴリに該当する製品を調べることに関心があるユーザーの場合を考えてみましょう。 1 つの GridView 内のすべての 製品を一覧表示するページから、ユーザーは、すべてのシーフード製品をまとめてグループ化するカテゴリ別に結果を並べ替えるのが最善の策であると判断する場合があります。 カテゴリ別に並べ替えた後、ユーザーはリストを探して、シーフードグループ化された製品の開始と終了を探す必要があります。 結果はカテゴリ名でアルファベット順に並べられているため、シーフード製品を見つけることは難しくありませんが、グリッド内の項目のリストを厳密にスキャンする必要があります。

並べ替えられたグループ間の境界を強調するために、多くの Web サイトでは、このようなグループ間に区切り記号を追加するユーザー インターフェイスが使用されています。 図 1 に示すような区切り記号を使用すると、ユーザーは特定のグループをより迅速に見つけて境界を特定し、データに存在する個別のグループを確認できます。

各カテゴリ グループが明確に識別される

図 1: 各カテゴリ グループが明確に識別されている (クリックするとフルサイズの画像が表示されます)

このチュートリアルでは、このような並べ替えユーザー インターフェイスを作成する方法について説明します。

手順 1: 標準の並べ替え可能な GridView の作成

GridView を拡張して拡張並べ替えインターフェイスを提供する方法を調べる前に、まず、製品を一覧表示する標準の並べ替え可能な GridView を作成しましょう。 まず、フォルダー内の CustomSortingUI.aspx ページを PagingAndSorting 開きます。 ページに GridView を追加し、そのプロパティを IDProductList設定し、それを新しい ObjectDataSource にバインドします。 レコードを選択するために クラス s GetProducts() メソッドをProductsBLL使用するように ObjectDataSource を構成します。

次に、GridView に 、および BoundFields と UnitPrice Discontinued CheckBoxField のみが含まれるSupplierNameProductNameCategoryNameような構成を行います。 最後に、GridView のスマート タグの [並べ替えを有効にする] チェック ボックスをオンにするか、その AllowSorting プロパティを に true設定して、並べ替えをサポートするように GridView を構成します。 ページにこれらの追加を CustomSortingUI.aspx 行った後、宣言型マークアップは次のようになります。

<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ObjectDataSource1" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category"
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier"
            ReadOnly="True" SortExpression="SupplierName" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
            HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
    TypeName="ProductsBLL"></asp:ObjectDataSource>

ブラウザーでこれまでの進行状況を確認してください。 図 2 は、データがカテゴリ別にアルファベット順に並べ替えられている場合の、並べ替え可能な GridView を示しています。

並べ替え可能な GridView のデータがカテゴリ別に並べ替えられます

図 2: 並べ替え可能な GridView のデータがカテゴリ別に並べ替えられている (フルサイズの画像を表示する をクリックします)

手順 2: 区切り文字行を追加するための手法を確認する

汎用の並べ替え可能な GridView が完了したら、残っているのは、一意の並べ替えられた各グループの前に GridView に区切り行を追加できることです。 しかし、このような行を GridView に挿入するにはどうすればよいですか? 基本的に、GridView の行を反復処理し、並べ替えられた列の値間の相違点を特定し、適切な区切り行を追加する必要があります。 この問題を考えると、ソリューションが GridView のイベント ハンドラーのどこかにあるのは当然の RowDataBound ようです。 「 データに基づくカスタム書式設定」 チュートリアルで説明したように、このイベント ハンドラーは、行のデータに基づいて行レベルの書式設定を適用する場合に一般的に使用されます。 ただし、 RowDataBound このイベント ハンドラーから GridView にプログラムで行を追加できないため、イベント ハンドラーはここではソリューションではありません。 実際、GridView の Rows コレクションは読み取り専用です。

GridView に行を追加するには、次の 3 つの選択肢があります。

  • これらのメタデータ区切り行を、GridView にバインドされている実際のデータに追加します
  • GridView がデータにバインドされたら、GridView のコントロール コレクションにインスタンスを追加 TableRow します
  • GridView コントロールを拡張し、GridView の構造の構築を担当するメソッドをオーバーライドするカスタム サーバー コントロールを作成する

多くの Web ページまたは複数の Web サイトでこの機能が必要な場合は、カスタム サーバー コントロールを作成するのが最適な方法です。 ただし、これにはかなりのコードと、GridView の内部作業の深さについての徹底的な調査が必要になります。 そのため、このチュートリアルではそのオプションは考慮しません。

他の 2 つのオプションは、GridView にバインドされている実際のデータに区切り文字行を追加し、そのバインド後に GridView のコントロール コレクションを操作するオプションです。問題を異なる方法で攻撃し、ディスカッションにメリットがあります。

GridView にバインドされたデータへの行の追加

GridView がデータ ソースにバインドされると、データ ソースによって返される各レコードに 対して が作成 GridViewRow されます。 そのため、GridView にバインドする前にデータ ソースに区切り記号レコードを追加することで、必要な区切り行を挿入できます。 図 3 に、この概念を示します。

1 つの手法として、データ ソースへの区切り文字行の追加が含まれます。

図 3: データ ソースに区切り文字行を追加する 1 つの手法

特別な区切り記号レコードがないため、区切り記号レコードという用語を引用符で囲んで使用します。むしろ、データ ソース内の特定のレコードが通常のデータ行ではなく区切り記号として機能するようにフラグを設定する必要があります。 この例では、 で構成ProductRowsされる GridView にインスタンスをバインドProductsDataTableしています。 レコードのプロパティを に-1設定することで、レコードに区切り行としてフラグを設定CategoryIDする場合があります (このような値は正常に存在できなかったため)。

この手法を利用するには、次の手順を実行する必要があります。

  1. GridView (インスタンス) にバインドするデータをプログラムで ProductsDataTable 取得する
  2. GridView の プロパティとSortDirectionプロパティに基づいてデータをSortExpression並べ替える
  3. 内の ProductsDataTableProductsRows反復処理し、並べ替えられた列の違いがどこにあるかを探します
  4. 各グループ境界で、区切り記号レコード ProductsRow インスタンスを DataTable に挿入します。このインスタンスは CategoryID-1 設定されています (または、レコードを区切り記号レコードとしてマークするために決定された指定)。
  5. 区切り文字行を挿入した後、プログラムによってデータを GridView にバインドします

これら 5 つの手順に加えて、GridView の RowDataBound イベントのイベント ハンドラーも提供する必要があります。 ここでは、それぞれDataRowをチェックし、CategoryIDそれが区切り行 (設定が である-1行) であるかどうかを判断します。 その場合は、書式設定またはセルに表示されるテキストを調整する必要があります。

並べ替えグループの境界を挿入するためにこの手法を使用するには、GridView Sorting のイベントのイベント ハンドラーを提供し、 と SortDirection の値を追跡する必要もあるため、上記よりも少し多くの作業がSortExpression必要です。

データバインド後の GridView のコントロール コレクションの操作

データを GridView にバインドする前にメッセージングするのではなく、データが GridView にバインドされた に区切り行を追加できます。 データ バインディングのプロセスによって GridView のコントロール階層が構築されます。実際には、単に Table 行のコレクションで構成されるインスタンスであり、それぞれがセルのコレクションで構成されます。 具体的には、GridView のコントロール コレクションには、そのルートにある オブジェクト、GridViewRowGridView にバインドされた 内の各レコードDataSourceの (クラスからTableRow派生した) 、および TableCell 内の各データ フィールドの各GridViewRowインスタンス内の オブジェクトがDataSource含まれますTable

各並べ替えグループの間に区切り行を追加するには、作成後にこのコントロール階層を直接操作できます。 GridView のコントロール階層は、ページのレンダリング時までに最後に作成されたと確信できます。 したがって、この方法はクラスの Render メソッドをPageオーバーライドします。その時点で、GridView の最終的な制御階層が更新され、必要な区切り行が含まれます。 図 4 は、このプロセスを示しています。

代替手法で GridView のコントロール階層を操作する

図 4: GridView のコントロール階層を操作する代替手法 (フルサイズの画像を表示する をクリックします)

このチュートリアルでは、この後者の方法を使用して、並べ替えのユーザー エクスペリエンスをカスタマイズします。

注意

このチュートリアルで説明するコードは、 Teemu Keiski のブログ エントリ「 GridView Sort Grouping でビットを再生する」で提供されている例に基づいています。

手順 3: GridView のコントロール階層に区切り文字行を追加する

コントロール階層が作成され、そのページ訪問時に最後に作成された後にのみ、区切り行を GridView のコントロール階層に追加する必要があるため、この追加はページ ライフサイクルの最後に実行し、実際の GridView コントロール階層が HTML にレンダリングされる前に実行します。 これを実現できる最新のポイントは クラスの Render イベントです。このイベントはPage、次のメソッド シグネチャを使用して分離コード クラスでオーバーライドできます。

protected override void Render(HtmlTextWriter writer)
{
    // Add code to manipulate the GridView control hierarchy
    base.Render(writer);
}

クラスの PageRender のメソッドが呼び出 base.Render(writer) されると、ページ内の各コントロールがレンダリングされ、コントロール階層に基づいてマークアップが生成されます。 したがって、両方とも を呼び出して、ページがレンダリングされるようにし、 を呼び base.Render(writer)base.Render(writer)す前に GridView コントロール階層を操作して、レンダリング前に区切り行が GridView コントロール階層に追加されるようにすることが不可欠です。

並べ替えグループ ヘッダーを挿入するには、まず、ユーザーがデータの並べ替えを要求していることを確認する必要があります。 既定では、GridView の内容は並べ替えされないため、グループの並べ替えヘッダーを入力する必要はありません。

注意

ページが最初に読み込まれるときに GridView を特定の列で並べ替える場合は、最初のページアクセス時に GridView メソッド Sort を呼び出します (後続のポストバックでは呼び出しません)。 これを実現するには、条件内のイベント ハンドラーに Page_Load この呼び出しを if (!Page.IsPostBack) 追加します。 メソッドの詳細については、「 レポート データのページングと並べ替え 」チュートリアルの情報を Sort 参照してください。

データが並べ替えられたと仮定すると、次のタスクは、データが並べ替えられた列を特定し、その列の値の違いを探して行をスキャンすることです。 次のコードでは、データが並べ替えられていることを確認し、データの並べ替えの基準となる列を検索します。

protected override void Render(HtmlTextWriter writer)
{
    // Only add the sorting UI if the GridView is sorted
    if (!string.IsNullOrEmpty(ProductList.SortExpression))
    {
        // Determine the index and HeaderText of the column that
        //the data is sorted by
        int sortColumnIndex = -1;
        string sortColumnHeaderText = string.Empty;
        for (int i = 0; i < ProductList.Columns.Count; i++)
        {
            if (ProductList.Columns[i].SortExpression.CompareTo(ProductList.SortExpression)
                == 0)
            {
                sortColumnIndex = i;
                sortColumnHeaderText = ProductList.Columns[i].HeaderText;
                break;
            }
        }
        // TODO: Scan the rows for differences in the sorted column�s values
}

GridView がまだ並べ替えられていない場合、GridView の SortExpression プロパティは設定されていません。 したがって、このプロパティに何らかの値がある場合にのみ、区切り行を追加します。 その場合は、データが並べ替えられた列のインデックスを次に決定する必要があります。 これを行うには、GridView のコレクションをループ処理し、GridView SortExpressionColumns プロパティと等しいプロパティを持SortExpressionつ列を検索します。 列のインデックスに加えて、 プロパティも取得 HeaderText します。これは、区切り文字の行を表示するときに使用されます。

データの並べ替えの基準となる列のインデックスを使用して、最後の手順として GridView の行を列挙します。 行ごとに、並べ替えられた列の値が前の行の並べ替えられた列の値と異なるかどうかを判断する必要があります。 その場合は、コントロール階層に新 GridViewRow しいインスタンスを挿入する必要があります。 これを行うには、次のコードを使用します。

protected override void Render(HtmlTextWriter writer)
{
    // Only add the sorting UI if the GridView is sorted
    if (!string.IsNullOrEmpty(ProductList.SortExpression))
    {
        // ... Code for finding the sorted column index removed for brevity ...
        // Reference the Table the GridView has been rendered into
        Table gridTable = (Table)ProductList.Controls[0];
        // Enumerate each TableRow, adding a sorting UI header if
        // the sorted value has changed
        string lastValue = string.Empty;
        foreach (GridViewRow gvr in ProductList.Rows)
        {
            string currentValue = gvr.Cells[sortColumnIndex].Text;
            if (lastValue.CompareTo(currentValue) != 0)
            {
                // there's been a change in value in the sorted column
                int rowIndex = gridTable.Rows.GetRowIndex(gvr);
                // Add a new sort header row
                GridViewRow sortRow = new GridViewRow(rowIndex, rowIndex,
                    DataControlRowType.DataRow, DataControlRowState.Normal);
                TableCell sortCell = new TableCell();
                sortCell.ColumnSpan = ProductList.Columns.Count;
                sortCell.Text = string.Format("{0}: {1}",
                    sortColumnHeaderText, currentValue);
                sortCell.CssClass = "SortHeaderRowStyle";
                // Add sortCell to sortRow, and sortRow to gridTable
                sortRow.Cells.Add(sortCell);
                gridTable.Controls.AddAt(rowIndex, sortRow);
                // Update lastValue
                lastValue = currentValue;
            }
        }
    }
    base.Render(writer);
}

このコードは、GridView のコントロール階層のルートにある オブジェクトをプログラムで参照 Table し、 という名前 lastValueの文字列変数を作成することから始めます。 lastValue は、現在の行の並べ替えられた列の値と前の行の値を比較するために使用されます。 次に、GridView の Rows コレクションが列挙され、各行に対して、並べ替えられた列の値が変数に currentValue 格納されます。

注意

特定の行の並べ替えられた列の値を決定するには、セルの Text プロパティを使用します。 これは BoundFields には適していますが、TemplateFields や CheckBoxFields などでは必要に応じて機能しません。 ここでは、間もなく別の GridView フィールドを考慮する方法について説明します。

次に currentValue 、 変数と lastValue 変数が比較されます。 異なる場合は、コントロール階層に新しい区切り文字行を追加する必要があります。 これは、オブジェクトRowsのコレクション内Tableの のGridViewRowインデックスを決定し、新しい GridViewRow インスタンスと TableCell インスタンスを作成してから、 と をコントロール階層に追加TableCellGridViewRowすることによって実現されます。

区切り行の 1 行 TableCell は、GridView の幅全体にまたがるように書式設定され、CSS クラスを使用して SortHeaderRowStyle 書式設定され、並べ替えグループ名 (Category など) とグループの値 (飲料など) の両方が表示されるようにプロパティを持 Text ちます。 最後に、 lastValue が の currentValue値に更新されます。

並べ替えグループ ヘッダー行 SortHeaderRowStyle の書式設定に使用する CSS クラスは、ファイルで指定する Styles.css 必要があります。 どんなスタイル設定でも自由に使用できます。私は次を使用しました:

.SortHeaderRowStyle
{
    background-color: #c00;
    text-align: left;
    font-weight: bold;
    color: White;
}

現在のコードでは、並べ替えインターフェイスは BoundField による並べ替え時に並べ替えグループ ヘッダーを追加します (図 5 を参照してください。これは、仕入先による並べ替え時のスクリーンショットを示しています)。 ただし、他のフィールド型 (CheckBoxField や TemplateField など) で並べ替える場合、並べ替えグループ ヘッダーはどこにも見つかりません (図 6 を参照)。

並べ替えインターフェイスには、BoundFields による並べ替え時に並べ替えグループ ヘッダーが含まれます

図 5: 並べ替えインターフェイスには、BoundFields による並べ替え時に並べ替えグループ ヘッダーが含まれています (フルサイズの画像を表示する をクリックします)。

CheckBoxField の並べ替え時にグループ ヘッダーの並べ替えが見つからない

図 6: CheckBoxField の並べ替え時にグループ ヘッダーの並べ替えが見つかりません (フルサイズの画像を表示する をクリックします)

CheckBoxField による並べ替え時に並べ替えグループ ヘッダーが見つからないのは、コードで現在、 プロパティTextだけをTableCell使用して各行の並べ替えられた列の値を決定しているためです。 CheckBoxFields の場合、TableCells Text プロパティは空の文字列です。代わりに、値は、コレクションControls内にある CheckBox Web コントロールをTableCell介して使用できます。

BoundFields 以外のフィールド型を処理するには、変数が currentValue チェック に割り当てられているコードを拡張して、コレクションControlsに CheckBox が存在するようにするTableCell必要があります。 を使用 currentValue = gvr.Cells[sortColumnIndex].Textする代わりに、このコードを次のように置き換えます。

string currentValue = string.Empty;
if (gvr.Cells[sortColumnIndex].Controls.Count > 0)
{
    if (gvr.Cells[sortColumnIndex].Controls[0] is CheckBox)
    {
        if (((CheckBox)gvr.Cells[sortColumnIndex].Controls[0]).Checked)
            currentValue = "Yes";
        else
            currentValue = "No";
    }
    // ... Add other checks here if using columns with other
    //      Web controls in them (Calendars, DropDownLists, etc.) ...
}
else
    currentValue = gvr.Cells[sortColumnIndex].Text;

このコードでは、現在の行の並べ替えられた列 TableCell を調べて、コレクション内に Controls コントロールがあるかどうかを判断します。 が存在し、最初のコントロールが CheckBox の場合、 currentValue CheckBox の Checked プロパティに応じて、変数は Yes または No に設定されます。 それ以外の場合、値は s Text プロパティからTableCell取得されます。 このロジックは、GridView に存在する可能性がある任意の TemplateField の並べ替えを処理するためにレプリケートできます。

上記のコードを追加すると、中止された CheckBoxField による並べ替え時に並べ替えグループ ヘッダーが表示されるようになりました (図 7 を参照)。

CheckBoxField の並べ替え時にグループ ヘッダーの並べ替えが表示されるようになりました

図 7: CheckBoxField の並べ替え時にグループ ヘッダーの並べ替えが表示されるようになりました (フルサイズの画像を表示する をクリックします)

注意

SupplierID、または UnitPrice フィールドのデータベース値CategoryIDを持つNULL製品がある場合、これらの値は既定で GridView に空の文字列として表示されます。つまり、値を持つNULL製品の区切り行のテキストは Category: のように読み上げられます (つまり、Category: Beverages のように Category: の後に名前はありません)。 ここに値を表示する場合は、BoundFields NullDisplayText プロパティを表示するテキストに設定するか、区切り行の Text プロパティに を割り当てるcurrentValueときに Render メソッドで条件付きステートメントを追加できます。

まとめ

GridView には、並べ替えインターフェイスをカスタマイズするための多くの組み込みオプションは含まれていません。 ただし、少し低レベルのコードでは、GridView のコントロール階層を微調整して、よりカスタマイズされたインターフェイスを作成できます。 このチュートリアルでは、並べ替え可能な GridView の並べ替えグループ区切り行を追加する方法について説明しました。これにより、個別のグループとそれらのグループの境界をより簡単に識別できます。 カスタマイズされた並べ替えインターフェイスのその他の例については、Scott Guthrie s A Few ASP.NET 2.0 GridView の並べ替えのヒントとテクニックに関するブログ エントリをチェックします。

幸せなプログラミング!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・自分自身 ASP.NET 24時間で2.0です。 にアクセスmitchell@4GuysFromRolla.comすることも、ブログを介して アクセスすることもできます。これは でhttp://ScottOnWriting.NET確認できます。