ASP.NET

ASP.NET GridView 列のソート機能実装とソートアイコンの表示(サンプルあり)

ASP.NET GridView の列をクリックしたタイミングで、列をソートする機能の実装方法を紹介します。また、ソートされた列ヘッダーには、ソート方向を示すアイコンも表示します。

GridView でのソート実装の煩雑さ

GridView では、データの並び替えのみであれば、AllowSorting プロパティを有効化するのみで実現できます。ただし、この状態では列ヘッダーにソート状態が反映されず、ユーザーはどの列をソートしたのかが非常に分かりづらいという課題があります。今回、簡易にソートアイコン(ソートインディケーター)も表示する実装を紹介します。

GridView インスタンスの準備

ASPX 上で、GridView インスタンスを宣言します。

<form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
    <div>
        <asp:GridView ID="GridView1" runat="server"
              AutoGenerateColumns="False"
              AllowSorting="true"
              OnSorting="GridView1_Sorting"
              OnRowDataBound="GridView1_RowDataBound">
            <Columns>
                <asp:BoundField DataField="ID" HeaderText="ID" SortExpression="ID" />
                <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
                <asp:BoundField DataField="Age" HeaderText="Age" SortExpression="Age" />
            </Columns>
        </asp:GridView>
    </div>
</form>

サンプルデータのバインド

DataTable のダミーデータをバインドしています。コードの注意点は、

1.ページロードタイミング(Page_Load イベント)で、BindGrid() 経由で GridView の DataSource に DataTable データをバインドしています。

2.ソート情報を含む呼び出しの場合、DataTable の DataView プロパティを利用して、ソート情報を付与したデータを GridView にバインドします。初回表示時はソート情報は含みませんが、後述の Sorting イベント内部から BindGrid が呼ばれるタイミングで、ソート情報が提供されます。

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        BindGrid();
    }
}

private void BindGrid(string sortExpression = null, string sortDirection = null)
{
    var dataSource = GetData();

    if (sortExpression != null)
    {
        DataView dv = dataSource.DefaultView;
        dv.Sort = sortExpression + " " + sortDirection;
        GridView1.DataSource = dv;
    }
    else
    {
        GridView1.DataSource = dataSource;
    }

    GridView1.DataBind();
}

private DataTable GetData()
{
    DataColumn[] primaryColumn = new DataColumn[1];
    DataTable dataTable = new DataTable();
    primaryColumn[0] = dataTable.Columns.Add("ID", typeof(int));
    dataTable.Columns.Add("Name", typeof(string));
    dataTable.Columns.Add("Age", typeof(int));
    dataTable.Rows.Add(1, "Junya", 23);
    dataTable.Rows.Add(2, "Kyoko", 25);
    dataTable.Rows.Add(3, "Emi", 24);
    dataTable.Rows.Add(4, "Tatsuya", 28);
    dataTable.Rows.Add(5, "Naoki", 33);
    dataTable.Rows.Add(6, "Yuka", 25);
    dataTable.Rows.Add(7, "Mami", 24);
    dataTable.PrimaryKey = primaryColumn;
    return dataTable;
}

ソート機能の有効化(AllowSorting プロパティ)

ソート機能の有効化には、GridView の AllowSorting プロパティを有効化(true)していることを確認します。

<asp:GridView ID="GridView1" runat="server"
              AutoGenerateColumns="False"
              AllowSorting="true"
              OnSorting="GridView1_Sorting"
              OnRowDataBound="GridView1_RowDataBound">
    ...
</asp:GridView>

また、列ヘッダークリックなどによるソート実行時の Sorting イベントをハンドルし、GetSortDirection メソッド内でソート情報を組み立てて、BindGrid メソッドにソート情報を提供します。

protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    string sortExpression = e.SortExpression;
    string sortDirection = GetSortDirection(sortExpression);

    BindGrid(sortExpression, sortDirection);
}
    private string GetSortDirection(string column)
    {
        // ViewState から前回のソートの状態を取得
        string sortDirection = "ASC"; // デフォルトは昇順

        string previousSortExpression = ViewState["SortExpression"] as string;
        string previousSortDirection = ViewState["SortDirection"] as string;

        if (previousSortExpression != null && previousSortExpression == column)
        {
            // 前回と同じ列でソートする場合は、ソート順を逆にする
            sortDirection = previousSortDirection == "ASC" ? "DESC" : "ASC";
        }

        // 現在のソート状態を ViewState に保存
        ViewState["SortExpression"] = column;
        ViewState["SortDirection"] = sortDirection;

        return sortDirection;
    }

この段階で、列のソートが動作するようになりました。下記実行例では、ID 列を降順でソートしています。この段階では、まだソートアイコンは表示されていません。

ID 列で降順ソートした結果

ソートアイコンの表示

続いて、ソートアイコンの表示も行いましょう。今回ソートアイコンは、ヘッダーの文字列に、ソート方向を示す三角形を加えることで実現します。RowDataBound イベントは、GridView で行にデータがバインドされた時に発生します。29-30 行目で、ソート方向に応じて三角形を加えていることが分かります。

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // ヘッダーでなければ処理をキャンセル
    if (e.Row.RowType != DataControlRowType.Header)
    {
        return;
    }

    string sortExpression = ViewState["SortExpression"] as string;
    string sortDirection = ViewState["SortDirection"] as string;

    // ソート情報が空であれば処理をキャンセル
    if (string.IsNullOrEmpty(sortExpression))
    {
        return;
    }

    // ソート対象の列を探してインディケーターを追加
    foreach (TableCell cell in e.Row.Cells)
    {
        if (!cell.HasControls())
        {
            return;
        }

        LinkButton sortLinkButton = cell.Controls[0] as LinkButton;
        if (sortLinkButton != null && sortLinkButton.CommandArgument == sortExpression)
        {
            string sortIndicator = sortDirection == "ASC" ? " ▲" : " ▼";
            sortLinkButton.Text = sortLinkButton.Text + sortIndicator;
        }
    }
}

最終的な実行結果

任意の列でソートを行うことで、データの並び替えと、ソートアイコンが表示されるようになりました。下記スクリーンショットでは、Name 列で昇順ソートした結果となります。

Name 列を昇順でソートした結果

サンプルのダウンロード

-ASP.NET