japan.internet.com The Internet & IT Network


RSSニュース検索
カテゴリ
> トップページ
> Webビジネス
> Eコマース
> Webファイナンス
> Webマーケティング
> パブリック
> Webテクノロジー
> 携帯・ワイヤレス
> Linux Today
> Linux Tutorial
> J.I.C.ブログ
キャリア
> 転職ならen
> 派遣ならen
> アルバイトならen
> IT求人情報
ヘッドライン
> 今日のヘッドライン
> 週間ヘッドライン
Special Link
> フォトコミュニティ
> ストックフォト
> クリップアート
> イラスト
> フェリカ
> Web2.0
> 写真
イベント&セミナー
> イベントカレンダー
> 書評「IT の耳」
> 出張・接待検索
> ニュースガジェット 注目
無料ニュースメール
> 新規登録
> 変更・解除
> オプトインメールの登録・変更・解除
インフォメーション
> パートナーサイト
転職ならエン
就職ならen
求人ならen
履歴書ならen
アルバイトならエン
CRM/SFAならオラクル
> 会社概要地図
> グループ会社
株式会社アエリア
(株)サンゼロミニッツ
株式会社エアネット
> お問い合わせ
> 広告掲載について
> リンクについて
> 著作権について
> その他お問い合わせ
> 利用規約
> 個人情報保護方針
コラム コラム一覧へ戻る

japan.internet.com 編集部 japan.internet.com 編集部
米国 Jupitermedia が運営する、ASP と ASP ネットワーク情報に関するサイト。

 メール  著者にメールする
 ホーム  http://www.4guysfromrolla.com/

最新コラム

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

著者: Scott Mitchell プリンター用 記事を転送
2006年8月29日 10:00 付の記事
■海外internet.com発の記事

はじめに

 先週の記事『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)

過去コラム集
CreateUserWizardコントロールのマルチステッププロセス化
GridView上のすべてのチェックボックスをオンにする方法
大量の結果セットをさらに効率的にページングする方法
ASP.NET 2.0とSQL Server 2005によるカスタムのページング処理
SQL Server 2000で大量の結果セットを効率的にページングする方法
ASP.NET 2.0でカスタムのページング処理と並び替えを併用する
ListItemの属性をレンダリングできるWebカスタムコントロールの作成
Repeaterで作る折りたたみ可能な詳細領域
ASP.NETを使ってランダムなパスワードを生成する
ASP.NETで機密データを暗号化して保存する
海外のインターネットコム アメリカ韓国ドイツトルコ
関連企業のサイト:ストックフォト イラスト ネットストリート ホテル予約サイト タウン情報 出張 事業継承 シミュレーション トランクルーム 優待映画チケット 田舎暮らしガイド オリジナルTシャツ ニタコエ
Copyright 2008 Jupitermedia Corporation All Rights Reserved. http://www.internet.com/
space.gif space.gif