GridView多行多列合并单元格(完整代码和例子)
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>
后台代码:
1: public partial class WebForm1 : System.Web.UI.Page
2: {
3: protected void Page_Load(object sender, EventArgs e)
4: {
5: if (!Page.IsPostBack)
6: {
7: #region 模拟数据
8: List<Salary> salaryList = new List<Salary>();
9: salaryList.Add(new Salary()
10: {
11: Name = "张三",
12: Item = "应发工资",
13: SubItem = "基本工资",
14: Month = "1月",
15: Money = "3000"
16: });
17: salaryList.Add(new Salary()
18: {
19: Name = "张三",
20: Item = "应发工资",
21: SubItem = "奖金",
22: Month = "1月",
23: Money = "500"
24: });
25: salaryList.Add(new Salary()
26: {
27: Name = "张三",
28: Item = "应发工资",
29: SubItem = "奖金",
30: Month = "1月",
31: Money = "130"
32: });
33: salaryList.Add(new Salary()
34: {
35: Name = "张三",
36: Item = "应发工资",
37: SubItem = "奖金",
38: Month = "1月",
39: Money = "150"
40: });
41: salaryList.Add(new Salary()
42: {
43: Name = "张三",
44: Item = "应发工资",
45: SubItem = "加班",
46: Month = "1月",
47: Money = "100"
48: });
49: salaryList.Add(new Salary()
50: {
51: Name = "张三",
52: Item = "应发工资",
53: SubItem = "加班",
54: Month = "1月",
55: Money = "100"
56: });
57: salaryList.Add(new Salary()
58: {
59: Name = "张三",
60: Item = "五险一金",
61: SubItem = "医疗保险",
62: Month = "1月",
63: Money = "500"
64: });
65: salaryList.Add(new Salary()
66: {
67: Name = "张三",
68: Item = "五险一金",
69: SubItem = "住房公积金",
70: Month = "1月",
71: Money = "370"
72: });
73: salaryList.Add(new Salary()
74: {
75: Name = "",
76: Item = "",
77: SubItem = "",
78: Month = "合计",
79: Money = "3500"
80: });
81: salaryList.Add(new Salary()
82: {
83: Name = "张三",
84: Item = "应发工资",
85: SubItem = "基本工资",
86: Month = "2月",
87: Money = "3000"
88: });
89: salaryList.Add(new Salary()
90: {
91: Name = "张三",
92: Item = "应发工资",
93: SubItem = "奖金",
94: Month = "2月",
95: Money = "400"
96: });
97: salaryList.Add(new Salary()
98: {
99: Name = "张三",
100: Item = "应发工资",
101: SubItem = "奖金",
102: Month = "2月",
103: Money = "100"
104: });
105: salaryList.Add(new Salary()
106: {
107: Name = "张三",
108: Item = "应发工资",
109: SubItem = "加班",
110: Month = "2月",
111: Money = "100"
112: });
113: salaryList.Add(new Salary()
114: {
115: Name = "张三",
116: Item = "应发工资",
117: SubItem = "加班",
118: Month = "2月",
119: Money = "100"
120: });
121: salaryList.Add(new Salary()
122: {
123: Name = "张三",
124: Item = "应发工资",
125: SubItem = "加班",
126: Month = "2月",
127: Money = "100"
128: });
129: salaryList.Add(new Salary()
130: {
131: Name = "张三",
132: Item = "五险一金",
133: SubItem = "医疗保险",
134: Month = "2月",
135: Money = "500"
136: });
137: salaryList.Add(new Salary()
138: {
139: Name = "张三",
140: Item = "五险一金",
141: SubItem = "住房公积金",
142: Month = "2月",
143: Money = "370"
144: });
145: salaryList.Add(new Salary()
146: {
147: Name = "",
148: Item = "",
149: SubItem = "",
150: Month = "合计",
151: Money = "3900"
152: });
153: //----------------------------------------
154: salaryList.Add(new Salary()
155: {
156: Name = "李四",
157: Item = "应发工资",
158: SubItem = "基本工资",
159: Month = "1月",
160: Money = "3000"
161: });
162: salaryList.Add(new Salary()
163: {
164: Name = "李四",
165: Item = "应发工资",
166: SubItem = "奖金",
167: Month = "1月",
168: Money = "500"
169: });
170: salaryList.Add(new Salary()
171: {
172: Name = "李四",
173: Item = "应发工资",
174: SubItem = "奖金",
175: Month = "1月",
176: Money = "130"
177: });
178: salaryList.Add(new Salary()
179: {
180: Name = "李四",
181: Item = "应发工资",
182: SubItem = "奖金",
183: Month = "1月",
184: Money = "150"
185: });
186: salaryList.Add(new Salary()
187: {
188: Name = "李四",
189: Item = "应发工资",
190: SubItem = "加班",
191: Month = "1月",
192: Money = "100"
193: });
194: salaryList.Add(new Salary()
195: {
196: Name = "李四",
197: Item = "应发工资",
198: SubItem = "加班",
199: Month = "1月",
200: Money = "100"
201: });
202: salaryList.Add(new Salary()
203: {
204: Name = "李四",
205: Item = "五险一金",
206: SubItem = "医疗保险",
207: Month = "1月",
208: Money = "500"
209: });
210: salaryList.Add(new Salary()
211: {
212: Name = "李四",
213: Item = "五险一金",
214: SubItem = "住房公积金",
215: Month = "1月",
216: Money = "370"
217: });
218: salaryList.Add(new Salary()
219: {
220: Name = "",
221: Item = "",
222: SubItem = "",
223: Month = "合计",
224: Money = "3500"
225: });
226: salaryList.Add(new Salary()
227: {
228: Name = "李四",
229: Item = "应发工资",
230: SubItem = "基本工资",
231: Month = "2月",
232: Money = "3000"
233: });
234: salaryList.Add(new Salary()
235: {
236: Name = "李四",
237: Item = "应发工资",
238: SubItem = "奖金",
239: Month = "2月",
240: Money = "400"
241: });
242: salaryList.Add(new Salary()
243: {
244: Name = "李四",
245: Item = "应发工资",
246: SubItem = "奖金",
247: Month = "2月",
248: Money = "100"
249: });
250: salaryList.Add(new Salary()
251: {
252: Name = "李四",
253: Item = "应发工资",
254: SubItem = "加班",
255: Month = "2月",
256: Money = "100"
257: });
258: salaryList.Add(new Salary()
259: {
260: Name = "李四",
261: Item = "应发工资",
262: SubItem = "加班",
263: Month = "2月",
264: Money = "100"
265: });
266: salaryList.Add(new Salary()
267: {
268: Name = "李四",
269: Item = "应发工资",
270: SubItem = "加班",
271: Month = "2月",
272: Money = "100"
273: });
274: salaryList.Add(new Salary()
275: {
276: Name = "李四",
277: Item = "五险一金",
278: SubItem = "医疗保险",
279: Month = "2月",
280: Money = "500"
281: });
282: salaryList.Add(new Salary()
283: {
284: Name = "李四",
285: Item = "五险一金",
286: SubItem = "住房公积金",
287: Month = "2月",
288: Money = "370"
289: });
290: salaryList.Add(new Salary()
291: {
292: Name = "",
293: Item = "",
294: SubItem = "",
295: Month = "合计",
296: Money = "3900"
297: });
298: //-------------------------------------------
299: #endregion
300: Gridview1.DataSource = salaryList;
301: Gridview1.DataBind();
302: MergeGridViewCell.MergeRow(Gridview1, 0, 3);
303: }
304: }
305:
306: protected void Gridview1_RowDataBound(object sender, GridViewRowEventArgs e)
307: {
308: if (e.Row.RowType == DataControlRowType.Header)
309: {
310: e.Row.BackColor = Color.FromArgb(135, 206, 250);
311: }
312: if (e.Row.RowType == DataControlRowType.DataRow)
313: {
314: if (e.Row.Cells[3].Text == "合计")
315: {
316: e.Row.BackColor = Color.FromArgb(176, 226, 255);
317: e.Row.Cells[0].ColumnSpan = 4;
318: e.Row.Cells[0].Text = "合计";
319: e.Row.Cells[1].Visible = e.Row.Cells[2].Visible = e.Row.Cells[3].Visible = false;
320: }
321: }
322: }
323: }
324:
325: public class Salary
326: {
327: /// <summary>
328: /// 姓名
329: /// </summary>
330: public string Name { get; set; }
331: /// <summary>
332: /// 工资项
333: /// </summary>
334: public string Item { get; set; }
335: /// <summary>
336: /// 工资子项
337: /// </summary>
338: public string SubItem { get; set; }
339: /// <summary>
340: /// 月份
341: /// </summary>
342: public string Month { get; set; }
343: /// <summary>
344: /// 钱数
345: /// </summary>
346: public string Money { get; set; }
347: }
GridView运行前后比较: