单元格合并原理其实很简单,就是逐行判断要合并的单元格里的值是否和上一行的相同,要是相同的话就合并,不同的话就接着判断
我们可以通过扩展方法为GridView添加单元合并
我为GridView 创建了个RowSpan的方法 . 有一个object 参数
为什要定义object 参数 源于ASP.NET MVC 的Routing 组件配置规则 感觉这种方式很不错..
所以使用了这种方式来进行.
1
public
static
class
GridViewExtensions
2 {
3 /// <summary>
4 /// GridView行合并
5 /// </summary>
6 /// <param name="gridView"></param>
7 /// <param name="field"> 合并参数(匿名类型)
8 /// ColumnIndex:要合并行的索引 (以0开始,必须指定)
9 /// ColumnControlID(可选):如果该行为模板行则必须指定
10 /// PropertyName:根据ID属性 默认值为Text
11 /// Colums:(string类型)表示额外的行合并方式和ColumnIndex一样(多个使用逗号隔开,如Colums="5,6,7,8")
12 /// 例:
13 /// 合并第一行(第一行为模板行),绑定的一个Label名称为lblName 根据Text属性值合并 第6行方式和第一行相同
14 /// new {ColumnIndex=0,ColumnControlID="lblName",PropertyName="Text",Columns="5"}
15 /// </param>
16 public static GridView RowSpan( this GridView gridView, object field)
17 {
18 Dictionary < string , string > rowDictionary = ObjectLoadDictionary(field);
19 int columnIndex = int .Parse(rowDictionary[ " ColumnIndex " ]);
20 string columnName = rowDictionary[ " ColumnControlID " ];
21 string propertyName = rowDictionary[ " PropertyName " ];
22 string columns = rowDictionary[ " Columns " ];
23 for (var i = 0 ; i < gridView.Rows.Count; i ++ )
24 {
25
26 int rowSpanCount = 1 ;
27 for ( int j = i + 1 ; j < gridView.Rows.Count; j ++ )
28 {
29 // 绑定行合并处理
30 if ( string .IsNullOrEmpty(columnName))
31 {
32 // 比较2行的值是否相同
33 if (gridView.Rows[i].Cells[columnIndex].Text == gridView.Rows[j].Cells[columnIndex].Text)
34 {
35 // 合并行的数量+1
36 rowSpanCount ++ ;
37 // 隐藏相同的行
38 gridView.Rows[j].Cells[columnIndex].Visible = false ;
39 if ( ! string .IsNullOrEmpty(columns))
40 {
41 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[j].Cells[ int .Parse(c)].Visible = false );
42 }
43 }
44 else
45 {
46 break ;
47 }
48 }
49 else
50 {
51 // 模板行的合并处理
52 if (GetPropertyValue(gridView.Rows[i].Cells[columnIndex].FindControl(columnName), propertyName).ToString() == GetPropertyValue(gridView.Rows[j].Cells[columnIndex].FindControl(columnName), propertyName).ToString())
53 {
54 rowSpanCount ++ ;
55 // 隐藏相同的行
56 gridView.Rows[j].Cells[columnIndex].Visible = false ;
57 if ( ! string .IsNullOrEmpty(columns))
58 {
59
60 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[j].Cells[ int .Parse(c)].Visible = false );
61 }
62 }
63 else
64 {
65 break ;
66 }
67 }
68 }
69 if (rowSpanCount > 1 )
70 {
71 // 行合并
72 gridView.Rows[i].Cells[columnIndex].RowSpan = rowSpanCount;
73 // 判断是否有额外的行需要合并
74 if ( ! string .IsNullOrEmpty(columns))
75 {
76 // 额外的行合并
77 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[i].Cells[ int .Parse(c)].RowSpan = rowSpanCount);
78 }
79 i = i + rowSpanCount - 1 ;
80 }
81
82
83 }
84 return gridView;
85 }
86
87 private static Dictionary < string , string > ObjectLoadDictionary( object fields)
88 {
89 Dictionary < string , string > resultDictionary = new Dictionary < string , string > ();
90 PropertyInfo[] property = fields.GetType().GetProperties(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.GetProperty);
91 foreach (PropertyInfo tempProperty in property)
92 {
93 resultDictionary.Add(tempProperty.Name, tempProperty.GetValue(fields, null ).ToString());
94 }
95 // 指定默认值
96 if ( ! resultDictionary.Keys.Contains( " ColumnIndex " ))
97 {
98 throw new Exception( " 未指定要合并行的索引 ColumnIndex 属性! " );
99 }
100 if ( ! resultDictionary.Keys.Contains( " ColumnControlID " ))
101 {
102 resultDictionary.Add( " ColumnControlID " , null );
103 }
104
105 if ( ! resultDictionary.Keys.Contains( " PropertyName " ))
106 {
107 resultDictionary.Add( " PropertyName " , " Text " );
108 }
109
110 if ( ! resultDictionary.Keys.Contains( " Columns " ))
111 {
112 resultDictionary.Add( " Columns " , null );
113 }
114
115
116
117
118 return resultDictionary;
119 }
120
121 /// <summary>
122 /// 获取一个对象的一个属性..
123 /// </summary>
124 /// <param name="obj"></param>
125 /// <param name="PropertyName"> 属性名称 </param>
126 /// <returns> 属性的值, 如果无法获取则返回null </returns>
127 private static object GetPropertyValue( object obj, string PropertyName)
128 {
129 PropertyInfo property = obj.GetType().GetProperty(PropertyName);
130 return property.GetValue(obj, null );
131 }
132 }
2 {
3 /// <summary>
4 /// GridView行合并
5 /// </summary>
6 /// <param name="gridView"></param>
7 /// <param name="field"> 合并参数(匿名类型)
8 /// ColumnIndex:要合并行的索引 (以0开始,必须指定)
9 /// ColumnControlID(可选):如果该行为模板行则必须指定
10 /// PropertyName:根据ID属性 默认值为Text
11 /// Colums:(string类型)表示额外的行合并方式和ColumnIndex一样(多个使用逗号隔开,如Colums="5,6,7,8")
12 /// 例:
13 /// 合并第一行(第一行为模板行),绑定的一个Label名称为lblName 根据Text属性值合并 第6行方式和第一行相同
14 /// new {ColumnIndex=0,ColumnControlID="lblName",PropertyName="Text",Columns="5"}
15 /// </param>
16 public static GridView RowSpan( this GridView gridView, object field)
17 {
18 Dictionary < string , string > rowDictionary = ObjectLoadDictionary(field);
19 int columnIndex = int .Parse(rowDictionary[ " ColumnIndex " ]);
20 string columnName = rowDictionary[ " ColumnControlID " ];
21 string propertyName = rowDictionary[ " PropertyName " ];
22 string columns = rowDictionary[ " Columns " ];
23 for (var i = 0 ; i < gridView.Rows.Count; i ++ )
24 {
25
26 int rowSpanCount = 1 ;
27 for ( int j = i + 1 ; j < gridView.Rows.Count; j ++ )
28 {
29 // 绑定行合并处理
30 if ( string .IsNullOrEmpty(columnName))
31 {
32 // 比较2行的值是否相同
33 if (gridView.Rows[i].Cells[columnIndex].Text == gridView.Rows[j].Cells[columnIndex].Text)
34 {
35 // 合并行的数量+1
36 rowSpanCount ++ ;
37 // 隐藏相同的行
38 gridView.Rows[j].Cells[columnIndex].Visible = false ;
39 if ( ! string .IsNullOrEmpty(columns))
40 {
41 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[j].Cells[ int .Parse(c)].Visible = false );
42 }
43 }
44 else
45 {
46 break ;
47 }
48 }
49 else
50 {
51 // 模板行的合并处理
52 if (GetPropertyValue(gridView.Rows[i].Cells[columnIndex].FindControl(columnName), propertyName).ToString() == GetPropertyValue(gridView.Rows[j].Cells[columnIndex].FindControl(columnName), propertyName).ToString())
53 {
54 rowSpanCount ++ ;
55 // 隐藏相同的行
56 gridView.Rows[j].Cells[columnIndex].Visible = false ;
57 if ( ! string .IsNullOrEmpty(columns))
58 {
59
60 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[j].Cells[ int .Parse(c)].Visible = false );
61 }
62 }
63 else
64 {
65 break ;
66 }
67 }
68 }
69 if (rowSpanCount > 1 )
70 {
71 // 行合并
72 gridView.Rows[i].Cells[columnIndex].RowSpan = rowSpanCount;
73 // 判断是否有额外的行需要合并
74 if ( ! string .IsNullOrEmpty(columns))
75 {
76 // 额外的行合并
77 columns.Split( ' , ' ).ToList < string > ().ForEach(c => gridView.Rows[i].Cells[ int .Parse(c)].RowSpan = rowSpanCount);
78 }
79 i = i + rowSpanCount - 1 ;
80 }
81
82
83 }
84 return gridView;
85 }
86
87 private static Dictionary < string , string > ObjectLoadDictionary( object fields)
88 {
89 Dictionary < string , string > resultDictionary = new Dictionary < string , string > ();
90 PropertyInfo[] property = fields.GetType().GetProperties(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.GetProperty);
91 foreach (PropertyInfo tempProperty in property)
92 {
93 resultDictionary.Add(tempProperty.Name, tempProperty.GetValue(fields, null ).ToString());
94 }
95 // 指定默认值
96 if ( ! resultDictionary.Keys.Contains( " ColumnIndex " ))
97 {
98 throw new Exception( " 未指定要合并行的索引 ColumnIndex 属性! " );
99 }
100 if ( ! resultDictionary.Keys.Contains( " ColumnControlID " ))
101 {
102 resultDictionary.Add( " ColumnControlID " , null );
103 }
104
105 if ( ! resultDictionary.Keys.Contains( " PropertyName " ))
106 {
107 resultDictionary.Add( " PropertyName " , " Text " );
108 }
109
110 if ( ! resultDictionary.Keys.Contains( " Columns " ))
111 {
112 resultDictionary.Add( " Columns " , null );
113 }
114
115
116
117
118 return resultDictionary;
119 }
120
121 /// <summary>
122 /// 获取一个对象的一个属性..
123 /// </summary>
124 /// <param name="obj"></param>
125 /// <param name="PropertyName"> 属性名称 </param>
126 /// <returns> 属性的值, 如果无法获取则返回null </returns>
127 private static object GetPropertyValue( object obj, string PropertyName)
128 {
129 PropertyInfo property = obj.GetType().GetProperty(PropertyName);
130 return property.GetValue(obj, null );
131 }
132 }
这个扩展方法的使用方式很简单
var s
=
new
[] {
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 86f, 数学 = 90f, 学期 = " 第一学期 " },
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 89f, 数学 = 98f, 学期 = " 第二学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 75f, 数学 = 64f, 学期 = " 第二学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 63f, 数学 = 93f, 学期 = " 第二学期 " }
};
this .GridView1.DataSource = s;
this .GridView1.DataBind();
this .GridView1.RowSpan( new { ColumnIndex = 0 , Columns = " 1 " });
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 86f, 数学 = 90f, 学期 = " 第一学期 " },
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 89f, 数学 = 98f, 学期 = " 第二学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 75f, 数学 = 64f, 学期 = " 第二学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 63f, 数学 = 93f, 学期 = " 第二学期 " }
};
this .GridView1.DataSource = s;
this .GridView1.DataBind();
this .GridView1.RowSpan( new { ColumnIndex = 0 , Columns = " 1 " });
我们合并第1列的值姓名.. GirdView索引是从0开始的所以ColumnIndex=0 性别肯定和姓名对应的
可以是用Colunmns="" 这个属性来指定哪个列的合并方式和 ColumnIndex指定的列相同 多个用 ","隔开比如 Colunmns="2,3,4,5"这种方式
如果GridView中使用了模板列 则除了需要指定ColumnIndex外还需要添加ID和PropertyName属性
如 new {ColumnIndex=0,ID="lblName",PropertyName="Text",Columns="1" }
ID 表示模板列的控件名称 PropertyName 表示值来自于控件的哪个属性.
注:暂时只能指定普通属性如Text 或Value ;SelectedItem.Value 这种属性需要修改部分代码 也不能包含容器控件 修改部分代码可以支持容器控件
效果图
姓名 | 性别 | 语文 | 数学 | 学期 |
---|---|---|---|---|
张三 | 男 | 86 | 90 | 第一学期 |
89 | 98 | 第二学期 | ||
李四 | 男 | 89 | 64 | 第一学期 |
75 | 64 | 第二学期 | ||
王五 | 男 | 89 | 64 | 第一学期 |
63 | 93 | 第二学期 |
合并姓名和语文相同的分数
var s
=
new
[] {
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 86f, 数学 = 90f, 学期 = " 第一学期 " },
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 89f, 数学 = 98f, 学期 = " 第二学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 75f, 数学 = 64f, 学期 = " 第二学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 63f, 数学 = 93f, 学期 = " 第二学期 " }
};
this .GridView1.DataSource = s;
this .GridView1.DataBind();
this .GridView1.RowSpan( new { ColumnIndex = 0 , Columns = " 1 " });
this .GridView1.RowSpan( new { ColumnIndex = 2 });
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 86f, 数学 = 90f, 学期 = " 第一学期 " },
new { 姓名 = " 张三 " , 性别 = " 男 " , 语文 = 89f, 数学 = 98f, 学期 = " 第二学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 李四 " , 性别 = " 男 " , 语文 = 75f, 数学 = 64f, 学期 = " 第二学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 89f, 数学 = 64f, 学期 = " 第一学期 " },
new { 姓名 = " 王五 " , 性别 = " 男 " , 语文 = 63f, 数学 = 93f, 学期 = " 第二学期 " }
};
this .GridView1.DataSource = s;
this .GridView1.DataBind();
this .GridView1.RowSpan( new { ColumnIndex = 0 , Columns = " 1 " });
this .GridView1.RowSpan( new { ColumnIndex = 2 });
姓名 | 性别 | 语文 | 数学 | 学期 |
---|---|---|---|---|
张三 | 男 | 86 | 90 | 第一学期 |
89 | 98 | 第二学期 | ||
李四 | 男 | 64 | 第一学期 | |
75 | 64 | 第二学期 | ||
王五 | 男 | 89 | 64 | 第一学期 |
63 | 93 | 第二学期 |
可以使用这种方式
this
.GridView1.RowSpan(
new
{ ColumnIndex
=
0
, Columns
=
"
1
"
}).RowSpan(
new
{ ColumnIndex
=
2
}).RowSpan(
new
{ ColumnIndex
=
3
});
姓名 | 性别 | 语文 | 数学 | 学期 |
---|---|---|---|---|
张三 | 男 | 86 | 90 | 第一学期 |
89 | 98 | 第二学期 | ||
李四 | 男 | 64 | 第一学期 | |
75 | 第二学期 | |||
王五 | 男 | 89 | 第一学期 | |
63 | 93 | 第二学期 |