id="article_content">
在 ASPX 中,我们通过 GroupField 列来定义多表头,如下所示:
'>
这是一个树状的结构,通过 GroupField 的 Columns 集合来定义子列,从而实现多表头的效果:
老方法已经不再奏效
如果照搬之前的逻辑,我们和容易写出如下的导出代码(处理数组很简单,循环搞定):
protected void Button1_Click(object sender, EventArgs e)
{
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment; filename=myexcel.xls");
Response.ContentType = "application/excel";
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.Write(GetGridTableHtml(Grid1));
Response.End();
}
private string GetGridTableHtml(Grid grid)
{
StringBuilder sb = new StringBuilder();
sb.Append("");
sb.Append("
");
sb.Append("");
foreach (GridColumn column in grid.Columns)
{
sb.AppendFormat("{0} ", column.HeaderText);
}
sb.Append(" ");
foreach (GridRow row in grid.Rows)
{
sb.Append("");
foreach (GridColumn column in grid.Columns)
{
string html = row.Values[column.ColumnIndex].ToString();
if (column.ColumnID == "tfNumber")
{
html = (row.FindControl("spanNumber") as System.Web.UI.HtmlControls.HtmlGenericControl).InnerText;
}
else if (column.ColumnID == "tfGender")
{
html = (row.FindControl("labGender") as AspNet.Label).Text;
}
sb.AppendFormat("{0} ", html);
}
sb.Append(" ");
}
sb.Append("
");
return sb.ToString();
}
打开导出的文件,我们会发现所有子列都不见了:
这样很容易理解,因为在后台,FineUI 也是按照树状的结构存储 Grid1.Columns 属性的:
[{
"text": "分组一",
"columns": [{
"text": "姓名"
}, {
"text": "性别"
}, {
"text": "考试成绩",
"columns": [{
"text": "语文成绩"
}, {
"text": "数学成绩"
}, {
"text": "总成绩"
}]
}, ]
}, {
"text": "所学专业"
}, {
"text": "注册日期"
}]
树状结构转换为 table 标签
这个还真不好办,因为 table 标签不像它看起来那么简单,每个单元格都可能要设置 rowspan 和 colspan,来看下我们最终需要的结构:
最终生成的 table 标签如下所示:
分组一
所学专业
注册日期
姓名
性别
考试成绩
语文成绩
数学成绩
总成绩
最终生成了 3 行数据,每一行中 th 的个数不尽相同,每个 th 的参数也不相同,看来这个转换要自己手工做了。
实现转换之前,我们先来总结两个关键的逻辑:
1. 如果某列有子列,则更改本列的 colspan,并且增加所有父列(向上追溯)的 colspan
2. 如果下一行有数据,则增加上一行(向上追溯)中没有子项的列的 rowspan
每个 th 在 C# 代码中通过 object[] 来表达,比如 [考试成绩] 这一列最终的结构是:
[
1, // rowspan
3, // colspan
考试成绩, // 当前列对象
分组一 // 父列对象
]
逻辑点到为止,剩下的就来看代码了,我们把逻辑封装到自定义类中:
public class MultiHeaderTable
{
// 包含 rowspan,colspan 的多表头,方便生成 HTML 的 table 标签
public List> MultiTable = new List>();
// 最终渲染的列数组
public List Columns = new List();
public void ResolveMultiHeaderTable(GridColumnCollection columns)
{
List row = new List();
foreach (GridColumn column in columns)
{
object[] cell = new object[4];
cell[0] = 1; // rowspan
cell[1] = 1; // colspan
cell[2] = column;
cell[3] = null;
row.Add(cell);
}
ResolveMultiTable(row, 0);
ResolveColumns(row);
}
private void ResolveColumns(List row)
{
foreach (object[] cell in row)
{
GroupField groupField = cell[2] as GroupField;
if (groupField != null && groupField.Columns.Count > 0)
{
List subrow = new List();
foreach (GridColumn column in groupField.Columns)
{
subrow.Add(new object[]
{
1,
1,
column,
groupField
});
}
ResolveColumns(subrow);
}
else
{
Columns.Add(cell[2] as GridColumn);
}
}
}
private void ResolveMultiTable(List row, int level)
{
List nextrow = new List();
foreach (object[] cell in row)
{
GroupField groupField = cell[2] as GroupField;
if (groupField != null && groupField.Columns.Count > 0)
{
// 如果当前列包含子列,则更改当前列的 colspan,以及增加父列(向上递归)的colspan
cell[1] = Convert.ToInt32(groupField.Columns.Count);
PlusColspan(level - 1, cell[3] as GridColumn,groupField.Columns.Count - 1);
foreach (GridColumn column in groupField.Columns)
{
nextrow.Add(new object[]
{
1,
1,
column,
groupField
});
}
}
}
MultiTable.Add(row);
// 如果当前下一行,则增加上一行(向上递归)中没有子列的列的 rowspan
if (nextrow.Count > 0)
{
PlusRowspan(level);
ResolveMultiTable(nextrow, level + 1);
}
}
private void PlusRowspan(int level)
{
if (level 0)
{
// ...
}
else
{
cells[0] = Convert.ToInt32(cells[0]) + 1;
}
}
PlusRowspan(level - 1);
}
private void PlusColspan(int level, GridColumn parent, int plusCount)
{
if (level
其实主要的逻辑就上面提到的两点,然后需要好几个递归函数来一块完成任务。
导出的代码调用如下:
protected void Button1_Click(object sender, EventArgs e)
{
Response.ClearContent();
Response.AddHeader("content-disposition", "attachment; filename=myexcel.xls");
Response.ContentType = "application/excel";
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.Write(GetGridTableHtml(Grid1));
Response.End();
}
private string GetGridTableHtml(Grid grid)
{
StringBuilder sb = new StringBuilder();
MultiHeaderTable mht = new MultiHeaderTable();
mht.ResolveMultiHeaderTable(Grid1.Columns);
sb.Append(" ");
sb.Append("
");
foreach (List rows in mht.MultiTable)
{
sb.Append("");
foreach (object[] cell in rows)
{
int rowspan = Convert.ToInt32(cell[0]);
int colspan = Convert.ToInt32(cell[1]);
GridColumn column = cell[2] as GridColumn;
sb.AppendFormat("{3} ",
rowspan != 1 ? " rowspan="" + rowspan + """ : "",
colspan != 1 ? " colspan="" + colspan + """ : "",
colspan != 1 ? " style="text-align:center;"" : "",
column.HeaderText);
}
sb.Append(" ");
}
foreach (GridRow row in grid.Rows)
{
sb.Append("");
foreach (GridColumn column in mht.Columns)
{
string html = row.Values[column.ColumnIndex].ToString();
if (column.ColumnID == "tfNumber")
{
html = (row.FindControl("spanNumber") as System.Web.UI.HtmlControls.HtmlGenericControl).InnerText;
}
else if (column.ColumnID == "tfGender")
{
html = (row.FindControl("labGender") as AspNet.Label).Text;
}
sb.AppendFormat("{0} ", html);
}
sb.Append(" ");
}
sb.Append("
");
return sb.ToString();
}
最终导出的文件结构如下所示:
原创不易,请点赞
短短一篇文章,几行代码,看似轻描淡写,实则是花了很大功夫调试。你觉得作者在整个过程中做了多少次导出文件的动作?才最终实现了这个效果!
。
。
10?
。
。
。
20?
。
。
。
。
30?
。
。
。
。
。
。
40?
。
。
。
。
。
。
。
请恕作者愚钝,足足不下 50 次:
本章小结
本篇文章介绍了如何导出多表头表格,重点在于树状结构到 table 标签结构的转换,虽然实现稍微复杂了点,但只要思路清晰,最终还是能否完整呈现的。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/644/viewspace-2800928/
,如需转载,请注明出处,否则将追究法律责任。