有天在想工作上的事的时候,看着.net原有的DataGridView,想起以前我写过的一篇文章,总结了一个好的Gird控件应该具备哪些功能,在那里我提及到了分组功能,就像jqGrid那样,
其实这样的显示型式很常见,就在平时邮箱的邮件列表就是按这种分组型式显示的,按今天、昨天、上周之类,在购物网站的历史订单处也可以看见这种Grid表的身影。但是原有的DataGridView并不支持这种分组功能。那只能扩展一下了。
之前写了一个多维表头的GirdView,有经验了,知道搞这种图形化的东西无非都是用GDI+,上网找了一些文章后有点愣住了,之前画表头的是在DataGridView的OnPaint方法里把表头描绘出来,但是这里画分组的话就不同了,不是在DataGridViewRow的Paint方法里面处理。
因此要完成这个可分组DataGridView需要对两个类进行拓展,一个是DataGridView,另一个是DataGirdViewRow。而实现这个分组DataGridView的效果大体步骤就是先把分组的标题行添加到GirdView里面,然后逐个把该组的数据行添加到GridView里面并控制它的显示状态,在分组的标题行进行相关的操作来改变数据行显示状态达到分组显示的效果。
下面则逐个类来介绍吧!
GroupGridView继承DataGridView,以下是它的一些字段和属性的定义
成员名称
数据类型
修饰符
描述
GroupFieldName
String
public
要分组的字段的名称,只能是类的属性名或者是DataTable的列名
objDataSource
Object
Protected
使用分组时暂时记录数据源
GroupRowTemplate
GroupGridViewRow
Public
分组行实例的模板
isGroupping
Bool
Private
是否使用分组显示
如果要使用这个GroupGridView的话,还要默认地对原本的DataGridView进行一些设置,这些设置我都塞到了构造函数里面,大体上分三类,分别是操作设置,样式设置和部分属性或字段的赋值,代码如下
1 public GroupGridView() 2 { 3 //对GridView的操作的设置 4 this.AllowUserToAddRows = false; 5 this.AllowUserToDeleteRows = false; 6 this.SelectionMode = DataGridViewSelectionMode.FullRowSelect; 7 8 //对GridView的样式设置 9 this.EditMode = DataGridViewEditMode.EditProgrammatically; 10 this.RowHeadersVisible = false; 11 this.CellBorderStyle = DataGridViewCellBorderStyle.RaisedHorizontal; 12 13 //对实例的部分成员赋值 14 isGroupping = false; 15 GroupRowTemplate = new GroupGridViewRow(); 16 }
其实GroupGridView要做的事就是能增加分组,在单击和双击分组行时作处理,还有就是数据绑定时把数据分组添加到GirdView里。那么就逐个方法来介绍
第一个是增加分组的
1 public GroupGridViewRow CreateGroupGridViewRow(string title) 2 { 3 GroupGridViewRow group = this.GroupRowTemplate.Clone() as GroupGridViewRow; 4 if (group == null) 5 throw new NullReferenceException("组模板为空或者组模板类型不是GroupGridViewRow"); 6 group.Title = title; 7 group.ParentGridView = this; 8 group.CreateCells(this, group.Title); 9 this.Rows.Add(group); 10 group.CollapseGroup(); 11 return group; 12 }
主要是按照模板拷贝一个GroupGridViewRow的实例,设置相应的属性后就把它添加到GirdView里面。
然后到鼠标对分组行的点击事件,单击展开/折叠图标就展示或隐藏该组的数据。双击分组行同样也达到这种显示状态切换的效果。
1 protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e) 2 { 3 if (e.RowIndex == -1 ||e.ColumnIndex==-1|| e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != 1) 4 return; 5 GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow; 6 if (group != null && group.IsIconHit(e)) 7 group.Toggle(); 8 base.OnCellMouseDown(e); 9 } 10 11 protected override void OnCellDoubleClick(DataGridViewCellEventArgs e) 12 { 13 if (e.RowIndex == -1||e.ColumnIndex==-1) 14 return; 15 GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow; 16 if (group != null ) 17 group.Toggle(); 18 base.OnCellDoubleClick(e); 19 }
Toggle和IsIconHit方法在GroupGridViewRow里会定义,在介绍GroupGridViewRow会列出该方法的定义,在单击时就要通过IsIconHit判断鼠标是否点击到了图片,当然是点击中图标才会经行状态切换,而双击则不需要了。无论是双击和单击都要判断当前鼠标所在的行是分组行,如果是数据行的话就不会作任何操作了。
为了方便点绑定到数据,我重新定义了控件的DataSource属性。当分组字段信息(GroupFieldName属性)不存在时就会使用GridView默认的绑定数据,如果存在就会分组筛选出数据,然后逐个组去把行添加进去。
1 public new object DataSource 2 { 3 get 4 { 5 if (isGroupping) return objDataSource; 6 return base.DataSource; 7 } 8 set 9 { 10 if (string.IsNullOrEmpty(GroupFieldName) 11 || string.IsNullOrWhiteSpace(GroupFieldName)) 12 { 13 14 foreach (DataGridViewColumn col in this.Columns) 15 col.SortMode = DataGridViewColumnSortMode.Automatic; 16 base.DataSource = value; 17 isGroupping = false; 18 } 19 else 20 { 21 22 foreach (DataGridViewColumn col in this.Columns) 23 col.SortMode = DataGridViewColumnSortMode.NotSortable; 24 if (value is IEnumerable) 25 BindIEnumerableDataSource(value as IEnumerable); 26 else if (value is DataTable) 27 BindDataTableDataSouce(value as DataTable); 28 else if (value is DataSet) 29 BindDataSetDataSource(value as DataSet); 30 else 31 { 32 throw new NotImplementedException("不支持此类型作数据源"); 33 } 34 objDataSource = value; 35 isGroupping = true; 36 37 } 38 } 39 }
现在能自动绑定的数据源只能是可枚举的或DataTable,DataSet其实是把里面第一个DataTable作为数据源而已。不同类型的数据有相应的处理方法
1 private void BindIEnumerableDataSource(IEnumerable enumerable) 2 { 3 IEnumerable iends = enumerable; 4 if (iends == null) return; 5 Type dsItemType = null; 6 foreach (object item in iends) 7 dsItemType = item.GetType(); 8 if (iends == null) return; 9 10 PropertyInfo proInfo = dsItemType.GetProperty(GroupFieldName); 11 12 Dictionary<string, List<object>> groupDataSource = new Dictionary<string, List<object>>(); 13 foreach (object item in iends) 14 { 15 string tempStr = proInfo.GetValue(item, null).ToString(); 16 if (!groupDataSource.ContainsKey(tempStr)) 17 groupDataSource[tempStr] = new List<object>(); 18 groupDataSource[tempStr].Add(item); 19 } 20 21 List<string> colFildNames = new List<string>(this.Columns.Count); 22 foreach (DataGridViewColumn col in this.Columns) 23 colFildNames.Add(col.DataPropertyName); 24 GroupGridViewRow group = null; 25 List<object> datas = new List<object>(colFildNames.Count); 26 foreach (KeyValuePair<string, List<object>> gi in groupDataSource) 27 { 28 group = CreateGroupGridViewRow(gi.Key); 29 foreach (object celli in gi.Value) 30 { 31 foreach (string colName in colFildNames) 32 { 33 datas.Add(dsItemType.GetProperty(colName).GetValue(celli, null)); 34 } 35 group.AddRowToGroup(datas.ToArray()); 36 datas.Clear(); 37 } 38 group.CollapseGroup(); 39 } 40 }
对于可枚举的数据源,我就通过反射把相应的属性的值都拿出来填到DataGridViewRow里面。
1 private void BindDataTableDataSouce(DataTable table) 2 { 3 Dictionary<string, List<DataRow>> groupDataSource = new Dictionary<string, List<DataRow>>(); 4 foreach (DataRow row in table.Rows) 5 { 6 string tempStr = row[GroupFieldName].ToString(); 7 if (!groupDataSource.ContainsKey(tempStr)) 8 groupDataSource[tempStr] = new List<DataRow>(); 9 groupDataSource[tempStr].Add(row); 10 } 11 12 List<string> colFildNames = new List<string>(this.Columns.Count); 13 foreach (DataGridViewColumn col in this.Columns) 14 colFildNames.Add(col.DataPropertyName); 15 GroupGridViewRow group = null; 16 List<object> datas = new List<object>(colFildNames.Count); 17 foreach (KeyValuePair<string, List<DataRow>> gi in groupDataSource) 18 { 19 group = CreateGroupGridViewRow(gi.Key); 20 foreach (DataRow celli in gi.Value) 21 { 22 foreach (string colName in colFildNames) 23 { 24 datas.Add(celli[colName]); 25 } 26 group.AddRowToGroup(datas.ToArray()); 27 datas.Clear(); 28 } 29 group.CollapseGroup(); 30 } 31 }
DataTable的绑定也类似,更方便的是DataTable不需要用反射了,直接通过行列的访问就可以了。
GruopGridView介绍就到此结束,下面则介绍另一个类GroupGridViewRow,它是继承DataGridViewRow。而GDI+的描绘都是在这个方法里面。也是先看看里面的成员
成员名称
数据类型
修饰符
描述
IsExpanded
bool
public
分组的状态,是展开还是折叠
Title
string
public
分组的标题,通常是分组的数据
ParentGridView
GroupGridView
public
分组行所在的GridView
groupRows
List<DataGridViewRow>
private
此分组包含的数据行
那么一个GroupGridViewRow的要处理的事就有以下几个,对分组下的数据行的状态控制,判断鼠标单击是否命中图标,增加一行数据到该分组下,还有最重要的就是描绘出分组行的外观。
先列举数据行的状态控制方法,ExpandGroup()展开分组,CollapseGroup()折叠分组,还有Toggle()切换
1 public void ExpandGroup() 2 { 3 IsExpanded = true; 4 foreach (DataGridViewRow row in groupRows) 5 row.Visible = true; 6 } 7 8 public void CollapseGroup() 9 { 10 IsExpanded = false; 11 foreach (DataGridViewRow row in groupRows) 12 row.Visible = false; 13 } 14 15 public void Toggle() 16 { 17 if (IsExpanded) 18 CollapseGroup(); 19 else 20 ExpandGroup(); 21 }
实际上就是通过遍历groupRows集合,改变其显示状态而已。
判断单击图标的方法如下
1 public bool IsIconHit(DataGridViewCellMouseEventArgs e) 2 { 3 Rectangle groupBound = this.ParentGridView.GetRowDisplayRectangle(e.RowIndex, false); 4 5 if ( 6 e.X > groupBound.Left + 5 && 7 e.X < groupBound.Left + 15 && 8 e.Y > ((groupBound.Height - 1) - 5) / 2-2 && 9 e.Y < ((groupBound.Height - 1) - 5) / 2 + 10-2 10 ) 11 return true; 12 13 return false; 14 }
主要是通过鼠标指针当前所在的坐标是不是在图标的范围以内,那个范围有点不好把握,要通过DataGridView的GetRowDisplayRectangle方法得到分组行的矩形,Left,X这两个属性有点分不清了。
增加数据行的方法如下
1 public DataGridViewRow AddRowToGroup(params object[] values) 2 { 3 DataGridViewRow row = this.ParentGridView.RowTemplate.Clone() as DataGridViewRow; 4 if (row == null) throw new NullReferenceException("行模板为空或者组模板类型不是DataGridViewRow"); 5 6 row.CreateCells(this.ParentGridView, values); 7 this.ParentGridView.Rows.Add(row); 8 this.groupRows.Add(row); 9 return row; 10 }
也复杂,通过DataGridView的普通数据行(不是分组行)的目标拷贝,填上数据,然后分别添加到这个分组的数据行集合中和GridView中。
最后到描绘分组行的方法,重写Paint方法,这个基本上是参考园友老虎哥的代码的,也作了一些小的调整,解决了水平滚动之后文字和图标有重影的问题,所以老虎哥看见了不要怪哈!
1 protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow) 2 { 3 4 int holdWidth = this.ParentGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible); 5 Color backgroudColor; 6 if (this.Selected) 7 backgroudColor = this.ParentGridView.DefaultCellStyle.SelectionBackColor; 8 else 9 backgroudColor = this.ParentGridView.DefaultCellStyle.BackColor; 10 using (Brush backgroudBrush = new SolidBrush(backgroudColor)) 11 { 12 graphics.FillRectangle(backgroudBrush, rowBounds.X,rowBounds.Y,holdWidth,rowBounds.Height); 13 } 14 using (Brush bottomLineBrush=new SolidBrush(Color.FromKnownColor( KnownColor.GradientActiveCaption))) 15 { 16 graphics.FillRectangle(bottomLineBrush, rowBounds.Left, rowBounds.Top + rowBounds.Height-2, holdWidth, 2); 17 } 18 19 StringFormat sf = new StringFormat(); 20 sf.LineAlignment = StringAlignment.Center; 21 Font font = new Font(this.ParentGridView.Font, FontStyle.Bold); 22 graphics.DrawString(this.Title, font, Brushes.Black, 23 rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + 20, rowBounds.Top + rowBounds.Height / 2, sf); 24 25 26 int symbolX = rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + 5; 27 int symbolY =rowBounds.Y+ ((rowBounds.Height - 1) - 5) / 2-2; 28 if (Application.RenderWithVisualStyles) 29 { 30 31 VisualStyleRenderer glyphRenderer; 32 if (this.IsExpanded) 33 glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened); 34 else 35 glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed); 36 37 Rectangle glyphRectangle =new Rectangle(symbolX, symbolY, 10, 10); 38 39 glyphRenderer.DrawBackground(graphics, glyphRectangle); 40 41 } 42 else 43 { 44 int h = 8; 45 int w = 8; 46 int x = symbolX; 47 int y = symbolY; 48 graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h); 49 graphics.FillRectangle(new SolidBrush(Color.White), 50 x + 1, y + 1, w - 1, h - 1); 51 52 //画横线 53 graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), 54 x + 2, y + 4, x + w - 2, y + 4); 55 56 //画竖线 57 if (!this.IsExpanded) 58 graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), 59 x + 4, y + 2, x + 4, y + h - 2); 60 } 61 }
我自己写出来的话还是有点棘手的,绘制对于我自己而言就分了三部分,分组栏的背景边线描绘,文字的填写,还有图标的描绘,而图标在这里很好的考虑那个显示效果,分了渐变效果和单色效果。我也该多学习学习。
最后列一下控件的使用,列的设置分组设置,数据绑定如下,CreateColumn是我定义的方法,主要是构造一个新的列,对其进行设置之后就添加到GridView里面。
1 groupGridView21.GroupFieldName = "name"; 2 3 CreateColumn("id", "id", groupGridView21); 4 CreateColumn("invdate", "invdate", groupGridView21); 5 CreateColumn("note", "note", groupGridView21); 6 CreateColumn("amount", "amount", groupGridView21); 7 CreateColumn("tax", "tax", groupGridView21); 8 CreateColumn("total", "total", groupGridView21); 9 CreateColumn("name", "name", groupGridView21); 10 11 groupGridView21.DataSource = datasource;
效果就这样,数据我完全拿了那个jqGrid的Demo里面的数据
控件的缺点还是跟老虎哥的控件类似,要把显示的列逐个添加,不支持自动添加列,并且分组显示的时候不能对列经行排序。最后附上控件的完整源码,介绍完毕,谢谢!
class="code_img_closed" src="/Upload/Images/2013100522/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('9f7fe05d-f941-4658-a166-ff27dffc4c68',event)" src="/Upload/Images/2013100522/2B1B950FA3DF188F.gif" alt="" />1 public class GroupGridView:DataGridView 2 { 3 public string GroupFieldName { get; set; } 4 5 protected object objDataSource; 6 7 public GroupGridViewRow GroupRowTemplate { get;protected set; } 8 9 private bool isGroupping; 10 11 public GroupGridView() 12 { 13 //对GridView的操作的设置 14 this.AllowUserToAddRows = false; 15 this.AllowUserToDeleteRows = false; 16 this.SelectionMode = DataGridViewSelectionMode.FullRowSelect; 17 18 //对GridView的样式设置 19 this.EditMode = DataGridViewEditMode.EditProgrammatically; 20 this.RowHeadersVisible = false; 21 this.CellBorderStyle = DataGridViewCellBorderStyle.RaisedHorizontal; 22 23 //对实例的部分成员赋值 24 isGroupping = false; 25 GroupRowTemplate = new GroupGridViewRow(); 26 } 27 28 public GroupGridViewRow CreateGroupGridViewRow(string title) 29 { 30 GroupGridViewRow group = this.GroupRowTemplate.Clone() as GroupGridViewRow; 31 if (group == null) 32 throw new NullReferenceException("组模板为空或者组模板类型不是GroupGridViewRow"); 33 group.Title = title; 34 group.ParentGridView = this; 35 group.CreateCells(this, group.Title); 36 this.Rows.Add(group); 37 //do 38 //{ 39 // group.Toggle(); 40 //} while (group.IsExpanded); 41 group.CollapseGroup(); 42 return group; 43 } 44 45 public new object DataSource 46 { 47 get 48 { 49 if (isGroupping) return objDataSource; 50 return base.DataSource; 51 } 52 set 53 { 54 if (string.IsNullOrEmpty(GroupFieldName) 55 || string.IsNullOrWhiteSpace(GroupFieldName)) 56 { 57 58 foreach (DataGridViewColumn col in this.Columns) 59 col.SortMode = DataGridViewColumnSortMode.Automatic; 60 base.DataSource = value; 61 isGroupping = false; 62 } 63 else 64 { 65 66 foreach (DataGridViewColumn col in this.Columns) 67 col.SortMode = DataGridViewColumnSortMode.NotSortable; 68 if (value is IEnumerable) 69 BindIEnumerableDataSource(value as IEnumerable); 70 else if (value is DataTable) 71 BindDataTableDataSouce(value as DataTable); 72 else if (value is DataSet) 73 BindDataSetDataSource(value as DataSet); 74 else 75 { 76 throw new NotImplementedException("不支持此类型作数据源"); 77 } 78 objDataSource = value; 79 isGroupping = true; 80 81 } 82 } 83 } 84 85 protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e) 86 { 87 if (e.RowIndex == -1 ||e.ColumnIndex==-1|| e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != 1) 88 return; 89 GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow; 90 if (group != null && group.IsIconHit(e)) 91 group.Toggle(); 92 base.OnCellMouseDown(e); 93 } 94 95 protected override void OnCellDoubleClick(DataGridViewCellEventArgs e) 96 { 97 if (e.RowIndex == -1||e.ColumnIndex==-1) 98 return; 99 GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow; 100 if (group != null ) 101 group.Toggle(); 102 base.OnCellDoubleClick(e); 103 } 104 105 private void BindIEnumerableDataSource(IEnumerable enumerable) 106 { 107 IEnumerable iends = enumerable; 108 if (iends == null) return; 109 Type dsItemType = null; 110 foreach (object item in iends) 111 dsItemType = item.GetType(); 112 if (iends == null) return; 113 114 PropertyInfo proInfo = dsItemType.GetProperty(GroupFieldName); 115 116 Dictionary<string, List<object>> groupDataSource = new Dictionary<string, List<object>>(); 117 foreach (object item in iends) 118 { 119 string tempStr = proInfo.GetValue(item, null).ToString(); 120 if (!groupDataSource.ContainsKey(tempStr)) 121 groupDataSource[tempStr] = new List<object>(); 122 groupDataSource[tempStr].Add(item); 123 } 124 125 List<string> colFildNames = new List<string>(this.Columns.Count); 126 foreach (DataGridViewColumn col in this.Columns) 127 colFildNames.Add(col.DataPropertyName); 128 GroupGridViewRow group = null; 129 List<object> datas = new List<object>(colFildNames.Count); 130 foreach (KeyValuePair<string, List<object>> gi in groupDataSource) 131 { 132 group = CreateGroupGridViewRow(gi.Key); 133 foreach (object celli in gi.Value) 134 { 135 foreach (string colName in colFildNames) 136 { 137 datas.Add(dsItemType.GetProperty(colName).GetValue(celli, null)); 138 } 139 group.AddRowToGroup(datas.ToArray()); 140 datas.Clear(); 141 } 142 //do 143 //{ 144 // group.Toggle(); 145 //} while (group.IsExpanded); 146 group.CollapseGroup(); 147 } 148 } 149 150 private void BindDataTableDataSouce(DataTable table) 151 { 152 Dictionary<string, List<DataRow>> groupDataSource = new Dictionary<string, List<DataRow>>(); 153 foreach (DataRow row in table.Rows) 154 { 155 string tempStr = row[GroupFieldName].ToString(); 156 if (!groupDataSource.ContainsKey(tempStr)) 157 groupDataSource[tempStr] = new List<DataRow>(); 158 groupDataSource[tempStr].Add(row); 159 } 160 161 List<string> colFildNames = new List<string>(this.Columns.Count); 162 foreach (DataGridViewColumn col in this.Columns) 163 colFildNames.Add(col.DataPropertyName); 164 GroupGridViewRow group = null; 165 List<object> datas = new List<object>(colFildNames.Count); 166 foreach (KeyValuePair<string, List<DataRow>> gi in groupDataSource) 167 { 168 group = CreateGroupGridViewRow(gi.Key); 169 foreach (DataRow celli in gi.Value) 170 { 171 foreach (string colName in colFildNames) 172 { 173 datas.Add(celli[colName]); 174 } 175 group.AddRowToGroup(datas.ToArray()); 176 datas.Clear(); 177 } 178 //do 179 //{ 180 // group.Toggle(); 181 //} while (group.IsExpanded); 182 group.CollapseGroup(); 183 } 184 } 185 186 private void BindDataSetDataSource(DataSet dataset) 187 { 188 if (dataset == null || dataset.Tables.Count == null) return; 189 BindDataTableDataSouce(dataset.Tables[0]); 190 } 191 } 192 193 public class GroupGridViewRow:DataGridViewRow 194 { 195 public bool IsExpanded { get;private set; } 196 197 public string Title { get; set; } 198 199 public GroupGridView ParentGridView { get; set; } 200 201 private List<DataGridViewRow> groupRows { get; set; } 202 203 public GroupGridViewRow() 204 { 205 IsExpanded = false; 206 groupRows = new List<DataGridViewRow>(); 207 } 208 209 public void ExpandGroup() 210 { 211 IsExpanded = true; 212 foreach (DataGridViewRow row in groupRows) 213 row.Visible = true; 214 } 215 216 public void CollapseGroup() 217 { 218 IsExpanded = false; 219 foreach (DataGridViewRow row in groupRows) 220 row.Visible = false; 221 } 222 223 public void Toggle() 224 { 225 if (IsExpanded) 226 CollapseGroup(); 227 else 228 ExpandGroup(); 229 } 230 231 public bool IsIconHit(DataGridViewCellMouseEventArgs e) 232 { 233 Rectangle groupBound = this.ParentGridView.GetRowDisplayRectangle(e.RowIndex, false); 234 235 if ( 236 e.X > groupBound.Left + 5 && 237 e.X < groupBound.Left + 15 && 238 e.Y > ((groupBound.Height - 1) - 5) / 2-2 && 239 e.Y < ((groupBound.Height - 1) - 5) / 2 + 10-2 240 ) 241 return true; 242 243 return false; 244 } 245 246 public DataGridViewRow AddRowToGroup(params object[] values) 247 { 248 DataGridViewRow row = this.ParentGridView.RowTemplate.Clone() as DataGridViewRow; 249 if (row == null) throw new NullReferenceException("行模板为空或者组模板类型不是DataGridViewRow"); 250 251 row.CreateCells(this.ParentGridView, values); 252 this.ParentGridView.Rows.Add(row); 253 this.groupRows.Add(row); 254 return row; 255 } 256 257 protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow) 258 { 259 260 int holdWidth = this.ParentGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible); 261 Color backgroudColor; 262 if (this.Selected) 263 backgroudColor = this.ParentGridView.DefaultCellStyle.SelectionBackColor; 264 else 265 backgroudColor = this.ParentGridView.DefaultCellStyle.BackColor; 266 using (Brush backgroudBrush = new SolidBrush(backgroudColor)) 267 { 268 graphics.FillRectangle(backgroudBrush, rowBounds.X,rowBounds.Y,holdWidth,rowBounds.Height); 269 } 270 using (Brush bottomLineBrush=new SolidBrush(Color.FromKnownColor( KnownColor.GradientActiveCaption))) 271 { 272 graphics.FillRectangle(bottomLineBrush, rowBounds.Left, rowBounds.Top + rowBounds.Height-2, holdWidth, 2); 273 } 274 275 StringFormat sf = new StringFormat(); 276 sf.LineAlignment = StringAlignment.Center; 277 Font font = new Font(this.ParentGridView.Font, FontStyle.Bold); 278 graphics.DrawString(this.Title, font, Brushes.Black, 279 //rowBounds.Left+20,rowBounds.Top+rowBounds.Height/2,sf); 280 rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + 20, rowBounds.Top + rowBounds.Height / 2, sf); 281 282 283 int symbolX = rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + 5;//rowBounds.X + 5; 284 int symbolY =rowBounds.Y+ ((rowBounds.Height - 1) - 5) / 2-2; 285 if (Application.RenderWithVisualStyles) 286 { 287 288 VisualStyleRenderer glyphRenderer; 289 if (this.IsExpanded) 290 glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened); 291 else 292 glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed); 293 294 Rectangle glyphRectangle =new Rectangle(symbolX, symbolY, 10, 10); 295 296 glyphRenderer.DrawBackground(graphics, glyphRectangle); 297 298 } 299 else 300 { 301 int h = 8; 302 int w = 8; 303 int x = symbolX; 304 int y = symbolY; 305 graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h); 306 graphics.FillRectangle(new SolidBrush(Color.White), 307 x + 1, y + 1, w - 1, h - 1); 308 309 //画横线 310 graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), 311 x + 2, y + 4, x + w - 2, y + 4); 312 313 //画竖线 314 if (!this.IsExpanded) 315 graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), 316 x + 4, y + 2, x + 4, y + h - 2); 317 } 318 } 319 }GroupGridView和GroupGridViewRow