大家GridView都用的比較多吧..有沒遇到單元格需要合並的需求..
單元格合並原理其實很簡單,就是逐行判斷要合並的單元格裡的值是否和上一行的相同,要是相同的 話就合並,不同的話就接著判斷
我們可以通過擴展方法為GridView添加單元合並
public static class GridViewExtensions
{
/// <summary>
/// GridView行合並
/// </summary>
/// <param name="gridView"></param>
/// <param name="field">合並參數(匿名類型)
/// ColumnIndex:要合並行的索引 (以0開始,必須指定)
/// ID(可選):如果該行為模板行則必須指定
/// PropertyName:根據ID屬性 默認值為Text
/// Colums:(string類型)表示額外的行合並方式和ColumnIndex一樣(多個使用逗 號隔開,如Colums="5,6,7,8")
/// 例:
/// 合並第一行(第一行為模板行),綁定的一個Label名稱為lblName 根據Text 屬性值合並 第6行方式和第一行相同
/// new {ColumnIndex=0,ID="lblName",PropertyName="Text",Columns="5"}
/// </param>
public static GridView RowSpan(this GridView gridView, object field)
{
Dictionary<string, string> rowDictionary = ObjectLoadDictionary(field);
int columnIndex = int.Parse(rowDictionary["ColumnIndex"]);
string columnName = rowDictionary["ColumnName"];
string propertyName = rowDictionary["PropertyName"];
string columns = rowDictionary["Columns"];
for (var i = 0; i < gridView.Rows.Count; i++)
{
int rowSpanCount = 1;
for (int j = i + 1; j < gridView.Rows.Count; j++)
{
//綁定行合並處理
if (string.IsNullOrEmpty(columnName))
{
//比較2行的值是否相同
if (gridView.Rows[i].Cells [columnIndex].Text == gridView.Rows[j].Cells[columnIndex].Text)
{
//合並行的數量+1
rowSpanCount++;
//隱藏相同的行
gridView.Rows[j].Cells [columnIndex].Visible = false;
if (!string.IsNullOrEmpty (columns))
{
columns.Split (',').ToList<string>().ForEach(c => gridView.Rows[j].Cells[int.Parse (c)].Visible=false);
}
}
else
{
break;
}
}
else
{
//模板行的合並處理
if (GetPropertyValue(gridView.Rows [i].Cells[columnIndex].FindControl(columnName), propertyName) == GetPropertyValue (gridView.Rows[j].Cells[columnIndex].FindControl(columnName), propertyName))
{
rowSpanCount++;
//隱藏相同的行
gridView.Rows[j].Cells [columnIndex].Visible = false;
if (!string.IsNullOrEmpty (columns))
{
columns.Split (',').ToList<string>().ForEach(c => gridView.Rows[j].Cells[int.Parse(c)].Visible = false);
}
}
else
{
break;
}
}
}
if (rowSpanCount > 1)
{
//行合並
gridView.Rows[i].Cells[columnIndex].RowSpan = rowSpanCount;
//判斷是否有額外的行需要合並
if (!string.IsNullOrEmpty(columns))
{
//額外的行合並
columns.Split(',').ToList<string> ().ForEach(c => gridView.Rows[i].Cells[int.Parse(c)].RowSpan = rowSpanCount);
}
i = i + rowSpanCount - 1;
}
}
return gridView;
}
private static Dictionary<string, string> ObjectLoadDictionary (object fields)
{
Dictionary<string, string> resultDictionary = new Dictionary<string, string>();
PropertyInfo[] property = fields.GetType().GetProperties (BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.GetProperty);
foreach (PropertyInfo tempProperty in property)
{
resultDictionary.Add(tempProperty.Name, tempProperty.GetValue(fields, null).ToString());
}
//指定默認值
if (!resultDictionary.Keys.Contains("ColumnIndex"))
{
throw new Exception("未指定要合並行的索引 ColumnIndex 屬性!");
}
if (!resultDictionary.Keys.Contains("ColumnName"))
{
resultDictionary.Add("ColumnName", null);
}
if (!resultDictionary.Keys.Contains("PropertyName"))
{
resultDictionary.Add("PropertyName", "Text");
}
if (!resultDictionary.Keys.Contains("Columns"))
{
resultDictionary.Add("Columns", null);
}
return resultDictionary;
}
/// <summary>
/// 獲取一個對象的一個屬性..
/// </summary>
/// <param name="obj"></param>
/// <param name="PropertyName">屬性名稱</param>
/// <returns>屬性的值, 如果無法獲取則返回 null</returns>
private static object GetPropertyValue(object obj, string PropertyName)
{
PropertyInfo property = obj.GetType().GetProperty (PropertyName);
return property.GetValue(obj,null);
}
}
我為GridView 創建了個RowSpan的方法 .有一個object 參數
為什要定義object 參數 源於ASP.NET MVC 的Routing 組件配置規則 感覺這種方式很不錯..
所以使用了這種方式來進行.
這個擴展方法的使用方式很簡單
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" });
我們合並第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 這種屬性需要修改部分代碼 也 不能包含容器控件 修改部分代碼可以支持容器控件
效果圖
合並姓名和語文相同的分數
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 });
可以使用這種方式
this.GridView1.RowSpan(new { ColumnIndex = 0, Columns = "1" }).RowSpan(new { ColumnIndex = 2 }).RowSpan(new { ColumnIndex = 3 });
還有什麼額外的參數配置 大家可以提出來 我進行改進.
效率問題 我可以考慮使用Lambda表達式樹動態創建Lambda表達式的效率