by WebSurfer
17. May 2013 16:41
あまり使い道はないと思いますが、DataGrid と GridView に動的に列を追加する方法を備忘録として書いておきます。
(【2021/9/21 追記】もっと簡単かつスマートにできる方法がありました。詳しくは「DataGrid, GridView に動的に列を追加 (2)」を見てください)
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>