デベロッパー2006年8月1日 12:30
文字サイズ文字サイズ小文字サイズ中文字サイズ大

GridView上のすべてのチェックボックスをオンにする方法

この記事のURLhttp://japan.internet.com/developer/20060801/26.html
著者:Scott Mitchell
海外internet.com発の記事

はじめに

 Webサイトでよく目にするユーザーインターフェイスのパターンとして、各行にチェックボックスが付いている項目のリストがあります。その典型的な例が、WebベースのEメールクライアントによく見られる、チェックボックスをオンにすることで操作対象のメッセージを選択するというユーザーインターフェイスです。選択した1つ以上のEメールに対し、ユーザーはメッセージの削除、開封済みのマーキング、別フォルダへの移動などのアクションを適用することができます。このようなユーザーインターフェイスの多くは、チェックボックスを1つずつオンにする機能のほかに、[すべて選択]というチェックボックスをヘッダー行に用意しています。このチェックボックスをオンまたはオフにすると、その下にあるリスト内のすべての項目のチェックボックスが自動的にオンまたはオフになります。

 このようなユーザーインターフェイスをASP.NET 2.0GridViewコントロールで作成することは十分に可能であり、これが本稿のテーマです。特に、サーバーサイドコードとクライアントサイドスクリプトを使って「すべて選択(Check All)」と「すべて選択解除(Uncheck All)」の両方の機能を実現する方法を解説します。サーバーサイドコードを使う方がかなり簡単ですが、すべてのチェックボックスをオンまたはオフにすることを選んだときにポストバックが要求されます。それに対し、クライアントサイドのアプローチでは、ユーザーインターフェイスの反応が速くスムーズになりますが、正しく実装するには少し工夫が必要になります。

土台作り:各行にチェックボックスを備えたGridViewを作成する

 GridView内のチェックボックスをオンまたはオフにする機能をどうやって実現するかを説明する前に、まず各行にチェックボックスを備えたGridViewコントロールを作成するにはどうするかを考えてみましょう。GridViewにチェックボックスの列を追加するには、次の2つのアプローチのいずれかを使います。

  • CheckBoxFieldを使う -- CheckBoxFieldは、チェックボックス(CheckBox)の列をレンダリングする組み込み型のGridViewフィールドです。各CheckBoxのCheckedプロパティは、グリッドの基礎となるデータソース内の何らかのデータフィールドに関連付けられます。
  • TemplateFieldを使う -- TemplateFieldを追加して、項目テンプレート(ItemTemplate)内にCheckBox Webコントロールを追加することができます。CheckBox WebコントロールのCheckedプロパティを何らかのデータフィールド値に関連付ける必要がある場合は、<asp:CheckBox runat="server" ... Checked=’<%# Eval("ColumnName") %>’ ... />などの宣言を使用するか、RowDataBoundイベントハンドラなどを使用してCheckedプロパティを設定できます。

 2つ目のTemplateFieldを使う方法には利点がたくさんあります。まず、カスタマイズ性に優れています。各行にマークアップを追加することはもちろん、カスタムヘッダーを提供することも簡単です。実際、本稿のサンプルでは、この列のヘッダーにチェックボックスを置き、ユーザーがそれを使ってGridView上のすべてのチェックボックスをオンまたはオフにできるようにしています。詳細については、『Checking All CheckBoxes in a GridView Using Client-Side Script and a Check All CheckBox』を参照してください。また、CheckBoxのCheckedプロパティを、基礎となるデータフィールドに依存させない場合は、TemplateFieldの方法を使う必要があります。本稿では、TemplateFieldを使うことにします。

 本稿で紹介するサンプルは、Webベースのファイル管理ツールです。このインターフェイスには、現在のディレクトリに含まれているファイルの名前と、各ファイルに対するチェックボックスを表示します。このGridViewの後に、[Delete Checked Files]ボタンを追加します。このボタンをクリックすると、チェックボックスがオンになっているファイルがファイルシステムから削除されます(本稿のサンプルでは、ファイルは実際には削除されず、その機能が実装された場合にどのファイルが削除されるかを示すメッセージが表示されるだけです)。

 必要な機能を追加したGridViewを次に示します。ここでは、TemplateFieldを使用することでGridViewの各行にCheckBoxコントロールを追加しています(簡潔にするため、GridViewのフォーマット関連の設定は一部省略しました)。

<asp:GridView ID="FileList" runat="server"
    AutoGenerateColumns="False" DataKeyNames="FullName">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:CheckBox runat="server" ID="RowLevelCheckBox" />
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="Name" HeaderText="Name" />
        <asp:BoundField DataField="CreationTime" HeaderText="Created On">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
        <asp:BoundField DataField="Length" DataFormatString="{0:N0}"
                     HeaderText="File Size"
            HtmlEncode="False">
            <ItemStyle HorizontalAlign="Right" />
        </asp:BoundField>
    </Columns>
</asp:GridView>

 現在のディレクトリ内のファイルにアクセスしてGridViewにバインドする処理は、Page_Loadイベントハンドラ内に記述します。

Protected Sub Page_Load(ByVal sender As Object, _
                        ByVal e As System.EventArgs) Handles Me.Load
    If Not Page.IsPostBack Then
        Dim dirInfo As New DirectoryInfo(Request.PhysicalApplicationPath)

        FileList.DataSource = dirInfo.GetFiles()
        FileList.DataBind()
    End If
End Sub

 ディレクトリ内のファイルの一覧を表示する方法については、『Displaying the Files in a Directory Using a DataGrid』を参照してください。

 次に必要なのは、[Delete Checked Files]ボタンです。このボタンがクリックされたときに、オンの状態のチェックボックスを判別して、対応するファイルを削除する必要があります。このボタンのClickイベントハンドラでは、GridViewのRowsコレクション内を反復処理し、個々のGridViewRowインスタンスについて、RowLevelCheckBoxがオンになっているかどうかを判別します。オンになっている場合は、そのファイルパスをLabel Webコントロール(Summary)に連結します(実際に動作するアプリケーションでは、System.IO名前空間内のFile.Delete(path)メソッドを使ってファイルを削除する必要があります)。

Protected Sub DeleteButton_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles DeleteButton.Click
    Summary.Text = "The following file would have been deleted:<ul>"

    Dim currentRowsFilePath As String

    ’Enumerate the GridViewRows
    For index As Integer = 0 To FileList.Rows.Count - 1
        ’Programmatically access the CheckBox from the TemplateField
        Dim cb As CheckBox = CType(FileList.Rows(index). _
            FindControl("RowLevelCheckBox"), CheckBox)

        ’If it’s checked, delete it...
        If cb.Checked Then
            currentRowsFilePath = FileList.DataKeys(index).Value
            Summary.Text &= String.Concat( _
                "<li>", currentRowsFilePath, "</li>")
        End If
    Next

    Summary.Text &= "</ul>"
End Sub

 TemplateFieldからCheckBoxを参照するときの構文FileList.Rows(index).FindControl("controlID")に注意してください。CheckBoxFieldを使用した場合は、この構文はFileList.Rows(index).Cells(0).Controls(0)になります。Cells(0)は指定された行の最初の列を表し(ここでは、CheckBoxFieldがGridViewの最初のフィールドであると想定しています)、Control(0)はその列のControlsコレクション内での最初のコントロール、つまりCheckBoxFieldの場合はCheckBoxコントロールを表します。

 土台作りはこれで終わりです。次に、GridView上のすべてのチェックボックスをオンまたはオフにする機能の実装方法を説明します。まず、サーバーサイドコードを使ってこの機能を実現する方法を解説します。

Check All/Uncheck All機能をサーバーサイドコードで実現する

 Check All/Uncheck All機能を実現するために、先ほどのGridViewにButton Webコントロールを2つ追加します。

<p>
    <asp:Button ID="CheckAll" runat="server" Text="Check All" />
    &nbsp;
    <asp:Button ID="UncheckAll" runat="server" Text="Uncheck All" />
</p>

 次に、両方のButtonコントロールのClickイベントハンドラに必要なコードを記述します。具体的には、GridViewのRowsコレクションを反復処理し、各GridViewRowインスタンスのCheckBoxにアクセスし、それぞれのボタンの機能に合わせてCheckedプロパティを設定します。

Protected Sub CheckAll_Click(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles CheckAll.Click
    ’Enumerate each GridViewRow
    For Each gvr As GridViewRow In FileList.Rows
        ’Programmatically access the CheckBox from the TemplateField
        Dim cb As CheckBox = CType( _
            gvr.FindControl("RowLevelCheckBox"), CheckBox)

        ’Check it!
        cb.Checked = True
    Next
End Sub

Protected Sub UncheckAll_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles UncheckAll.Click
    ’Enumerate each GridViewRow
    For Each gvr As GridViewRow In FileList.Rows
        ’Programmatically access the CheckBox from the TemplateField
        Dim cb As CheckBox = CType( _
            gvr.FindControl("RowLevelCheckBox"), CheckBox)

        ’Uncheck it!
        cb.Checked = False
    Next
End Sub

 これで終わりです。サーバーサイドのアプローチの唯一の問題は、Check All/Uncheck All機能を実行するとポストバックが発生し、その結果としてオーバーヘッドやスクリーンフラッシュが生じることです。このような動作はイントラネット環境では目立ちませんが、インターネットのサイトでは、クライアントサイドスクリプトを使って反応速度をできるだけ速くしようとする努力が一般に行われています。クライアントサイドスクリプトでこのような機能を実現する場合は、少し工夫が必要です。クライアントサイドスクリプト機能の説明に移る前に、サーバーサイドのアプローチの実際のスクリーンショットを見てください。[Check All]ボタンまたは[Uncheck All]ボタンをクリックすると、それに応じて、GridView内のすべてのチェックボックスがオンまたはオフになります。次のスクリーンショットは、[Check All]ボタンをクリックした直後の状態です。

Check All/Uncheck All機能をクライアントサイドスクリプトで実現する

 反応をもっと速くするためには、Check All/Uncheck All機能でポストバックを発生させずに、クライアントサイドですべてのチェックボックスをオンまたはオフにすることが理想です。クライアントサイドのJavaScriptを使ってGridView上のチェックボックスにアクセスするには次のパターンを使います。

// Get a reference to the CheckBox
var cb = document.getElementById(’checkBoxID’);

// Adjust the checked state
cb.Checked = false;  // or whatever...

 checkBoxIDは、レンダリングするCheckBoxコントロールのクライアントサイドIDを表しています。これは、CheckBox WebコントロールのIDプロパティの値と必ずしも同じではありません。それどころか、クライアントサイドIDの先頭には、CheckBox Webコントロールの親コントロールのIDが付きます。クライアントサイドスクリプトでCheckBoxを扱うためには、GridView上の一連のCheckBoxのクライアントサイドIDを格納するクライアントサイド配列を作成する必要があります。

 ASP.NET 2.0のClientScriptManagerクラスには、ASP.NETページからClientScriptプロパティを使って直接アクセスできるメソッドが数多く用意されています。このクラスには、ASP.NET 1.xのPageクラスと同じクライアントスクリプト関連のメソッドのほか、追加メソッドもいくつか含まれています。1.xと2.0に共通するメソッドの一つに、RegisterArrayDeclaration(arrayName, arrayValue)があります。このメソッドは、arrayValue入力パラメータで指定された要素のクライアントサイド配列を作成します(1つの配列に複数の要素を追加するには、RegisterArrayDeclaration(arrayName, arrayValue)を繰り返し呼び出して、同じarrayName値を渡します)。

 次のコードでは、GridViewのRowsコレクションを反復処理し、各GridViewRowインスタンスのCheckBoxにアクセスし、そのクライアントサイドID(ClientIDプロパティで取得可能)をクライアントサイド配列(ClientSideIDs)に格納します。

Protected Sub Page_Load(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Me.Load
    If Not Page.IsPostBack Then
        Dim dirInfo As New DirectoryInfo(Request.PhysicalApplicationPath)

        FileList.DataSource = dirInfo.GetFiles()
        FileList.DataBind()
    End If

    ’On every page visit we need to build up the CheckBoxIDs array
    For Each gvr As GridViewRow In FileList.Rows
        ’Get a programmatic reference to the CheckBox control
        Dim cb As CheckBox = CType( _
            gvr.FindControl("RowLevelCheckBox"), CheckBox)

        ClientScript.RegisterArrayDeclaration("CheckBoxIDs", _
            String.Concat("’", cb.ClientID, "’"))
    Next
End Sub

 このコードにより、ページに次のようなJavaScriptが追加されます。

<script type="text/javascript">
<!--
var CheckBoxIDs = new Array(’<i>CheckBoxID1</i>’,
    ’<i>CheckBoxID2</i>’, ... ,’<i>CheckBoxIDN</i>’);
// -->
</script>

 クライアントサイドで挿入されるこのようなタイプの配列は次のポストバックまで残らないので、このコードはPage_Loadイベントハンドラに置き、ページアクセスがあるたびに実行する必要があります。このクライアントサイド配列を初めてページを読み込んだとき(またはGridViewがデータバインドされるとき)にのみ作成した場合は、[Delete Checked Files]ボタンのクリックなどによってポストバックが発生したときに、以降のページ読み込みでクライアントサイド配列CheckBoxIDsが組み込まれません(そのため、Check All/Uncheck All機能のレンダリングが適切に行われなくなります)。

 サーバーサイドコードからクライアントサイドスクリプトを操作する方法の詳細については、『Working With Client-Side Script』を参照してください。

 このクライアントサイドIDの配列を利用することで、この配列の要素を反復処理し、各要素のチェックボックスを参照し、その結果に従ってcheckedプロパティを設定するクライアントサイドスクリプトを作成することができます。今回の例では、この処理を行うために次の2つの関数を作成しました。

  • ChangeCheckBoxState(id, checkState)
  • 指定されたチェックボックスを(document.getElementById(id)を使って)参照し、そのcheckedプロパティをcheckStateパラメータの値に設定します。
  • ChangeAllCheckBoxStates(checkState)
  • CheckBoxIDs配列内を反復処理し、それぞれの要素についてChangeCheckBoxStateを呼び出して、現在の要素のクライアントサイドID値とcheckStateパラメータの値を渡します。

 次に、[Check All]ボタンと[Uncheck All]ボタンを表す2つの<input type="button" ... />をページに追加し、各ボタンのクライアントサイドのonclickイベントからChangeAllCheckBoxStatesを呼び出して、checkStateパラメータに適切な値を渡すようにします。

<script type="text/javascript">
    function ChangeCheckBoxState(id, checkState)
    {
        var cb = document.getElementById(id);
        if (cb != null)
            cb.checked = checkState;
    }

    function ChangeAllCheckBoxStates(checkState)
    {
        // Toggles through all of the checkboxes
        // defined in the CheckBoxIDs array
        // and updates their value to the checkState input parameter
        if (CheckBoxIDs != null)
        {
            for (var i = 0; i < CheckBoxIDs.length; i++)
                ChangeCheckBoxState(CheckBoxIDs[i], checkState);
        }
    }
</script>

...

<p>
    <input type="button" value="Check All"
           onclick="ChangeAllCheckBoxStates(true);" />
    &nbsp;
    <input type="button" value="Uncheck All"
           onclick="ChangeAllCheckBoxStates(false);" />
</p>

 これらの変更を加えることで、[Check All]ボタンと[Uncheck All]ボタンがクライアントサイドだけでタスクを実行するようになり、インターフェイスの反応速度が向上します。本稿のサンプルアプリケーションでは、クライアントサイドのコード例を拡張して、チェックボックスの列のヘッダーにすべてのチェックボックスをオフまたはオフにするチェックボックスを含めています。この拡張例の詳細については、『Checking All CheckBoxes in a GridView Using Client-Side Script and a Check All CheckBox』を参照してください。

まとめ

 本稿では、GridViewコントロール上のすべてのチェックボックスをオンまたはオフにする2つの方法を解説しました。これは、サーバーサイドコードを使えば非常に簡単に実現できますが、ポストバックというコストが生じます。クライアントサイドのアプローチではインターフェイスの反応速度を改善できますが、コードを工夫してJavaScript機能を追加する必要があります。

 それでは、ハッピープログラミング!

著者紹介

Scott Mitchell(Scott Mitchell)

Copyright 2008 Jupitermedia Corporation All Rights Reserved.http://www.internet.com/