QA@IT

ASP.NET[VB]GridViewに追加したHyperLinkFieldのCSSを動的に変更したい

5948 PV

いつもお世話になります。
ASP.netのGridViewコントロールに追加したHyperLinkFieldのCSSを行の値によって動的に変更したいと考えています。

HyperLinkFieldの追加はページロードイベントで行っています。

    Dim hField As New HyperLinkField()
    Dim strFields As String() = {"URL"}
    hField.DataTextField = "選択"
    hField.DataNavigateUrlFields = strFields
    hField.ControlStyle.CssClass = "btn btn-primary"
    grid.Columns.Add(hField)

ControlStyle.CssClassの部分をGridのRowDataBoundイベントで行おうとしているのですがうまくいきません。
ご指導願えないでしょうか。

    e.Row.Cells(2).ControlStyle.CssClass = "btn btn-success"

のように書きましたが意図した部分に適用されません。

回答

HyperLinkField を Page.Load イベントで動的に追加する必要があるのでしょうか?

静的に、デザイナで TemplateField を追加してその中に HyperLink コントロールを配置することで問題なければその方が後の処置が簡単です。HyperLink コントロールに ID を付与し(ドラッグ&ドロップすると自動的に付与されるはず)、GridView.RowDataBound イベントで FindControl メソッドを使って HyperLink コントロールを探し、それの CssClass に設定することが可能です。

どうしても HyperLinkField を Page.Load イベントで動的に追加する必要があれば(そもそもそれはうまく行ってますか?)、

e.Row.Cells(2).ControlStyle.CssClass = "btn btn-success"

ではダメです。3 番目の列が HyperLinkField であるなら、Cells(2) は HyperLink ではないですから。

Cell(2) の中の子コントロールに HyperLink が入っているはずです。Controls プロパティで子コントロールのコレクションを取得し、その中から is 演算子(C# の場合)などを使って HyperLink を探し、それの CssClass に設定してみてください。

**************** 2015/12/17 12:25 追記 ********************

GridView に HyperLinkField を動的に追加した場合のサンプルをアップしておきます。(C# ですが、読めなければ変換サービスを使ってください。例えば http://converter.telerik.com/

なお、この方法をお勧めしているわけではありません。HyperLinkField を使う場合はこう言う面倒なことをせざるを得ないという例を示したと理解ください。

お勧めは上に書いたとおり「デザイナで TemplateField を追加してその中に HyperLink コントロールを配置する」です。そうすればデータバインディング式だけで処置できるかもしれませんし。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>

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

<script runat="server">

    // 動的にコントロールを追加するなら Init イベントで行うべき。
    protected void Page_Init(object sender, EventArgs e)
    {
        HyperLinkField hField = new HyperLinkField();
        hField.DataTextField = "url";
        hField.DataNavigateUrlFields = new string[] { "url" };
        GridView1.Columns.Add(hField);
    }

    // DataTable の作成、GridView へのバインド。
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            DataTable dt = new DataTable();

            dt.Columns.Add(new DataColumn("id", typeof(Int32)));
            dt.Columns.Add(new DataColumn("name", typeof(string)));
            dt.Columns.Add(new DataColumn("url", typeof(string)));

            for (int i = 0; i < 5; i++)
            {
                DataRow dr = dt.NewRow();
                dr["id"] = i;
                dr["name"] = "Name" + i.ToString();
                dr["url"] = "default.aspx?n=" + i.ToString();
                dt.Rows.Add(dr);
            }

            GridView1.DataSource = dt;
            GridView1.DataBind();            
        }
    }

    // HyperLink を探して CssClass を設定。
    // 例としてデータソース中の id が偶数・奇数で css を変えてみた。
    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            foreach (Control control in e.Row.Cells[2].Controls)
            {                
                if (control is HyperLink)
                {
                    DataRowView view = (DataRowView)e.Row.DataItem;
                    int id = (int)view["id"];
                    if ((id % 2) == 0)
                    {
                        // id が偶数
                        ((HyperLink)control).CssClass = "even";
                    }
                    else
                    {
                        // id が奇数
                        ((HyperLink)control).CssClass = "odd";
                    }
                    break;
                }
            }
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server" 
            AutoGenerateColumns="False" 
            OnRowDataBound="GridView1_RowDataBound">
            <Columns>
                <asp:BoundField DataField="id" HeaderText="id" />
                <asp:BoundField DataField="name" HeaderText="name" />
            </Columns>
        </asp:GridView>
    </div>
    </form>
</body>
</html>

上のコードを実行すると table 部分の html ソースは以下のようになります。

<table cellspacing="0" rules="all" border="1" id="GridView1" style="border-collapse:collapse;">
  <tr>
    <th scope="col">id</th><th scope="col">name</th><th scope="col">&nbsp;</th>
  </tr><tr>
    <td>0</td><td>Name0</td><td><a class="even" href="default.aspx?n=0">default.aspx?n=0</a></td>
  </tr><tr>
    <td>1</td><td>Name1</td><td><a class="odd" href="default.aspx?n=1">default.aspx?n=1</a></td>
  </tr><tr>
    <td>2</td><td>Name2</td><td><a class="even" href="default.aspx?n=2">default.aspx?n=2</a></td>
  </tr><tr>
    <td>3</td><td>Name3</td><td><a class="odd" href="default.aspx?n=3">default.aspx?n=3</a></td>
  </tr><tr>
    <td>4</td><td>Name4</td><td><a class="even" href="default.aspx?n=4">default.aspx?n=4</a></td>
  </tr>
</table>
編集 履歴 (1)

SurferOnWwwさん>
ソースまで書いていただきありがとうございます!
サンプルを参考に無事実現ができました。
感謝です!

編集 履歴 (0)

自分がお勧めする TemplateField + HyperLink を使った場合のサンプルもアップしておきます。CssClass はデータバインディング式だけで設定しています。同じ結果を得るのに、先の回答のコードとどちらが簡単・スマートで、かつ既存のコードに影響が少ないかといえば、こちらの方だと思います。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>

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

<script runat="server">
    // DataTable の作成、GridView へのバインド。
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            DataTable dt = new DataTable();

            dt.Columns.Add(new DataColumn("id", typeof(Int32)));
            dt.Columns.Add(new DataColumn("name", typeof(string)));
            dt.Columns.Add(new DataColumn("url", typeof(string)));

            for (int i = 0; i < 5; i++)
            {
                DataRow dr = dt.NewRow();
                dr["id"] = i;
                dr["name"] = "Name" + i.ToString();
                dr["url"] = "default.aspx?n=" + i.ToString();
                dt.Rows.Add(dr);
            }

            GridView1.DataSource = dt;
            GridView1.DataBind();
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
  <form id="form1" runat="server">
    <div>
      <asp:GridView ID="GridView1" runat="server" 
        AutoGenerateColumns="False">
        <Columns>
          <asp:BoundField DataField="id" HeaderText="id" />
          <asp:BoundField DataField="name" HeaderText="name" />                
          <asp:TemplateField>
            <ItemTemplate>
              <asp:HyperLink ID="HyperLink1" 
                runat="server" 
                NavigateUrl='<%# Eval("url") %>' 
                Text='<%# Eval("url") %>'
                CssClass='<%# ((int)Eval("id") % 2 == 0)? "even" : "odd" %>'>
              </asp:HyperLink>
            </ItemTemplate>
          </asp:TemplateField>
        </Columns>
      </asp:GridView>
    </div>
  </form>
</body>
</html>

データバインド式が複雑で上記のように簡単にはいかない場合は、別にコードビハインド側にメソッドを定義してそれを設定することができます。詳しくは以下の記事を見てください。

データバインド式
http://surferonwww.info/BlogEngine/post/2010/08/17/Data-bind-method.aspx

編集 履歴 (2)

SurferOnWwwさん>
ありがとうございます。
”HyperLinkField を Page.Load イベントで動的に追加する必要があるのでしょうか?”
おっしゃる通りデザイナで列を追加しても問題はないと思いますが既存の動いてるプログラムへの変更依頼があり今回投稿した次第です。

RowDataBoundイベント
e.Row.Cells(2).ControlStyle.CssClass = "btn btn-success"

サンプルで書いたものはHTMLのソース上で言えばtdタグに適用されていました。
やりたいことはtdタグ内の
aタグのCssClassを変更したいのです。

>Cell(2) の中の子コントロールに HyperLink が入っているはずです。
>Controls プロパティで子コントロールのコレクションを取得し、
>その中から is 演算子(C# の場合)などを使って HyperLink を探し、
>それの CssClass に設定してみてください。
中の該当Controlを探し適切に処理するということですね。

今回、既存のプログラムへの修正ということで”GridViewに追加したHyperLinkFieldのCSSを動的に変更したい”という質問を投げさせていただきましたが本来、GridViewの列の値によってHyperLinkFieldのCSSを動的に変えるというのはどのように書くのが適切なのでしょうか?

編集 履歴 (0)
  • 「デザイナで列を追加しても問題はない」のであればそうすべきで、動的に追加するというイバラの道(?)へ進む必要は全くないと思いますが・・・ あとでサンプルを解答欄に追記しておきます。 -
  • GridView に HyperLinkField を動的に追加した場合のサンプルをアップしましたが、それに加えて、自分がお勧めする TemplateField + HyperLink を使った場合のサンプルもアップしておきます。最初の回答に追記すると長くなりすぎるので、別の回答欄を使います。 -
ウォッチ

この質問への回答やコメントをメールでお知らせします。