WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

GridView 内の RadioButton

by WebSurfer 2013年8月30日 14:02

GridView の 1 行のみを選択する目的で、GridView の各行に RadioButton を配置する場合の注意点を備忘録として書いておきます。

GridView 内の RadioButton

あるラジオボタンのグループで、単一のラジオボタンしか選択できないようにするには、そのグループの中のラジオボタンの name 属性の値を同一にします。

ところが GridView に 配置した RadioButton の場合、サーバー側のコードで name 属性が同一になるように設定しても、それからレンダリングされる input 要素の name 属性の値は ASP.NET によって書き換えられ、全て異なった値になります。結果、ラジオボタンなのに複数の行が選択できてしまいます。

理由は以下の通りです。

名前付けコンテナー(INamingContainer インターフェイスを実装するコントロール)内にコントロールが配置されていると、ASP.NET は名前付けコンテナーの ID をコントロールの ID に追加して ClientID 値を生成します。

GridView の場合、その個別の行をあらわす GridViewRow が名前付けコンテナーであり、それから HTML の繰り返しブロックがレンダリングされますが、その中に配置された RadioButton には連続番号を含むプレフィックスが追加され、ページの中で一意になる(同じ ID が無い)ように ClientID 値が生成されます。

RadioButton の場合は name 属性も生成され、その値もページの中で一意になるよう、名前付けコンテナーの ID がプレフィックスとして追加されます。(ClientID とは命名規則が異なりますが、ページ内で一意になる点は同じです)

例えば、今回の記事の例の場合、GridView 内のラジオボタンの name は GridView1$ctl02$RadioButton1 というようになり、その中で ctl02 の部分が行によって異なります。

ASP.NET が name の値を生成するタイミングは、PreRender イベントが完了した後、html コードをレンダリングする時のようです。従って、それ以前にサーバー側で name 属性をいかように設定しても、ブラウザに送信される html コードは名前付けコンテナーの ID が追加されたものに書き換えられてしまいます。

なので、RadioButton.GroupName プロパティを設定するとか、GridView の RowCreated や PreRender イベントなどのハンドラで全ての RadioButton の name 属性を同一に書き換えるなどしても効果はありません。

何か方法はないか探してみましたが、サーバー側で設定する方法は見つけられませんでした。

結局、ブラウザが html コードの読み込みを完了した後、クライアントスクリプトで当該ラジオボタンの name 属性を同一に書き換えてやることで対応しました。

以下のサンプルコードのような感じです。jQuery を利用すると比較的簡単にできます。ただし、当然ですが、ブラウザで JavaScript が無効になっているとダメです。

自分が見つけてない、サーバー側のコードで対応できる、もっと簡単な方法があるかもしれません。

2013/8/31 追記:
愚直に name 属性の値を書き換えるより簡単な方法がありました。詳しくはこの記事の下の方の「------ 2013/8/31 追記 ------」を見てください。また。サーバー側だけで対応する方法も書いておきました。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  protected void Button1_Click(object sender, EventArgs e)
  {
    string ids = "";
    for (int i = 0; i < GridView1.Rows.Count; i++)
    {
      GridViewRow row = GridView1.Rows[i];
      RadioButton rb = 
        (RadioButton)row.FindControl("RadioButton1");
      if (rb != null)
      {
        if (rb.Checked == true)
        {
          ids += GridView1.DataKeys[i].Value.ToString() + " ";
        }
      }
    }
    Label1.Text = "Setected product(s): " + ids;
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="Scripts/jquery-1.8.3.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    // サーバー側で設定されたラジオボタンの name 属性
    // のオリジナル値を保存しておくための配列。
    var originalNames;

    // ラジオボタンの name 属性の元の値を配列に保存した
    // 上で、同一(この例では radiobutton1)に書き換え。
    $(function () {
      originalNames = new Array();
      $('#<%=GridView1.ClientID%> input:radio').each(
        function () {
          originalNames.push($(this).attr('name'));
          $(this).attr('name', 'radiobutton1');
        });
    });

    // ポストバックする前に、ラジオボタンの name 属性を
    // オリジナル値に戻すためのメソッド。オリジナル値に
    // 戻さないとチェックされたラジオボタンをサーバー側
    // で取得できず、かつ、再描画されたときにチェックマ
    // ークがつかない。
    // この例では Button1.OnClientClick プロパティにこの
    // メソッドを設定している。
    function RetrieveOriginalNames() {
      $('#<%=GridView1.ClientID%> input:radio').each(
        function (n) {
          $(this).attr('name', originalNames[n]);
        });
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Northwind %>" 
      SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice] 
        FROM [Products] 
        WHERE ([CategoryID] = @CategoryID)">
      <SelectParameters>
        <asp:Parameter DefaultValue="1" 
          Name="CategoryID" Type="Int32" />
      </SelectParameters>
    </asp:SqlDataSource>

    <asp:GridView ID="GridView1" 
      runat="server" 
      AutoGenerateColumns="False" 
      DataKeyNames="ProductID" 
      DataSourceID="SqlDataSource1">
      <Columns>
        <asp:TemplateField HeaderText="選択">
          <ItemTemplate>
            <asp:RadioButton ID="RadioButton1"
              runat="server" />
          </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="ProductID" 
          HeaderText="ProductID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" 
          HeaderText="ProductName" 
          SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" 
          HeaderText="UnitPrice" 
          SortExpression="UnitPrice" />
      </Columns>
    </asp:GridView>

    <asp:Button ID="Button1" runat="server" 
      Text="Button" 
      OnClick="Button1_Click" 
      OnClientClick="RetrieveOriginalNames();" />
    <br />
    <asp:Label ID="Label1" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

------ 2013/8/31 追記 ------

name 属性は書き換えないでそのままにしておいて、あるラジオボタンをチェックしたら他のラジオボタンのチェックを外すようにする方が簡単でした。以下のような感じです。

<script src="Scripts/jquery-1.8.3.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
  // ラジオボタンがチェックされたら、一旦全てのラジオ
  // ボタンのチェックを外し、チェックされたラジオボタ
  // ンに改めてチェックを入れ直す方がはるかに簡単。
  $(function () {
    $('#<%=GridView1.ClientID%> input:radio').change(
      function () {
        $('#<%=GridView1.ClientID%> input:radio').
          removeAttr("checked");
        $(this).attr('checked', 'checked');
      });
  });

  // Button1 クリックで name を元の値に書き戻す必要は
  // なくなる。
//]]>
</script>

その他、クライアントスクリプトを使わないでサーバー側だけで対応する方法として、RadioButton を継承したカスタムコントロールを作る方法があります。その例は以下のページを見てください。

データバインドコントロール内で使用できるカスタムラジオボタンの作成

DataGrid内のラジオボタンでグループに出来ない問題の回避方法

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar