GitHub项目地址:https://github.com/mingceng/merge-gridviewcell
使用GridView展示数据,经常会遇到合并单元格的情况。首先说明一下项目中合并单元格的要求,如下图所示,左边是合并之前的GridView,右边是合并之后的GridView。从图中可以看到GridView一共有“等级”、“颜色”,“箱子”,“净重”,“规格”,“汇总”6列,首先要要合并等级,如右图中合并后的“一级”,然后再合并每个等级下面的颜色,如右图中合并后的“一级”下面的“片红”和“条红”,依次类推。
从网上搜索了一下“GridView合并单元格”,大多是合并单列,诸如此类。搜索未果,只能自己动手写一个了。参考现有的合并单列的代码,我完成了满足上面合并要求的代码,自我感觉算法很烂,写这篇文章也是希望有经验的园友提供更好的解决方法。
首先,完成合并单列的行,代码如下:
1: /// <summary>
2: /// 合并单列的行
3: /// </summary>
4: /// <param name="gv">GridView</param>
5: /// <param name="currentCol">当前列</param>
6: /// <param name="startRow">开始合并的行索引</param>
7: /// <param name="endRow">结束合并的行索引</param>
8: private static void MergeRow(GridView gv, int currentCol, int startRow, int endRow)
9: {
10: for (int rowIndex = endRow; rowIndex >= startRow; rowIndex--)
11: {
12: GridViewRow currentRow = gv.Rows[rowIndex];
13: GridViewRow prevRow = gv.Rows[rowIndex + 1];
14: if (currentRow.Cells[currentCol].Text != "" && currentRow.Cells[currentCol].Text != " ")
15: {
16: if (currentRow.Cells[currentCol].Text == prevRow.Cells[currentCol].Text)
17: {
18: currentRow.Cells[currentCol].RowSpan = prevRow.Cells[currentCol].RowSpan < 1 ? 2 : prevRow.Cells[currentCol].RowSpan + 1;
19: prevRow.Cells[currentCol].Visible = false;
20: }
21: }
22: }
23: }
在合并后面的列之前,首先要遍历前一列。上面的例子中,合并第二列,首先要遍历第一列,以便得到一级是从第几行到第几行。开始写遍历前一列的代码之前,首先定义一个类,如下所示:
1: class RowArg
2: {
3: public int StartRowIndex { get; set; }
4: public int EndRowIndex { get; set; }
5: }
该类有两个属性,分别表示要合并的开始行的索引和结束行的索引。下面是遍历前一列的代码:
1: /// <summary>
2: /// 遍历前一列
3: /// </summary>
4: /// <param name="gv">GridView</param>
5: /// <param name="prevCol">当前列的前一列</param>
6: /// <param name="list"></param>
7: private static void TraversesPrevCol(GridView gv, int prevCol, List<RowArg> list)
8: {
9: if (list == null)
10: {
11: list = new List<RowArg>();
12: }
13: RowArg ra = null;
14: for (int i = 0; i < gv.Rows.Count; i++)
15: {
16: if (!gv.Rows[i].Cells[prevCol].Visible)
17: {
18: continue;
19: }
20: ra = new RowArg();
21: ra.StartRowIndex = gv.Rows[i].RowIndex;
22: ra.EndRowIndex = ra.StartRowIndex + gv.Rows[i].Cells[prevCol].RowSpan - 2;
23: list.Add(ra);
24: }
25: }
下面完成最后一个方法,代码如下:
1: /// <summary>
2: /// GridView合并行,
3: /// </summary>
4: /// <param name="gv">GridView</param>
5: /// <param name="startCol">开始列</param>
6: /// <param name="endCol">结束列</param>
7: public static void MergeRow(GridView gv, int startCol, int endCol)
8: {
9: RowArg init = new RowArg()
10: {
11: StartRowIndex = 0,
12: EndRowIndex = gv.Rows.Count - 2
13: };
14: for (int i = startCol; i < endCol + 1; i++)
15: {
16: if (i > 0)
17: {
18: List<RowArg> list = new List<RowArg>();
19: //从第二列开始就要遍历前一列
20: TraversesPrevCol(gv, i - 1, list);
21: foreach (var item in list)
22: {
23: MergeRow(gv, i, item.StartRowIndex, item.EndRowIndex);
24: }
25: }
26: //合并开始列的行
27: else
28: {
29: MergeRow(gv, i, init.StartRowIndex, init.EndRowIndex);
30: }
31: }
32: }
这个方法是最后在程序中调用的方法。
最后写个简单的例子:
页面代码:
1: <asp:GridView ID="Gridview1" runat="server" AutoGenerateColumns="false"
2: onrowdatabound="Gridview1_RowDataBound">
3: <Columns>
4: <asp:BoundField DataField="Name" HeaderText="姓名" />
5: <asp:BoundField DataField="Item" HeaderText="工资项" />
6: <asp:BoundField DataField="SubItem" HeaderText="工资子项" />
7: <asp:BoundField DataField="Month" HeaderText="月份" />
8: <asp:BoundField DataField="Money" HeaderText="钱数" />
9: </Columns>
10: </asp:GridView>
后台代码:
class WebForm1 : System.Web.UI.Page
2: {
object sender, EventArgs e)
4: {
if (!Page.IsPostBack)
6: {
#region 模拟数据
new List<Salary>();
new Salary()
10: {
,
,
,
,
16: });
new Salary()
18: {
,
,
,
,
24: });
new Salary()
26: {
,
,
,
,
32: });
new Salary()
34: {
,
,
,
,
40: });
new Salary()
42: {
,
,
,
,
48: });
new Salary()
50: {
,
,
,
,
56: });
new Salary()
58: {
,
,
,
,
64: });
new Salary()
66: {
,
,
,
,
72: });
new Salary()
74: {
,
,
,
,
80: });
new Salary()
82: {
,
,
,
,
88: });
new Salary()
90: {
,
,
,
,
96: });
new Salary()
98: {
,
,
,
,
104: });
new Salary()
106: {
,
,
,
,
112: });
new Salary()
114: {
,
,
,
,
120: });
new Salary()
122: {
,
,
,
,
128: });
new Salary()
130: {
,
,
,
,
136: });
new Salary()
138: {
,
,
,
,
144: });
new Salary()
146: {
,
,
,
,
152: });
//----------------------------------------
new Salary()
155: {
,
,
,
,
161: });
new Salary()
163: {
,
,
,
,
169: });
new Salary()
171: {
,
,
,
,
177: });
new Salary()
179: {
,
,
,
,
185: });
new Salary()
187: {
,
,
,
,
193: });
new Salary()
195: {
,
,
,
,
201: });
new Salary()
203: {
,
,
,
,
209: });
new Salary()
211: {
,
,
,
,
217: });
new Salary()
219: {
,
,
,
,
225: });
new Salary()
227: {
,
,
,
,
233: });
new Salary()
235: {
,
,
,
,
241: });
new Salary()
243: {
,
,
,
,
249: });
new Salary()
251: {
,
,
,
,
257: });
new Salary()
259: {
,
,
,
,
265: });
new Salary()
267: {
,
,
,
,
273: });
new Salary()
275: {
,
,
,
,
281: });
new Salary()
283: {
,
,
,
,
289: });
new Salary()
291: {
,
,
,
,
297: });
//-------------------------------------------
#endregion
300: Gridview1.DataSource = salaryList;
301: Gridview1.DataBind();
302: MergeGridViewCell.MergeRow(Gridview1, 0, 3);
303: }
304: }
305:
object sender, GridViewRowEventArgs e)
307: {
if (e.Row.RowType == DataControlRowType.Header)
309: {
310: e.Row.BackColor = Color.FromArgb(135, 206, 250);
311: }
if (e.Row.RowType == DataControlRowType.DataRow)
313: {
)
315: {
316: e.Row.BackColor = Color.FromArgb(176, 226, 255);
317: e.Row.Cells[0].ColumnSpan = 4;
;
false;
320: }
321: }
322: }
323: }
324:
class Salary
326: {
/// <summary>
/// 姓名
/// </summary>
string Name { get; set; }
/// <summary>
/// 工资项
/// </summary>
string Item { get; set; }
/// <summary>
/// 工资子项
/// </summary>
string SubItem { get; set; }
/// <summary>
/// 月份
/// </summary>
string Month { get; set; }
/// <summary>
/// 钱数
/// </summary>
string Money { get; set; }
347: }
GridView运行前后比较:
GitHub项目地址:https://github.com/mingceng/merge-gridviewcell