japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2008年10月8日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2006年8月29日 10:00

クライアントサイドスクリプトを使ってGridView上のすべてのチェックボックスをオンにする方法

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!

はじめに

 先週の記事『GridView上のすべてのチェックボックスをオンにする方法』では、1個のボタンのクリックでGridView上のすべてのチェックボックスをオンまたはオフにする機能を追加する方法を説明しました。この機能を実現するにはサーバーサイドコードを使う方法と、クライアントサイドスクリプトを使う方法の2つがあり、両方について詳しく解説しました。どちらのバージョンでも、ページには2個のボタン――「すべて選択(Check All)」と「すべて選択解除(Uncheck All)」――が配置され、これをクリックするとすべてのチェックボックスがオンまたはオフになります。サーバーサイドコードを使うバージョンでは、どちらのボタンをクリックしてもポストバックが発生し、プログラムによってすべてのチェックボックスのオンまたはオフが実行されます。クライアントサイドのバージョンでは、チェックボックスのオン/オフは短いJavaScriptコードで処理されます。

 今回の記事では、このユーザーインターフェイスを改良し、クライアントサイドスクリプトを利用して、GridViewのチェックボックス(CheckBox)列の上にあるヘッダー行に[すべて選択/選択解除(Check/Uncheck All)]チェックボックスを用意することにします(次のスクリーンショットを参照)。このヘッダーのチェックボックスをオンにするとGridView上のすべてのチェックボックスがオンになり、逆にこれをオフにするとGridView上のすべてのチェックボックスがオフになります。この機能を追加するのは最初に考えていたよりも少しトリッキーな作業で、ちょっとした工夫が必要でした。この記事では、そういった問題点と、それを解決するために使った迂回策について詳細に見ていきます。それでは説明に移ります。

 前回の記事『GridView上のすべてのチェックボックスをオンにする方法』をまだ読んでいなければ、先に進む前に目を通しておいてください。

[Check/Uncheck All]チェックボックスをヘッダーに追加する

 このユーザーインターフェイスを実装する最初のステップでは、チェックボックス列のヘッダーに[Check/Uncheck All]チェックボックスを追加します。GridView上のチェックボックス列はTemplateFieldとして実装したので、ヘッダーにCheckBoxを追加するのに必要な作業は、次のようにTemplateFieldにHeaderTemplateを追加することだけです(簡潔にするため、GridViewのフォーマット関連の設定は一部省略しました)。

<asp:GridView ID="FileList" runat="server"
   AutoGenerateColumns="False" DataKeyNames="FullName">
   <Columns>
      <asp:TemplateField>
         <HeaderTemplate>
            <asp:CheckBox runat="server" ID="HeaderLevelCheckBox" />
         </HeaderTemplate>
         <ItemTemplate>
            <asp:CheckBox runat="server" ID="RowLevelCheckBox" />
         </ItemTemplate>
      </asp:TemplateField>

      <asp:BoundField DataField="Name" HeaderText="Name" />
      ... Remaining BoundFields ...
   </Columns>
</asp:GridView>

 このコードを追加すると、チェックボックス列のヘッダーにCheckBoxが配置されますが、このヘッダーのCheckBoxをクリックしても何も起こりません。ヘッダーのCheckBoxをオンにするとグリッド内のすべてのCheckBoxがオンになり、同様にヘッダーのCheckBoxをオフにするとグリッド内のすべてのCheckBoxがオフになるような仕組みが必要です。また、オン/オフの反応速度を改善するために、この処理を完全にクライアントサイドでJavaScriptを使って行うことも必要です。

 「GridView上のすべてのチェックボックスをオンにする方法」では、JavaScript関数ChangeAllCheckBoxStates(checkState)を詳しく見ていきました。この関数は、checkState入力パラメータの値に基づいてグリッド上のすべてのCheckBoxをオンまたはオフにします。前回の記事で紹介したクライアントサイドのデモの場合は、この関数を[Check All]ボタンまたは[Uncheck All]ボタンから呼び出し、その際にcheckState入力パラメータに値true([Check All]の場合)またはfalse([Uncheck All]の場合)を渡していました。今回まず考えたのは、このJavaScript関数をヘッダーのCheckBoxで再利用することでした。つまり、ヘッダーのCheckBoxのクライアントサイドonclickイベントでChangeAllCheckBoxStatesを呼び出し、checkStateパラメータにthis.checkedを渡すというものです。

 この処理を行うために、Page_Loadイベントハンドラで以下のサーバーサイドコードを使用して、ヘッダーのCheckBoxのクライアントサイドonclickイベントを関連付けました。

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

    Get the header CheckBox
    Dim cbHeader As CheckBox = _
        CType(FileList.HeaderRow.FindControl( _
        "HeaderLevelCheckBox"), CheckBox)

    Run the ChangeCheckBoxState client-side function whenever the
    header checkbox is checked/unchecked
    cbHeader.Attributes("onclick") = _
        "ChangeAllCheckBoxStates(this.checked);"

    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)

        ’Add the CheckBox’s ID to the client-side CheckBoxIDs array
        ClientScript.RegisterArrayDeclaration( _
            "CheckBoxIDs", String.Concat("’", cb.ClientID, "’"))
    Next
End Sub

 Page_Loadイベントハンドラの太字ではない部分は、前回の記事からそのままコピーしたコードです。最初の部分では、ページが最初に表示されたときにデータをGridViewにバインドします(それ以降のポストバックではこのバインドを行いません)。For Eachループの最後では、CheckBoxIDsクライアントサイド配列を作成します。この配列は、ChangeAllCheckBoxStates関数ですべてのグリッドのCheckBoxのオンまたはオフを行うときに、反復処理の対象となります。今回、[Check/Uncheck All]ヘッダーのCheckBoxをサポートするために追加したのが太字の部分です。特に、GridViewのHeaderRowのコントロールコレクションを検索してIDが"HeaderLevelCheckBox"のコントロールを見つけ、ヘッダーのCheckBoxへの参照を取得する部分に注目してください("HeaderLevelCheckBox"は、前述の宣言構文で宣言したヘッダーのCheckBoxのID値です)。

 ヘッダーのCheckBoxへの参照を取得したら、次にクライアントサイドのonclickイベントハンドラでChangeAllCheckBoxStates関数が呼び出されるようにします。そのためには、CheckBoxのAttributesコレクションのonclick設定にChangeAllCheckBoxStates関数の呼び出しを割り当て、パラメータとしてthis.checkedを渡します。Attributesコレクションは、コントロールの外観となるHTMLに追加の属性値を指定するための手段です。クライアントサイドのHTML属性の操作とサーバーサイドASP.NETコードの使用方法については、記事『Working with Client-Side Script』を参照してください。

 このコードを追加したことにより、ヘッダーのCheckBoxがオンになるとChangeAllCheckBoxStates(true)が呼び出されてグリッド上のすべてのCheckBoxがオンになり、逆にヘッダーのCheckBoxがオフになるとChangeAllCheckBoxStates(false)が呼び出されてすべてのCheckBoxがオフになります。

問題1

 以上の変更を行った後に最初に気がついた問題は、ページ上の[Check All]ボタンや[Uncheck All]ボタンをクリックしても、ヘッダーのCheckBoxがオンまたはオフにならないことでした。グリッド上の他のCheckBoxは、それまでどおり正しくオン、オフされます。原因は、この2つのボタンはChangeAllCheckBoxStates関数を呼び出しており、ChangeAllCheckBoxStates関数は、CheckBoxIDs配列だけを反復処理して、この配列に登録されたすべてのCheckBoxをオンまたはオフにしているからです。ヘッダーのCheckBoxはこの配列に含まれないため、ChangeAllCheckBoxStates関数を呼び出してもオン/オフの状態は以前と変わりません(もちろん、ヘッダーのCheckBoxを直接オン/オフするときはこの問題は起こりません。ユーザーの操作によってCheckBoxのオン/オフが適切に処理されるからです)。

 この問題を修正するため、単純な方法ですが、ヘッダーのCheckBoxをCheckBoxIDs配列内のIDリストに加えました。この追加処理は、Page_Loadイベントハンドラ内でヘッダーのCheckBoxのAttributes("onclick")プロパティを設定した直後に行います。以下にそのコードを示します。

’Add the CheckBox’s ID to the client-side CheckBoxIDs array
ClientScript.RegisterArrayDeclaration( _
    "CheckBoxIDs", String.Concat("’", cbHeader.ClientID, "’"))

 ヘッダーのCheckBoxのIDを配列に追加すると、ユーザーが[Check All]ボタンと[Uncheck All]ボタンをクリックしたときに、ヘッダーのCheckBoxがグリッド上の下位のCheckBoxと共に正しくオンまたはオフになります。

問題2

 ヘッダーのCheckBoxをオンにすると、グリッド上のすべてのチェックボックスがオンになるのですから、当初私は、ヘッダーのCheckBoxは一定のアクション(具体的に言うと、グリッド上のすべての下位チェックボックスをオンにするアクション)を単独で実行するユーザーインターフェイス項目であると考えていました。しかし実際には、ヘッダーのCheckBoxは、グリッド上のチェックボックスの状態を反映しているに過ぎません。つまり、ヘッダーのCheckBoxがオンになっていれば、グリッド上のすべての他のチェックボックスもオンになっていることを意味します。ヘッダーチェックボックスがオフになっていれば、グリッド上のチェックボックスの1個以上がオフになっています。

 このような事情を勘案すれば、グリッド上の他のチェックボックスがすべて手動でオンにされたときにヘッダーチェックボックスが自動的にオンになるような仕組みが必要なことは明らかです。またその反対に、その他のすべてのチェックボックスがオンにされた後でその1個が手動でオフにされた場合は、ヘッダーチェックボックスが自動的にオフになる必要もあります。この仕組みを実現するため、次のように修正することにしました。

  1. ChangeHeaderAsNeeded()というJavaScript関数を追加し、この関数の中で、グリッド上の他のCheckBoxがすべてオンの場合はヘッダーのCheckBoxをオンにし、オフのCheckBoxが1個でもある場合はヘッダーのCheckBoxをオフにする。
  2. 下位の各CheckBoxのクライアントサイドonclickイベントがChangeHeaderAsNeeded()関数を呼び出すように関連付ける。

 まずはChangeHeaderAsNeeded()関数を見ていきましょう。この関数では、CheckBoxIDs配列の最初の要素がヘッダーのCheckBoxのIDであり、それ以外の要素が下位CheckBoxのIDであることを前提としています。ループを使って下位CheckBoxを1つずつチェックして、オンかどうかを確認します。オンではないCheckBoxが1つでもあれば、ヘッダーのCheckBoxをオフにして関数を終了します。オフの下位CheckBoxが見つからないままループが終わった場合は、ヘッダーのCheckBoxをオンにします。

function ChangeHeaderAsNeeded()
{
    // Whenever a checkbox in the GridView is toggled, we need to
    // check the Header checkbox if ALL of the GridView checkboxes are
    // checked, and uncheck it otherwise
    if (CheckBoxIDs != null)
    {
        // check to see if all other checkboxes are checked
        for (var i = 1; i < CheckBoxIDs.length; i++)
        {
            var cb = document.getElementById(CheckBoxIDs[i]);
            if (!cb.checked)
            {
                // Whoops, there is an unchecked checkbox, make sure
                // that the header checkbox is unchecked
                ChangeCheckBoxState(CheckBoxIDs[0], false);
                return;
            }
        }

        // If we reach here, ALL GridView checkboxes are checked
        ChangeCheckBoxState(CheckBoxIDs[0], true);
    }
}

 次に、下位CheckBoxを設定して、クライアントサイドonclickイベントの発生時にChangeHeaderAsNeeded()関数が呼び出されるようにします。具体的には、以下のクライアントサイドコードをPage_Loadイベントハンドラ内のFor Eachループに追加します。

Protected Sub Page_Load(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Me.Load
    ... Code omitted for brevity ...
    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)

        If the checkbox is unchecked, ensure 
        that the Header CheckBox is unchecked
        cb.Attributes("onclick") = "ChangeHeaderAsNeeded();"

        ’Add the CheckBox’s ID to the client-side CheckBoxIDs array
        ClientScript.RegisterArrayDeclaration( _
            "CheckBoxIDs", String.Concat("’", cb.ClientID, "’"))
    Next
End Sub

 これで終わりです。この変更により、すべてのチェックボックスを手動でオンにするとヘッダーのCheckBoxがオンになります。同様に、すべての下位チェックボックスがオンのときに1個のチェックボックスをオフにすると、ヘッダーのCheckBoxがオフになります。

ヘッダーのCheckBoxのIDが常に最初にあると前提しても安全か?

 ChangeHeaderAsNeeded()関数では、ヘッダーのCheckBoxのIDが常にCheckBoxIDs配列の最初のIDであることを前提としています。でも、この前提は常に正しいのでしょうか? コードでは、下位CheckBoxのIDを追加する前にヘッダーのCheckBoxのIDを追加するので、そう信じる根拠はあります。ヘッダーのCheckBoxのIDは、CheckBoxIDs配列の最初の要素になるはずです。

 この前提が正しいのは、ASP.NET 2.0を使用しているときです。ASP.NET 2.0の内部のRegisterArrayDeclaration(arrayName, arrayValue)メソッドでは、ListDictionaryオブジェクトに配列要素を格納してから、それらの要素をクライアントサイドの配列にレンダリングするからです。ListDictionaryはFIFO(First In, First Out)で動作するので、要素はサーバーサイドコードで追加した順序でクライアントサイド配列に格納されます。

 ただし、ASP.NET 1.xを使用していると、RegisterArrayDeclaration(arrayName, arrayValue)の実装でHybridDictionaryが使われます。HybridDictionaryでは、最初にListDictionaryを使って項目が保存されますが、要素が一定の数を超えると(具体的には9番目の要素から)Hashtableが使われます。Hashtableの要素は、ハッシュ値に基づく順序で返されるため、必ずしもプログラムで追加した順序と同じではありません。この問題を回避するには、RegisterArrayDeclarationメソッドのロジックを独自の方法(素のJavaScriptそのものを吐き出すなど)で置き換えるか、Peter BlumのフリーのRegisterScriptsライブラリを使います。HybridDictionaryの順序の問題と使用可能な迂回策については、記事『Specialized Collections』を参照してください。

まとめ

 この記事では、クライアントサイドのテクニックを使ってGridViewのヘッダーに[Check/Uncheck All]チェックボックスを追加して、すべての下位チェックボックスを一度にオンまたはオフにする機能を用意する手順を説明しました。[Check All]ボタンと[Uncheck All]ボタンに加えてヘッダーの[Check/Uncheck All]チェックボックスを用意することで、グリッドのすべてのチェックボックスをオンまたはオフにする複数の方法をユーザーに提供できます。クライアントサイドスクリプトを使ってチェックボックスのオン、オフを処理するので、このインターフェイスは応答速度が速くなります。

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

著者紹介

Scott Mitchell(Scott Mitchell)
最新トップニュース
Graphic Design Forum
【Graphic Design Forum】
活気に満ちた誕生日をどうぞ (10月8日)
データメーション
【データメーション】
Google 版酒気検知機能が間もなく登場(10月8日)
ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」
【ベンチャー専門家の目利きブログ「なぜこの企業は伸びるのか?」】
「ITを活用し、消費生活における意思決定の支援、悩み・迷いを解決する!」/株式会社ALBERT(10月8日)
エンジニアの独り言
【エンジニアの独り言】
得体の知れない情報(?)との向き合い方(9月17日)
最新テクノロジーの意外な処方箋
【最新テクノロジーの意外な処方箋】
昆虫と退屈なことについて(9月16日)
気になるトレンド用語
気になるトレンド用語
はてなブックマークが変わる!そもそもブラウザのお気に入りと何が違うの?(10月8日)
e-Japan 先端テクノロジー解説
e-Japan 先端テクノロジー解説
行政サービスのマルチチャネル化について(10月8日)
ウチのサイトを SEO
ウチのサイトを SEO
ちゃんと title つけていますか?(10月8日)
百式のネットビジネス研究
百式のネットビジネス研究
Blog 記事の編集を読者に任せることができる「gooseGrade」(10月8日)
「IT の耳」
「IT の耳」
【書評】ニコ動から RMT まで〜『人はなぜ形のないものを買うのか―仮想世界のビジネスモデル』(10月7日)
DevX
DevX
アジャイルソフトウェアプロジェクトを管理する(10月7日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
SEって、デジタル製品は判官びいきで選ぶよね?(10月7日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
フル CSS でサイトを構築する SEO のメリット(10月7日)
モバイルSEO@フラクタリスト
モバイルSEO@フラクタリスト
応用的な SEO 施策(3)(10月6日)
サーチからはじまるインタラクティブエージェンシー
サーチからはじまるインタラクティブエージェンシー
DB マーケティングと Web マーケティング 〜ビールとオムツの伝説から〜(10月6日)
海外のインターネットコムアメリカ韓国ドイツトルコ
Copyright 2008 Jupitermedia Corporation All Rights Reserved.http://www.internet.com/