动态列的GridView嵌套解决方法整理
最近使用GridView做报表时遇到一个问题,在老大帮助下完成。现在记录一下。
项目需求类似如下的嵌套:
(例如一个借还系统,父级是借设备人的资料,子级是具体设备资料)
-------------------------
父编号 姓名 年龄
1 张三 20
----------------
子编号 产品类型 产品名称
1 路由器 凌凯
----------------
2 李四 24
--------------------------
1.在一级数据的列数固定的情况下可直接使用如下嵌套:
基本思路是在行绑定事件中绑定嵌套的GridView2,这里如果GrideView2是动态列也可以,后台也能动态添加进去。
前台:
代码
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false"
onrowdatabound="GridView1_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="编号">
<ItemTemplate><%#Eval("id") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="姓名">
<ItemTemplate><%#Eval("name") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="年龄" >
<ItemTemplate><%#Eval("age") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
onrowdatabound="GridView1_RowDataBound">
<Columns>
<asp:TemplateField HeaderText="编号">
<ItemTemplate><%#Eval("id") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="姓名">
<ItemTemplate><%#Eval("name") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="年龄" >
<ItemTemplate><%#Eval("age") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<tr colspan="3">
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateField HeaderText="编号">
<ItemTemplate><%#Eval("id") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="类型">
<ItemTemplate><%#Eval("type") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="名称">
<ItemTemplate><%#Eval("name") %></ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:GridView ID="GridView2" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateField HeaderText="编号">
<ItemTemplate><%#Eval("id") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="类型">
<ItemTemplate><%#Eval("type") %></ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="名称">
<ItemTemplate><%#Eval("name") %></ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</tr>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
后台:
代码
protected void Page_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("name");
dt.Columns.Add("age");
for (int i = 0; i < 3;i++ )
{
dt.Rows.Add(i.ToString(),"用户"+i,20+i);
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
this.GridView1.DataSource = ds;
this.GridView1.DataBind();
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
GridView gv = e.Row.FindControl("GridView2") as GridView;
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("name");
dt.Columns.Add("type");
for (int i = 0; i < 3; i++)
{
dt.Rows.Add(i.ToString(), "产品" + i, "产品" + i);
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
gv.DataSource = ds;
gv.DataBind();
}
}
{
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("name");
dt.Columns.Add("age");
for (int i = 0; i < 3;i++ )
{
dt.Rows.Add(i.ToString(),"用户"+i,20+i);
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
this.GridView1.DataSource = ds;
this.GridView1.DataBind();
}
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
GridView gv = e.Row.FindControl("GridView2") as GridView;
DataTable dt = new DataTable();
dt.Columns.Add("id");
dt.Columns.Add("name");
dt.Columns.Add("type");
for (int i = 0; i < 3; i++)
{
dt.Rows.Add(i.ToString(), "产品" + i, "产品" + i);
}
DataSet ds = new DataSet();
ds.Tables.Add(dt);
gv.DataSource = ds;
gv.DataBind();
}
}
2,一级数据的行中含有动态列
开始考虑是动态的加列就可以了,我们在Page_Load事件中增加下面代码:
代码
for (int i = 0; i < 2;i++ )//向模板列中插入几列
{
BoundField bf = new BoundField();
bf.HeaderText = "测试"+i;
bf.DataField = "name";
this.GridView1.Columns.Insert(1,bf);
}
{
BoundField bf = new BoundField();
bf.HeaderText = "测试"+i;
bf.DataField = "name";
this.GridView1.Columns.Insert(1,bf);
}
此时动态列增加上了,但是分页时在行绑定事件中已经找不到嵌套的GridView2了,原因我现在还不知道,请知道的朋友告知一下。
编号 | 测试1 | 测试0 | 姓名 | 年龄 | |
---|---|---|---|---|---|
0 | 用户0 | 用户0 | 用户0 | 20 | |
编号 | 类型 | 名称 |
---|---|---|
0 | 产品0 | 产品0 |
1 | 产品1 | 产品1 |
2 | 产品2 | 产品2 |
编号 | 类型 | 名称 |
---|---|---|
0 | 产品0 | 产品0 |
1 | 产品1 | 产品1 |
2 | 产品2 | 产品2 |
于是,到这里就想到自己写一个模板类了,控制更好一些,在写的过程中老大给了帮助,模板如下
代码
#region MyTemplate
/// <summary>
///MyTemplate 的摘要说明
/// </summary>
public class MyTemplate : ITemplate
{
private DataControlRowType dataControlRowType;
private string dataField;
public string DataField
{
get { return dataField; }
set { dataField = value; }
}
private string columnName;
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
private string controlID;
public string ControlID
{
get { return controlID; }
set { controlID = value; }
}
private bool isGrid;
public bool IsGrid
{
get { return isGrid; }
set { isGrid = value; }
}
public MyTemplate(string columnName)
{
dataControlRowType = DataControlRowType.Header;
this.columnName = columnName;
}
public MyTemplate(string controlID, string dataField, bool isGrid)
{
dataControlRowType = DataControlRowType.DataRow;
this.dataField = dataField;
this.controlID = controlID;
this.isGrid = isGrid;
}
#region ITemplate 成员
JXSoft.TicketManage.BLL.TicketTypeBLL ticketTypeBLL = new JXSoft.TicketManage.BLL.TicketTypeBLL();
void ITemplate.InstantiateIn(Control container)
{
switch (dataControlRowType)
{
case DataControlRowType.Header:
Literal l = new Literal();
l.Text = ColumnName;
container.Controls.Add(l);
break;
case DataControlRowType.DataRow:
if (!isGrid)
{
Label lbl = new Label();
lbl.ID = ControlID;
lbl.DataBinding += new EventHandler(lbl_DataBinding);
container.Controls.Add(lbl);
}
else
{
Label lbl = new Label();
lbl.ID = ControlID;
lbl.DataBinding += new EventHandler(lbl_DataBinding);
container.Controls.Add(lbl);
#region 添加GridView 及内部绑定列
GridView gv = new GridView();
gv.ID = "GridView2";
gv.AutoGenerateColumns = false;
BoundField bf;
bf = new BoundField();
bf.HeaderText = "编号";
bf.DataField = "id";
gv.Columns.Add(bf);
bf = new BoundField();
bf.HeaderText = "类型";
bf.DataField = "type";
gv.Columns.Add(bf);
bf = new BoundField();
bf.HeaderText = "名称";
bf.DataField = "name";
gv.Columns.Add(bf);
TableRow trow = new TableRow();
TableCell cell = new TableCell();
cell.ColumnSpan = 3;
cell.Controls.Add(gv);
trow.Cells.Add(cell);
container.Controls.Add(trow);
#endregion
}
break;
default:
break;
}
}
void lbl_DataBinding(object sender, EventArgs e)
{
Label lbl = sender as Label;
GridViewRow gr = lbl.NamingContainer as GridViewRow;
lbl.Text = DataBinder.Eval(gr.DataItem, DataField).ToString();
}
#endregion
}
#endregion
/// <summary>
///MyTemplate 的摘要说明
/// </summary>
public class MyTemplate : ITemplate
{
private DataControlRowType dataControlRowType;
private string dataField;
public string DataField
{
get { return dataField; }
set { dataField = value; }
}
private string columnName;
public string ColumnName
{
get { return columnName; }
set { columnName = value; }
}
private string controlID;
public string ControlID
{
get { return controlID; }
set { controlID = value; }
}
private bool isGrid;
public bool IsGrid
{
get { return isGrid; }
set { isGrid = value; }
}
public MyTemplate(string columnName)
{
dataControlRowType = DataControlRowType.Header;
this.columnName = columnName;
}
public MyTemplate(string controlID, string dataField, bool isGrid)
{
dataControlRowType = DataControlRowType.DataRow;
this.dataField = dataField;
this.controlID = controlID;
this.isGrid = isGrid;
}
#region ITemplate 成员
JXSoft.TicketManage.BLL.TicketTypeBLL ticketTypeBLL = new JXSoft.TicketManage.BLL.TicketTypeBLL();
void ITemplate.InstantiateIn(Control container)
{
switch (dataControlRowType)
{
case DataControlRowType.Header:
Literal l = new Literal();
l.Text = ColumnName;
container.Controls.Add(l);
break;
case DataControlRowType.DataRow:
if (!isGrid)
{
Label lbl = new Label();
lbl.ID = ControlID;
lbl.DataBinding += new EventHandler(lbl_DataBinding);
container.Controls.Add(lbl);
}
else
{
Label lbl = new Label();
lbl.ID = ControlID;
lbl.DataBinding += new EventHandler(lbl_DataBinding);
container.Controls.Add(lbl);
#region 添加GridView 及内部绑定列
GridView gv = new GridView();
gv.ID = "GridView2";
gv.AutoGenerateColumns = false;
BoundField bf;
bf = new BoundField();
bf.HeaderText = "编号";
bf.DataField = "id";
gv.Columns.Add(bf);
bf = new BoundField();
bf.HeaderText = "类型";
bf.DataField = "type";
gv.Columns.Add(bf);
bf = new BoundField();
bf.HeaderText = "名称";
bf.DataField = "name";
gv.Columns.Add(bf);
TableRow trow = new TableRow();
TableCell cell = new TableCell();
cell.ColumnSpan = 3;
cell.Controls.Add(gv);
trow.Cells.Add(cell);
container.Controls.Add(trow);
#endregion
}
break;
default:
break;
}
}
void lbl_DataBinding(object sender, EventArgs e)
{
Label lbl = sender as Label;
GridViewRow gr = lbl.NamingContainer as GridViewRow;
lbl.Text = DataBinder.Eval(gr.DataItem, DataField).ToString();
}
#endregion
}
#endregion
也就是说我们可以在后台将所有的模板列动态的添加进GridView1
代码
TemplateField tf;
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("编号");
tf.ItemTemplate = new MyTemplate("lblId", "id", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("姓名");
tf.ItemTemplate = new MyTemplate("lblName", "name", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("年龄");
tf.ItemTemplate = new MyTemplate("lblAge", "age", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("");
tf.ItemTemplate = new MyTemplate("GridView1", "age", true);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("编号");
tf.ItemTemplate = new MyTemplate("lblId", "id", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("姓名");
tf.ItemTemplate = new MyTemplate("lblName", "name", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("年龄");
tf.ItemTemplate = new MyTemplate("lblAge", "age", false);
GridView1.Columns.Add(tf);
tf = new TemplateField();
tf.HeaderTemplate = new MyTemplate("");
tf.ItemTemplate = new MyTemplate("GridView1", "age", true);
GridView1.Columns.Add(tf);
接下来就可以在行绑定事件中找到GridView2并绑定数据了。分页也没问题。
当然也可以顺利导出。导出需要加以下代码:
代码
protected void btnExport_Click(object sender, EventArgs e)
{
this.GridView1.AllowPaging = false;
BindDetailReportToGv();
Response.Clear();
Response.AddHeader("content-disposition", "attachment;filename=DetailsLog.xls");
Response.ContentType = "application/vnd.xls";
System.IO.StringWriter sw = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(sw);
this.GridView1.RenderControl(htmlWrite);
Response.Write(sw.ToString());
Response.End();
this.GridView1.AllowPaging = true;
}
public override void VerifyRenderingInServerForm(Control control)
{
// Confirms that an HtmlForm control is rendered for
}
{
this.GridView1.AllowPaging = false;
BindDetailReportToGv();
Response.Clear();
Response.AddHeader("content-disposition", "attachment;filename=DetailsLog.xls");
Response.ContentType = "application/vnd.xls";
System.IO.StringWriter sw = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(sw);
this.GridView1.RenderControl(htmlWrite);
Response.Write(sw.ToString());
Response.End();
this.GridView1.AllowPaging = true;
}
public override void VerifyRenderingInServerForm(Control control)
{
// Confirms that an HtmlForm control is rendered for
}
其实解决这个问题应该还有更好的办法,关键在于2点:
第一个问题是如果一级数据中有动态列,那么分页时的行绑定事件内找不到行里的GridView2,
第二个问题是导出数据时由于采用colspan=“100%”,页面看上去是嵌套了,但是导出为excel后布局就会乱了。
您有更好的办法吗,欢迎交流。