WebSurfer's Home

Filter by APML

DataGrid, GridView に動的に列を追加

by WebSurfer 17. May 2013 16:41

あまり使い道はないと思いますが、DataGrid と GridView に動的に列を追加する方法を備忘録として書いておきます。 (【2021/9/21 追記】もっと簡単かつスマートにできる方法がありました。詳しくは「DataGrid, GridView に動的に列を追加 (2)」を見てください)

DataGrid に動的に列を追加

DataGrid, GridView ともヘッダ、フッタを含め各行は TableCellCollection で構成されているので、そのコレクションの適切な位置に TableCell を追加することが基本です。

追加したセルの中に文字列や CheckBox などを配置したい場合は、当該セルの ControlCollection に LiteralControl や CheckBox を初期化して追加します。

追加するタイミングは、DataGrid なら ItemCreated イベント、GridView なら RowCreated イベントがよさそうです。

その例を以下のコードに示します。上の画像を表示したものです。実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ 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">

  // 表示用のデータソース (DataView) を生成
  ICollection CreateDataSource()
  {
    DataTable dt = new DataTable();
    DataRow dr;
       
    dt.Columns.Add(new DataColumn("Item", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Name", typeof(string)));
    dt.Columns.Add(new DataColumn("Price", typeof(decimal)));

    for (int i = 0; i < 5; i++)
    {
      dr = dt.NewRow();

      dr["Item"] = i;
      dr["Name"] = "Name-" + i.ToString();
      dr["Price"] = 1.23m * (i + 1);

      dt.Rows.Add(dr);
    }

    DataView dv = new DataView(dt);
    return dv;
  }

  // 初回のみデータソースを生成してバインド(ポストバック
  // 時は ViewState から自動的にデータを取得するので不要)
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack)
    {
      ICollection dv = CreateDataSource();
      DataGrid1.DataSource = dv;
      DataGrid1.DataBind();
      GridView1.DataSource = dv;
      GridView1.DataBind();
    }
  }
    
  // GataGrid.ItemCreated イベントで列を動的に追加する。
  // ItemDataBound イベントで行うと以下の問題があるので
  // 注意:
  // (1) ポストバック時にはイベントが発生しないので追加
  //     行を再生できず、結果消えてしまう。
  // (2) 追加コントロールの LoadViewState, LoadPostData 
  //     の呼び出しがうまくいかない。結果、以下の例では 
  //     CheckBox の CheckedChanged イベントの発生が期
  //     待通りにならない。
  protected void DataGrid1_ItemCreated(object sender, 
    DataGridItemEventArgs e)
  {
    if (e.Item.ItemType == ListItemType.Header)
    {
      // ヘッダ行に列(セル)を追加。セルの中にリテ
      // ラルを配置。
      TableCell cell = new TableCell();
      cell.Controls.Add(new LiteralControl("追加列"));
      e.Item.Cells.Add(cell);
    }
    else if (e.Item.ItemType == ListItemType.Item ||
      e.Item.ItemType == ListItemType.AlternatingItem)
    {
      // データ行に列(セル)を追加。セルの中に
      // CheckBox を配置。
      TableCell cell = new TableCell();
      CheckBox cb1 = new CheckBox();
      cb1.AutoPostBack = true;
      cb1.ID = "CheckBoxInItemIndex-" + 
        e.Item.ItemIndex.ToString();
      cb1.CheckedChanged += 
        new EventHandler(cb1_CheckedChanged);
      cell.Controls.Add(cb1);
      e.Item.Cells.Add(cell);
    }
    else if (e.Item.ItemType == ListItemType.Footer)
    {
      // フッタ行に列(セル)を追加。セルの中にリテ
      // ラルを配置。
      TableCell cell = new TableCell();
      cell.Controls.Add(new LiteralControl("追加列"));
      e.Item.Cells.Add(cell);
    }
  }

  protected void cb1_CheckedChanged(object sender, 
    EventArgs e)
  {
    CheckBox cb = (CheckBox)sender;
    Label1.Text = cb.ID + " clicked to " + 
      cb.Checked.ToString();
  }

  // DataGrid の場合と同様な理由で RowDataBound イベ
  // ントではなく RowCreated イベントで動的に列を追
  // 加する。
  protected void GridView1_RowCreated(object sender, 
    GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Header)
    {
      // ヘッダ行に列(セル)を追加。セルの中にリテ
      // ラルを配置。
      // GridView のヘッダ行は th 要素が使われるので、
      // TableCell でなく TableHeaderCell を用いる。
      TableHeaderCell hc = new TableHeaderCell();
      hc.Controls.Add(new LiteralControl("追加列"));
      e.Row.Cells.Add(hc);
    }
    else if (e.Row.RowType == DataControlRowType.DataRow)
    {
      // データ行に列(セル)を追加。セルの中に
      // CheckBox を配置。
      TableCell cell = new TableCell();
      CheckBox cb2 = new CheckBox();
      cb2.AutoPostBack = true;
      cb2.ID = "CheckBoxInRowIndex-" + 
        e.Row.RowIndex.ToString();
      cb2.CheckedChanged += 
        new EventHandler(cb2_CheckedChanged);
      cell.Controls.Add(cb2);
      e.Row.Cells.Add(cell);
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      // フッタ行に列(セル)を追加。セルの中にリテ
      // ラルを配置。
      TableCell cell = new TableCell();
      cell.Controls.Add(new LiteralControl("追加列"));
      e.Row.Cells.Add(cell);
    }
  }

  protected void cb2_CheckedChanged(object sender, 
    EventArgs e)
  {
    CheckBox cb = (CheckBox)sender;
    Label2.Text = cb.ID + " clicked to " + 
      cb.Checked.ToString();
  }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>WebSurfer's Page - 実験室</title>
</head>
<body>
  <form id="form1" runat="server">

    <h2>DataGrid</h2>
    <asp:DataGrid ID="DataGrid1"
      runat="server" 
      ShowFooter="True" 
      OnItemCreated="DataGrid1_ItemCreated">
    </asp:DataGrid>
    <asp:Label ID="Label1" runat="server" />

    <h2>GridView</h2>
    <asp:GridView ID="GridView1" 
      runat="server" 
      ShowFooter="True" 
      OnRowCreated="GridView1_RowCreated">
    </asp:GridView>
    <asp:Label ID="Label2" runat="server" />

  </form>
</body>
</html>

Tags: ,

ASP.NET

GridView のヘッダ、列を固定(その 2)

by WebSurfer 4. February 2013 21:26

github というサイトの Grid というページに紹介されている JavaScript(jQuery ではない)と CSS を利用して、GridView のヘッダと列を固定する例の紹介です。

GridView に Grid.js, Grid.css を適用

先の記事「GridView のヘッダ、列を固定」で、CSS の Internet Explorer (IE) 独自実装である expression 関数を使ってテーブルのヘッダと列を固定する例を書きました。

しかしながら、expression 関数のサポートは終了していて IE でも互換モードでないと動きませんし、IE の独自実装なので、当然、Firefox など他のブラウザでは動きませんので、あまり使い道はありません。

2017/8/16 注記追加
Windows 10 IE11 では Quirks モード(IE5 相当)にしても expression 関数が働かないようです。いつそうなったのかは不明ですが、expression 関数を使ってテーブルのヘッダ・列を固定する方法は使用禁止にした方がよさそうです。

代わりに、上に紹介した Grid のページから入手できる Grid.js と Grid.css を利用して、GridView のヘッダと列を固定するサンプルを作ってみました。これなら IE7+, Firefox, Chrome, Safari, Opera コンパチなので一般的に使えると思います。

上の画像を表示したサンプルコードを以下に書いておきます。注意点は以下およびコード内のコメントに書いておきましたので読んでください。

  1. Grid.js は、table の DOM、json 文字列、xml 文字列のいずれかをソースとして、ヘッダや列を固定可能なテーブルを生成します。GridView の場合は、GridView が生成した table の DOM をソースとして使うことになります。
  2. GridView が生成した table の DOM は、Grid.js が生成した別テーブルに置き換えられます。つまり、上の画像に表示されているテーブルは GridView ではなく、Grid.js が生成した別物です。
  3. Grid.js が生成した別テーブルを操作してデータベースの更新を行うのは無理っぽいです。別テーブルは表示するだけにして、更新は別に DetailsView か FormView を表示して行うのがよさそうです。

実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ 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 を作成
  protected DataTable CreateDataTable()
  {
    DataTable dt = new DataTable();
    DataRow dr;

    dt.Columns.Add(new DataColumn("ID", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Name", typeof(string)));
    dt.Columns.Add(new DataColumn("Type", typeof(string)));
    dt.Columns.Add(new DataColumn("Price", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Qty", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Amount", typeof(Int32)));
    dt.Columns.Add(new DataColumn("CategoryID", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Note", typeof(string)));
    dt.Columns.Add(new DataColumn("Discontinued", typeof(bool)));
    dt.Columns.Add(new DataColumn("DateTime", typeof(DateTime)));

    for (int i = 0; i < 50; i++)
    {
      dr = dt.NewRow();
      dr["ID"] = i;
      dr["Name"] = "Product Name_" + i.ToString();
      dr["Type"] = "Product Type " + (100 - i).ToString();
      dr["Price"] = 123000 * (i + 1);
      dr["Qty"] = (i + 1) * 20;
      dr["Amount"] = 123000 * (i + 1) * (i + 1);
      dr["CategoryID"] = 100 - i;
      dr["Note"] = "Note_" + i.ToString();
      dr["Discontinued"] = (i%2 == 0)? true : false;
      dr["DateTime"] = DateTime.Now.AddDays(i);
      dt.Rows.Add(dr);
    }
    return dt;
  }

  // GridView に上記メソッドで作った DataTable をバインド
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack)
    {
      GridView1.DataSource = CreateDataTable();
      GridView1.DataBind();
    }
  }

  // ソースが DOM の場合、thead, tbody が必要。
  // GridView はデフォルトでは thead, tbody は生成
  // されないので、以下のコードを使って追加する。
  protected void GridView1_RowCreated(
    object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Header)
    {
      e.Row.TableSection =
        System.Web.UI.WebControls.TableRowSection.TableHeader;
    }
    else if (e.Row.RowType == DataControlRowType.DataRow)
    {
      e.Row.TableSection =
        System.Web.UI.WebControls.TableRowSection.TableBody;
    }
    // フッターがある場合(GridView.ShowFooter が true の場合)は
    // 以下のコードのコメントアウトを解除。
    //else if (e.Row.RowType == DataControlRowType.Footer)
    //{
    //  e.Row.TableSection =
    //    System.Web.UI.WebControls.TableRowSection.TableFooter;
    //}
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="/Scripts/Grid.js" type="text/javascript"></script>
  <link href="/css/Grid.css" rel="stylesheet" type="text/css" />
  <script type="text/javascript">
  //<![CDATA[
    window.onload = function () {
      // GridView は div 要素で囲った table を生成するの
      // で、その div 要素を利用する。div 要素に id を付
      // 与して初期サイズを指定する CSS を設定する。
      // div 要素の直下に table がないと、Grid.js による
      // 置き換えがうまくいかない(GridView の table が
      // 残ってしまう)ので注意。
      var tableGridView = 
        document.getElementById("<%=GridView1.ClientID%>");
      var parentElement = tableGridView.parentNode;
      parentElement.setAttribute("id", "myGrid");
      parentElement.setAttribute("class", "style1");

      // ソートするために各列のデータ型を指定。デフォルト
      // は string なので、数字などの列がある場合は指定し
      // ないとソート結果が期待通りにならない。
      var gridColSortTypes =
        ["number", "string", "string", "number", "number", 
        "number", "number", "string", "none", "date"];

      // GridView (table) の場合、srcType は "dom" に設定。
      // 下のコードの myGrid は table を囲う div 要素の id。
      // SrcData には table の id(GridView の ClientID)を
      // 設定。その他のパラメータ設定は Grid のページ参照。
      // Grid.js は、SrcData に指定された table の DOM を
      // ソースに使って別テーブルを生成し、元の table と置
      // き換える。
      new Grid("myGrid", {
        srcType: "dom",
        srcData: "<%=GridView1.ClientID%>",
        allowGridResize: true,
        allowColumnResize: true,
        allowClientSideSorting: true,
        allowSelections: true,
        allowMultipleSelections: true,
        showSelectionColumn: false,
        colSortTypes : gridColSortTypes,
        fixedCols: 1
      });
    };
  //]]>
  </script>

  <style type="text/css">
    /* テーブルの初期サイズの指定 */
    .style1
    {
      width: 400px;
      height: 360px;
    }    
  </style>
</head>
<body>
  <form id="form1" runat="server">
  <asp:GridView ID="GridView1" 
    runat="server"
    OnRowCreated="GridView1_RowCreated" 
    EnableViewState="False">
  </asp:GridView>
  </form>
</body>
</html>

Tags: ,

JavaScript

GridView と thead, tbody, tfoot

by WebSurfer 16. December 2012 20:13

GridView も html にレンダリングされると table, tr, th, td などの要素になりますが、thead, tbody, tfoot 要素はデフォルトではレンダリングされません。今回は GridView で thead, tbody, tfoot 要素を追加する方法を書きます。

GridView は、内部で Table コントロール を利用しているようです。

従って、Table コントロール内の TableRow オブジェクトの TableSection プロパティTableRowSection 列挙体 のTableHeader, TableBody, TableFooter のいずれかに設定してやれば thead, tbody, tfoot 要素が追加されます。

以下のコードのような感じです。

実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。html ソースを見れば thead, tbody, tfoot 要素が追加されているのが分かると思います。

<%@ 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 を作成
  protected DataTable CreateDataTable()
  {
    DataTable dt = new DataTable();
    DataRow dr;

    dt.Columns.Add(new DataColumn("ID", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Name", typeof(string)));
    dt.Columns.Add(new DataColumn("Price", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Qty", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Amount", typeof(Int32)));
    dt.Columns.Add(new DataColumn("Note", typeof(string)));

    for (int i = 0; i < 10; i++)
    {
      dr = dt.NewRow();
      dr["ID"] = i;
      dr["Name"] = "Name_" + i.ToString();
      dr["Price"] = 123000 * (i + 1);
      dr["Qty"] = (i + 1) * 20;
      dr["Amount"] = 123000 * (i + 1) * (i + 1);
      dr["Note"] = "Note_" + i.ToString();
      dt.Rows.Add(dr);
    }
    return dt;
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    if (!IsPostBack)
    {
      GridView1.DataSource = CreateDataTable();
      GridView1.DataBind();
    }
  }

  protected void GridView1_RowCreated(
    object sender, GridViewRowEventArgs e)
  {
    if (e.Row.RowType == DataControlRowType.Header)
    {
      e.Row.TableSection = 
        System.Web.UI.WebControls.TableRowSection.TableHeader;
    }
    else if (e.Row.RowType == DataControlRowType.DataRow)
    {
      e.Row.TableSection = 
        System.Web.UI.WebControls.TableRowSection.TableBody;
    }
    else if (e.Row.RowType == DataControlRowType.Footer)
    {
      e.Row.TableSection = 
        System.Web.UI.WebControls.TableRowSection.TableFooter;
    }
  }
</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" 
      OnRowCreated="GridView1_RowCreated" 
      ShowFooter="True" 
      EnableViewState="False">
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags: , , ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  May 2026  >>
MoTuWeThFrSaSu
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

View posts in large calendar