能分组的GridView
有天在想工作上的事的时候,看着.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进行一些设置,这些设置我都塞到了构造函数里面,大体上分三类,分别是操作设置,样式设置和部分属性或字段的赋值,代码如下
- public GroupGridView()
- {
- //对GridView的操作的设置
- this.AllowUserToAddRows = false;
- this.AllowUserToDeleteRows = false;
- this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
- //对GridView的样式设置
- this.EditMode = DataGridViewEditMode.EditProgrammatically;
- this.RowHeadersVisible = false;
- this.CellBorderStyle = DataGridViewCellBorderStyle.RaisedHorizontal;
- //对实例的部分成员赋值
- isGroupping = false;
- GroupRowTemplate = new GroupGridViewRow();
- }
其实GroupGridView要做的事就是能增加分组,在单击和双击分组行时作处理,还有就是数据绑定时把数据分组添加到GirdView里。那么就逐个方法来介绍
第一个是增加分组的
- public GroupGridViewRow CreateGroupGridViewRow(string title)
- {
- GroupGridViewRow group = this.GroupRowTemplate.Clone() as GroupGridViewRow;
- if (group == null)
- throw new NullReferenceException("组模板为空或者组模板类型不是GroupGridViewRow");
- group.Title = title;
- group.ParentGridView = this;
- group.CreateCells(this, group.Title);
- this.Rows.Add(group);
- group.CollapseGroup();
- return group;
- }
主要是按照模板拷贝一个GroupGridViewRow的实例,设置相应的属性后就把它添加到GirdView里面。
然后到鼠标对分组行的点击事件,单击展开/折叠图标就展示或隐藏该组的数据。双击分组行同样也达到这种显示状态切换的效果。
- protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
- {
- if (e.RowIndex == - ||e.ColumnIndex==-|| e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != )
- return;
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null && group.IsIconHit(e))
- group.Toggle();
- base.OnCellMouseDown(e);
- }
- protected override void OnCellDoubleClick(DataGridViewCellEventArgs e)
- {
- if (e.RowIndex == -||e.ColumnIndex==-)
- return;
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null )
- group.Toggle();
- base.OnCellDoubleClick(e);
- }
Toggle和IsIconHit方法在GroupGridViewRow里会定义,在介绍GroupGridViewRow会列出该方法的定义,在单击时就要通过IsIconHit判断鼠标是否点击到了图片,当然是点击中图标才会经行状态切换,而双击则不需要了。无论是双击和单击都要判断当前鼠标所在的行是分组行,如果是数据行的话就不会作任何操作了。
为了方便点绑定到数据,我重新定义了控件的DataSource属性。当分组字段信息(GroupFieldName属性)不存在时就会使用GridView默认的绑定数据,如果存在就会分组筛选出数据,然后逐个组去把行添加进去。
- public new object DataSource
- {
- get
- {
- if (isGroupping) return objDataSource;
- return base.DataSource;
- }
- set
- {
- if (string.IsNullOrEmpty(GroupFieldName)
- || string.IsNullOrWhiteSpace(GroupFieldName))
- {
- foreach (DataGridViewColumn col in this.Columns)
- col.SortMode = DataGridViewColumnSortMode.Automatic;
- base.DataSource = value;
- isGroupping = false;
- }
- else
- {
- foreach (DataGridViewColumn col in this.Columns)
- col.SortMode = DataGridViewColumnSortMode.NotSortable;
- if (value is IEnumerable)
- BindIEnumerableDataSource(value as IEnumerable);
- else if (value is DataTable)
- BindDataTableDataSouce(value as DataTable);
- else if (value is DataSet)
- BindDataSetDataSource(value as DataSet);
- else
- {
- throw new NotImplementedException("不支持此类型作数据源");
- }
- objDataSource = value;
- isGroupping = true;
- }
- }
- }
现在能自动绑定的数据源只能是可枚举的或DataTable,DataSet其实是把里面第一个DataTable作为数据源而已。不同类型的数据有相应的处理方法
- private void BindIEnumerableDataSource(IEnumerable enumerable)
- {
- IEnumerable iends = enumerable;
- if (iends == null) return;
- Type dsItemType = null;
- foreach (object item in iends)
- dsItemType = item.GetType();
- if (iends == null) return;
- PropertyInfo proInfo = dsItemType.GetProperty(GroupFieldName);
- Dictionary<string, List<object>> groupDataSource = new Dictionary<string, List<object>>();
- foreach (object item in iends)
- {
- string tempStr = proInfo.GetValue(item, null).ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<object>();
- groupDataSource[tempStr].Add(item);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<object>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (object celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(dsItemType.GetProperty(colName).GetValue(celli, null));
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- group.CollapseGroup();
- }
- }
对于可枚举的数据源,我就通过反射把相应的属性的值都拿出来填到DataGridViewRow里面。
- private void BindDataTableDataSouce(DataTable table)
- {
- Dictionary<string, List<DataRow>> groupDataSource = new Dictionary<string, List<DataRow>>();
- foreach (DataRow row in table.Rows)
- {
- string tempStr = row[GroupFieldName].ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<DataRow>();
- groupDataSource[tempStr].Add(row);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<DataRow>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (DataRow celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(celli[colName]);
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- group.CollapseGroup();
- }
- }
DataTable的绑定也类似,更方便的是DataTable不需要用反射了,直接通过行列的访问就可以了。
GruopGridView介绍就到此结束,下面则介绍另一个类GroupGridViewRow,它是继承DataGridViewRow。而GDI+的描绘都是在这个方法里面。也是先看看里面的成员
成员名称 |
数据类型 |
修饰符 |
描述 |
IsExpanded |
bool |
public |
分组的状态,是展开还是折叠 |
Title |
string |
public |
分组的标题,通常是分组的数据 |
ParentGridView |
GroupGridView |
public |
分组行所在的GridView |
groupRows |
List<DataGridViewRow> |
private |
此分组包含的数据行 |
那么一个GroupGridViewRow的要处理的事就有以下几个,对分组下的数据行的状态控制,判断鼠标单击是否命中图标,增加一行数据到该分组下,还有最重要的就是描绘出分组行的外观。
先列举数据行的状态控制方法,ExpandGroup()展开分组,CollapseGroup()折叠分组,还有Toggle()切换
- public void ExpandGroup()
- {
- IsExpanded = true;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = true;
- }
- public void CollapseGroup()
- {
- IsExpanded = false;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = false;
- }
- public void Toggle()
- {
- if (IsExpanded)
- CollapseGroup();
- else
- ExpandGroup();
- }
实际上就是通过遍历groupRows集合,改变其显示状态而已。
判断单击图标的方法如下
- public bool IsIconHit(DataGridViewCellMouseEventArgs e)
- {
- Rectangle groupBound = this.ParentGridView.GetRowDisplayRectangle(e.RowIndex, false);
- if (
- e.X > groupBound.Left + &&
- e.X < groupBound.Left + &&
- e.Y > ((groupBound.Height - ) - ) / - &&
- e.Y < ((groupBound.Height - ) - ) / + -
- )
- return true;
- return false;
- }
主要是通过鼠标指针当前所在的坐标是不是在图标的范围以内,那个范围有点不好把握,要通过DataGridView的GetRowDisplayRectangle方法得到分组行的矩形,Left,X这两个属性有点分不清了。
增加数据行的方法如下
- public DataGridViewRow AddRowToGroup(params object[] values)
- {
- DataGridViewRow row = this.ParentGridView.RowTemplate.Clone() as DataGridViewRow;
- if (row == null) throw new NullReferenceException("行模板为空或者组模板类型不是DataGridViewRow");
- row.CreateCells(this.ParentGridView, values);
- this.ParentGridView.Rows.Add(row);
- this.groupRows.Add(row);
- return row;
- }
也复杂,通过DataGridView的普通数据行(不是分组行)的目标拷贝,填上数据,然后分别添加到这个分组的数据行集合中和GridView中。
最后到描绘分组行的方法,重写Paint方法,这个基本上是参考园友老虎哥的代码的,也作了一些小的调整,解决了水平滚动之后文字和图标有重影的问题,所以老虎哥看见了不要怪哈!
- protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow)
- {
- int holdWidth = this.ParentGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible);
- Color backgroudColor;
- if (this.Selected)
- backgroudColor = this.ParentGridView.DefaultCellStyle.SelectionBackColor;
- else
- backgroudColor = this.ParentGridView.DefaultCellStyle.BackColor;
- using (Brush backgroudBrush = new SolidBrush(backgroudColor))
- {
- graphics.FillRectangle(backgroudBrush, rowBounds.X,rowBounds.Y,holdWidth,rowBounds.Height);
- }
- using (Brush bottomLineBrush=new SolidBrush(Color.FromKnownColor( KnownColor.GradientActiveCaption)))
- {
- graphics.FillRectangle(bottomLineBrush, rowBounds.Left, rowBounds.Top + rowBounds.Height-, holdWidth, );
- }
- StringFormat sf = new StringFormat();
- sf.LineAlignment = StringAlignment.Center;
- Font font = new Font(this.ParentGridView.Font, FontStyle.Bold);
- graphics.DrawString(this.Title, font, Brushes.Black,
- rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + , rowBounds.Top + rowBounds.Height / , sf);
- int symbolX = rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + ;
- int symbolY =rowBounds.Y+ ((rowBounds.Height - ) - ) / -;
- if (Application.RenderWithVisualStyles)
- {
- VisualStyleRenderer glyphRenderer;
- if (this.IsExpanded)
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
- else
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
- Rectangle glyphRectangle =new Rectangle(symbolX, symbolY, , );
- glyphRenderer.DrawBackground(graphics, glyphRectangle);
- }
- else
- {
- int h = ;
- int w = ;
- int x = symbolX;
- int y = symbolY;
- graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
- graphics.FillRectangle(new SolidBrush(Color.White),
- x + , y + , w - , h - );
- //画横线
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + w - , y + );
- //画竖线
- if (!this.IsExpanded)
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + , y + h - );
- }
- }
我自己写出来的话还是有点棘手的,绘制对于我自己而言就分了三部分,分组栏的背景边线描绘,文字的填写,还有图标的描绘,而图标在这里很好的考虑那个显示效果,分了渐变效果和单色效果。我也该多学习学习。
最后列一下控件的使用,列的设置分组设置,数据绑定如下,CreateColumn是我定义的方法,主要是构造一个新的列,对其进行设置之后就添加到GridView里面。
- groupGridView21.GroupFieldName = "name";
- CreateColumn("id", "id", groupGridView21);
- CreateColumn("invdate", "invdate", groupGridView21);
- CreateColumn("note", "note", groupGridView21);
- CreateColumn("amount", "amount", groupGridView21);
- CreateColumn("tax", "tax", groupGridView21);
- CreateColumn("total", "total", groupGridView21);
- CreateColumn("name", "name", groupGridView21);
- groupGridView21.DataSource = datasource;
效果就这样,数据我完全拿了那个jqGrid的Demo里面的数据
控件的缺点还是跟老虎哥的控件类似,要把显示的列逐个添加,不支持自动添加列,并且分组显示的时候不能对列经行排序。最后附上控件的完整源码,介绍完毕,谢谢!
- public class GroupGridView:DataGridView
- {
- public string GroupFieldName { get; set; }
- protected object objDataSource;
- public GroupGridViewRow GroupRowTemplate { get;protected set; }
- private bool isGroupping;
- public GroupGridView()
- {
- //对GridView的操作的设置
- this.AllowUserToAddRows = false;
- this.AllowUserToDeleteRows = false;
- this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
- //对GridView的样式设置
- this.EditMode = DataGridViewEditMode.EditProgrammatically;
- this.RowHeadersVisible = false;
- this.CellBorderStyle = DataGridViewCellBorderStyle.RaisedHorizontal;
- //对实例的部分成员赋值
- isGroupping = false;
- GroupRowTemplate = new GroupGridViewRow();
- }
- public GroupGridViewRow CreateGroupGridViewRow(string title)
- {
- GroupGridViewRow group = this.GroupRowTemplate.Clone() as GroupGridViewRow;
- if (group == null)
- throw new NullReferenceException("组模板为空或者组模板类型不是GroupGridViewRow");
- group.Title = title;
- group.ParentGridView = this;
- group.CreateCells(this, group.Title);
- this.Rows.Add(group);
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- return group;
- }
- public new object DataSource
- {
- get
- {
- if (isGroupping) return objDataSource;
- return base.DataSource;
- }
- set
- {
- if (string.IsNullOrEmpty(GroupFieldName)
- || string.IsNullOrWhiteSpace(GroupFieldName))
- {
- foreach (DataGridViewColumn col in this.Columns)
- col.SortMode = DataGridViewColumnSortMode.Automatic;
- base.DataSource = value;
- isGroupping = false;
- }
- else
- {
- foreach (DataGridViewColumn col in this.Columns)
- col.SortMode = DataGridViewColumnSortMode.NotSortable;
- if (value is IEnumerable)
- BindIEnumerableDataSource(value as IEnumerable);
- else if (value is DataTable)
- BindDataTableDataSouce(value as DataTable);
- else if (value is DataSet)
- BindDataSetDataSource(value as DataSet);
- else
- {
- throw new NotImplementedException("不支持此类型作数据源");
- }
- objDataSource = value;
- isGroupping = true;
- }
- }
- }
- protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
- {
- if (e.RowIndex == - ||e.ColumnIndex==-|| e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != )
- return;
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null && group.IsIconHit(e))
- group.Toggle();
- base.OnCellMouseDown(e);
- }
- protected override void OnCellDoubleClick(DataGridViewCellEventArgs e)
- {
- if (e.RowIndex == -||e.ColumnIndex==-)
- return;
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null )
- group.Toggle();
- base.OnCellDoubleClick(e);
- }
- private void BindIEnumerableDataSource(IEnumerable enumerable)
- {
- IEnumerable iends = enumerable;
- if (iends == null) return;
- Type dsItemType = null;
- foreach (object item in iends)
- dsItemType = item.GetType();
- if (iends == null) return;
- PropertyInfo proInfo = dsItemType.GetProperty(GroupFieldName);
- Dictionary<string, List<object>> groupDataSource = new Dictionary<string, List<object>>();
- foreach (object item in iends)
- {
- string tempStr = proInfo.GetValue(item, null).ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<object>();
- groupDataSource[tempStr].Add(item);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<object>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (object celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(dsItemType.GetProperty(colName).GetValue(celli, null));
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- }
- }
- private void BindDataTableDataSouce(DataTable table)
- {
- Dictionary<string, List<DataRow>> groupDataSource = new Dictionary<string, List<DataRow>>();
- foreach (DataRow row in table.Rows)
- {
- string tempStr = row[GroupFieldName].ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<DataRow>();
- groupDataSource[tempStr].Add(row);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<DataRow>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (DataRow celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(celli[colName]);
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- }
- }
- private void BindDataSetDataSource(DataSet dataset)
- {
- if (dataset == null || dataset.Tables.Count == null) return;
- BindDataTableDataSouce(dataset.Tables[]);
- }
- }
- public class GroupGridViewRow:DataGridViewRow
- {
- public bool IsExpanded { get;private set; }
- public string Title { get; set; }
- public GroupGridView ParentGridView { get; set; }
- private List<DataGridViewRow> groupRows { get; set; }
- public GroupGridViewRow()
- {
- IsExpanded = false;
- groupRows = new List<DataGridViewRow>();
- }
- public void ExpandGroup()
- {
- IsExpanded = true;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = true;
- }
- public void CollapseGroup()
- {
- IsExpanded = false;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = false;
- }
- public void Toggle()
- {
- if (IsExpanded)
- CollapseGroup();
- else
- ExpandGroup();
- }
- public bool IsIconHit(DataGridViewCellMouseEventArgs e)
- {
- Rectangle groupBound = this.ParentGridView.GetRowDisplayRectangle(e.RowIndex, false);
- if (
- e.X > groupBound.Left + &&
- e.X < groupBound.Left + &&
- e.Y > ((groupBound.Height - ) - ) / - &&
- e.Y < ((groupBound.Height - ) - ) / + -
- )
- return true;
- return false;
- }
- public DataGridViewRow AddRowToGroup(params object[] values)
- {
- DataGridViewRow row = this.ParentGridView.RowTemplate.Clone() as DataGridViewRow;
- if (row == null) throw new NullReferenceException("行模板为空或者组模板类型不是DataGridViewRow");
- row.CreateCells(this.ParentGridView, values);
- this.ParentGridView.Rows.Add(row);
- this.groupRows.Add(row);
- return row;
- }
- protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow)
- {
- int holdWidth = this.ParentGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible);
- Color backgroudColor;
- if (this.Selected)
- backgroudColor = this.ParentGridView.DefaultCellStyle.SelectionBackColor;
- else
- backgroudColor = this.ParentGridView.DefaultCellStyle.BackColor;
- using (Brush backgroudBrush = new SolidBrush(backgroudColor))
- {
- graphics.FillRectangle(backgroudBrush, rowBounds.X,rowBounds.Y,holdWidth,rowBounds.Height);
- }
- using (Brush bottomLineBrush=new SolidBrush(Color.FromKnownColor( KnownColor.GradientActiveCaption)))
- {
- graphics.FillRectangle(bottomLineBrush, rowBounds.Left, rowBounds.Top + rowBounds.Height-, holdWidth, );
- }
- StringFormat sf = new StringFormat();
- sf.LineAlignment = StringAlignment.Center;
- Font font = new Font(this.ParentGridView.Font, FontStyle.Bold);
- graphics.DrawString(this.Title, font, Brushes.Black,
- //rowBounds.Left+20,rowBounds.Top+rowBounds.Height/2,sf);
- rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + , rowBounds.Top + rowBounds.Height / , sf);
- int symbolX = rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + ;//rowBounds.X + 5;
- int symbolY =rowBounds.Y+ ((rowBounds.Height - ) - ) / -;
- if (Application.RenderWithVisualStyles)
- {
- VisualStyleRenderer glyphRenderer;
- if (this.IsExpanded)
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
- else
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
- Rectangle glyphRectangle =new Rectangle(symbolX, symbolY, , );
- glyphRenderer.DrawBackground(graphics, glyphRectangle);
- }
- else
- {
- int h = ;
- int w = ;
- int x = symbolX;
- int y = symbolY;
- graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
- graphics.FillRectangle(new SolidBrush(Color.White),
- x + , y + , w - , h - );
- //画横线
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + w - , y + );
- //画竖线
- if (!this.IsExpanded)
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + , y + h - );
- }
- }
- }
GroupGridView和GroupGridViewRow
补充部分
上面说的那个可分组GroupGridView有一个缺憾比较致命的,就是不能排序,刚好最近工作用上了,那有工作的压力逼着就把那个排序的功能都加了上去了。
要加这排序的,GroupGridView和GroupGridVIewRow两个类都要改,先介绍大概的思想,再分别介绍两个类。
排序的时候不能按照往常那样比较排序,因为在GridView里面含有显示组名的,不包含数据,用它来排序会导致整个GridView会乱的。因此排序的时候需要让各个组各自排列。
这时GroupGirdView最好就收集一下各个分组行,在排序的时候让各个分组排列
多增加两个字段
- protected List<GroupGridViewRow> groupCollection;
- private SortOrder mySortOrder;
在绑定数据源的时候取消对各列的排列限制
- //foreach (DataGridViewColumn col in this.Columns)
- // col.SortMode = DataGridViewColumnSortMode.NotSortable;
单击单元格时也要开放一下,否则控件会忽略对行号为-1,就是列标题的处理
- protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
- {
- if (e.RowIndex == - || e.ColumnIndex == - || e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != )
- {
- base.OnCellMouseDown(e);
- return;
- }
- //………
- }
跟排序比较相关的就是重写这个方法
- public override void Sort(DataGridViewColumn dataGridViewColumn, System.ComponentModel.ListSortDirection direction)
- {
- //base.Sort(dataGridViewColumn, direction);
- foreach (GroupGridViewRow row in groupCollection)
- row.SortByProtery(dataGridViewColumn, mySortOrder);
- mySortOrder = mySortOrder == System.Windows.Forms.SortOrder.Ascending ?
- System.Windows.Forms.SortOrder.Descending :
- System.Windows.Forms.SortOrder.Ascending;
- }
到GroupGirdViewRow类了,这个类的改动就是按照上面的代码那样外放了一个方法,那方法就是对组内各行按升序或降序排序。
- public void SortByProtery(DataGridViewColumn proteryName, SortOrder order)
- {
- int colIndex = this.ParentGridView.Columns.IndexOf(proteryName);
- groupRows.Sort(new Comparison<DataGridViewRow>((r1, r2) =>
- {
- object value1 = r1.Cells[proteryName.Name].Value;
- object value2 = r2.Cells[proteryName.Name].Value;
- if (order == SortOrder.Descending) return CompareDESC(value1, value2);
- else return CompareASC(value1, value2);
- }));
- int groupIndex = this.ParentGridView.Rows.IndexOf(this);
- foreach (DataGridViewRow row in groupRows)
- {
- this.ParentGridView.Rows.Remove(row);
- this.ParentGridView.Rows.Insert(groupIndex + , row);
- }
- }
- private int CompareASC(object obj1, object obj2)
- {
- decimal decTmep;
- if (decimal.TryParse(obj1.ToString(), out decTmep) && decimal.TryParse(obj2.ToString(), out decTmep))
- return decimal.Compare(Convert.ToDecimal(obj1), Convert.ToDecimal(obj2));
- if (obj1 is DateTime && obj2 is DateTime)
- return DateTime.Compare(Convert.ToDateTime(obj1), Convert.ToDateTime(obj2));
- return string.Compare(obj1.ToString(), obj2.ToString());
- }
- private int CompareDESC(object obj1, object obj2)
- {
- decimal decTmep;
- if (decimal.TryParse(obj1.ToString(), out decTmep) && decimal.TryParse(obj2.ToString(), out decTmep))
- return decimal.Compare(Convert.ToDecimal(obj1), Convert.ToDecimal(obj2)) * -;
- if (obj1 is DateTime && obj2 is DateTime)
- return DateTime.Compare(Convert.ToDateTime(obj1), Convert.ToDateTime(obj2)) * -;
- return string.Compare(obj1.ToString(), obj2.ToString()) * -;
- }
其实还比较笨拙的,按自定义的排序方法对各个列在List里面拍完序之后就按照List里的新顺序重新插入到GroupGridView里面。好了,排序的功能在这里草草介绍完了,下面则是新的控件代码,除了增加了这个功能之外,还修复了一些小错误。
- public class GroupGridView:DataGridView
- {
- public string GroupFieldName { get; set; }
- protected object objDataSource;
- public GroupGridViewRow GroupRowTemplate { get;protected set; }
- private bool isGroupping;
- protected List<GroupGridViewRow> groupCollection;
- private SortOrder mySortOrder;
- public GroupGridView()
- {
- //对GridView的操作的设置
- this.AllowUserToAddRows = false;
- this.AllowUserToDeleteRows = false;
- this.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
- //对GridView的样式设置
- this.EditMode = DataGridViewEditMode.EditProgrammatically;
- this.RowHeadersVisible = false;
- this.CellBorderStyle = DataGridViewCellBorderStyle.RaisedHorizontal;
- //对实例的部分成员赋值
- isGroupping = false;
- GroupRowTemplate = new GroupGridViewRow();
- groupCollection = new List<GroupGridViewRow>();
- }
- public GroupGridViewRow CreateGroupGridViewRow(string title)
- {
- GroupGridViewRow group = this.GroupRowTemplate.Clone() as GroupGridViewRow;
- if (group == null)
- throw new NullReferenceException("组模板为空或者组模板类型不是GroupGridViewRow");
- group.Title = title;
- group.ParentGridView = this;
- group.CreateCells(this, group.Title);
- this.Rows.Add(group);
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- return group;
- }
- public new object DataSource
- {
- get
- {
- if (isGroupping) return objDataSource;
- return base.DataSource;
- }
- set
- {
- if (string.IsNullOrEmpty(GroupFieldName)
- || string.IsNullOrWhiteSpace(GroupFieldName))
- {
- foreach (DataGridViewColumn col in this.Columns)
- col.SortMode = DataGridViewColumnSortMode.Automatic;
- base.DataSource = value;
- isGroupping = false;
- }
- else
- {
- //2013-10-13增加排序功能所注释
- //foreach (DataGridViewColumn col in this.Columns)
- // col.SortMode = DataGridViewColumnSortMode.NotSortable;
- this.Rows.Clear();
- this.groupCollection.Clear();
- if (value is IEnumerable)
- BindIEnumerableDataSource(value as IEnumerable);
- else if (value is DataTable)
- BindDataTableDataSouce(value as DataTable);
- else if (value is DataSet)
- BindDataSetDataSource(value as DataSet);
- else
- {
- throw new NotImplementedException("不支持此类型作数据源");
- }
- objDataSource = value;
- isGroupping = true;
- }
- }
- }
- protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
- {
- if (e.RowIndex == - || e.ColumnIndex == - || e.Button != System.Windows.Forms.MouseButtons.Left || e.Clicks != )
- {
- base.OnCellMouseDown(e);
- return;
- }
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null && group.IsIconHit(e))
- group.Toggle();
- base.OnCellMouseDown(e);
- }
- protected override void OnCellDoubleClick(DataGridViewCellEventArgs e)
- {
- if (e.RowIndex == -||e.ColumnIndex==-)
- return;
- GroupGridViewRow group = this.Rows[e.RowIndex] as GroupGridViewRow;
- if (group != null )
- group.Toggle();
- base.OnCellDoubleClick(e);
- }
- public override void Sort(DataGridViewColumn dataGridViewColumn, System.ComponentModel.ListSortDirection direction)
- {
- //base.Sort(dataGridViewColumn, direction);
- foreach (GroupGridViewRow row in groupCollection)
- row.SortByProtery(dataGridViewColumn, mySortOrder);
- mySortOrder = mySortOrder == System.Windows.Forms.SortOrder.Ascending ?
- System.Windows.Forms.SortOrder.Descending :
- System.Windows.Forms.SortOrder.Ascending;
- }
- private void BindIEnumerableDataSource(IEnumerable enumerable)
- {
- IEnumerable iends = enumerable;
- if (iends == null) return;
- Type dsItemType = null;
- foreach (object item in iends)
- dsItemType = item.GetType();
- if (iends == null) return;
- PropertyInfo proInfo = dsItemType.GetProperty(GroupFieldName);
- Dictionary<string, List<object>> groupDataSource = new Dictionary<string, List<object>>();
- foreach (object item in iends)
- {
- string tempStr = proInfo.GetValue(item, null).ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<object>();
- groupDataSource[tempStr].Add(item);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<object>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (object celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(dsItemType.GetProperty(colName).GetValue(celli, null));
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- }
- }
- private void BindDataTableDataSouce(DataTable table)
- {
- Dictionary<string, List<DataRow>> groupDataSource = new Dictionary<string, List<DataRow>>();
- foreach (DataRow row in table.Rows)
- {
- string tempStr = row[GroupFieldName].ToString();
- if (!groupDataSource.ContainsKey(tempStr))
- groupDataSource[tempStr] = new List<DataRow>();
- groupDataSource[tempStr].Add(row);
- }
- List<string> colFildNames = new List<string>(this.Columns.Count);
- foreach (DataGridViewColumn col in this.Columns)
- colFildNames.Add(col.DataPropertyName);
- GroupGridViewRow group = null;
- List<object> datas = new List<object>(colFildNames.Count);
- foreach (KeyValuePair<string, List<DataRow>> gi in groupDataSource)
- {
- group = CreateGroupGridViewRow(gi.Key);
- foreach (DataRow celli in gi.Value)
- {
- foreach (string colName in colFildNames)
- {
- datas.Add(celli[colName]);
- }
- group.AddRowToGroup(datas.ToArray());
- datas.Clear();
- }
- //do
- //{
- // group.Toggle();
- //} while (group.IsExpanded);
- group.CollapseGroup();
- }
- }
- private void BindDataSetDataSource(DataSet dataset)
- {
- if (dataset == null || dataset.Tables.Count == null) return;
- BindDataTableDataSouce(dataset.Tables[]);
- }
- }
- public class GroupGridViewRow:DataGridViewRow
- {
- public bool IsExpanded { get;private set; }
- public string Title { get; set; }
- public GroupGridView ParentGridView { get; set; }
- private List<DataGridViewRow> groupRows { get; set; }
- public GroupGridViewRow()
- {
- IsExpanded = false;
- groupRows = new List<DataGridViewRow>();
- }
- public void ExpandGroup()
- {
- IsExpanded = true;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = true;
- }
- public void CollapseGroup()
- {
- IsExpanded = false;
- foreach (DataGridViewRow row in groupRows)
- row.Visible = false;
- }
- public void Toggle()
- {
- if (IsExpanded)
- CollapseGroup();
- else
- ExpandGroup();
- }
- public bool IsIconHit(DataGridViewCellMouseEventArgs e)
- {
- Rectangle groupBound = this.ParentGridView.GetRowDisplayRectangle(e.RowIndex, false);
- if (
- e.X > groupBound.Left + &&
- e.X < groupBound.Left + &&
- e.Y > ((groupBound.Height - ) - ) / - &&
- e.Y < ((groupBound.Height - ) - ) / + -
- )
- return true;
- return false;
- }
- public void SortByProtery(DataGridViewColumn proteryName, SortOrder order)
- {
- int colIndex = this.ParentGridView.Columns.IndexOf(proteryName);
- groupRows.Sort(new Comparison<DataGridViewRow>((r1, r2) =>
- {
- object value1 = r1.Cells[proteryName.Name].Value;
- object value2 = r2.Cells[proteryName.Name].Value;
- //object value1 = r1.Cells[colIndex].Value;
- //object value2 = r2.Cells[colIndex].Value;
- if (order == SortOrder.Descending) return CompareDESC(value1, value2);
- else return CompareASC(value1, value2);
- }));
- int groupIndex = this.ParentGridView.Rows.IndexOf(this);
- foreach (DataGridViewRow row in groupRows)
- {
- this.ParentGridView.Rows.Remove(row);
- this.ParentGridView.Rows.Insert(groupIndex + , row);
- }
- }
- private int CompareASC(object obj1, object obj2)
- {
- //if (obj1 is decimal && obj2 is decimal)//2013-10-14 正确判断数值类型
- decimal decTmep;
- if (decimal.TryParse(obj1.ToString(), out decTmep) && decimal.TryParse(obj2.ToString(), out decTmep))
- return decimal.Compare(Convert.ToDecimal(obj1), Convert.ToDecimal(obj2));
- if (obj1 is DateTime && obj2 is DateTime)
- return DateTime.Compare(Convert.ToDateTime(obj1), Convert.ToDateTime(obj2));
- return string.Compare(obj1.ToString(), obj2.ToString());
- }
- private int CompareDESC(object obj1, object obj2)
- {
- //if (obj1 is decimal && obj2 is decimal)//2013-10-14 正确判断数值类型
- decimal decTmep;
- if (decimal.TryParse(obj1.ToString(), out decTmep) && decimal.TryParse(obj2.ToString(), out decTmep))
- return decimal.Compare(Convert.ToDecimal(obj1), Convert.ToDecimal(obj2)) * -;
- if (obj1 is DateTime && obj2 is DateTime)
- return DateTime.Compare(Convert.ToDateTime(obj1), Convert.ToDateTime(obj2)) * -;
- return string.Compare(obj1.ToString(), obj2.ToString()) * -;
- }
- public DataGridViewRow AddRowToGroup(params object[] values)
- {
- DataGridViewRow row = this.ParentGridView.RowTemplate.Clone() as DataGridViewRow;
- if (row == null) throw new NullReferenceException("行模板为空或者组模板类型不是DataGridViewRow");
- row.CreateCells(this.ParentGridView, values);
- this.ParentGridView.Rows.Add(row);
- this.groupRows.Add(row);
- return row;
- }
- protected override void Paint(System.Drawing.Graphics graphics, System.Drawing.Rectangle clipBounds, System.Drawing.Rectangle rowBounds, int rowIndex, DataGridViewElementStates rowState, bool isFirstDisplayedRow, bool isLastVisibleRow)
- {
- int holdWidth = this.ParentGridView.Columns.GetColumnsWidth(DataGridViewElementStates.Visible);
- Color backgroudColor;
- if (this.Selected)
- backgroudColor = this.ParentGridView.DefaultCellStyle.SelectionBackColor;
- else
- backgroudColor = this.ParentGridView.DefaultCellStyle.BackColor;
- using (Brush backgroudBrush = new SolidBrush(backgroudColor))
- {
- graphics.FillRectangle(backgroudBrush, rowBounds.X,rowBounds.Y,holdWidth,rowBounds.Height);
- }
- using (Brush bottomLineBrush=new SolidBrush(Color.FromKnownColor( KnownColor.GradientActiveCaption)))
- {
- graphics.FillRectangle(bottomLineBrush, rowBounds.Left, rowBounds.Top + rowBounds.Height-, holdWidth, );
- }
- StringFormat sf = new StringFormat();
- sf.LineAlignment = StringAlignment.Center;
- Font font = new Font(this.ParentGridView.Font, FontStyle.Bold);
- graphics.DrawString(this.Title, font, Brushes.Black,
- //rowBounds.Left+20,rowBounds.Top+rowBounds.Height/2,sf);
- rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + , rowBounds.Top + rowBounds.Height / , sf);
- int symbolX = rowBounds.Left - this.ParentGridView.HorizontalScrollingOffset + ;//rowBounds.X + 5;
- int symbolY =rowBounds.Y+ ((rowBounds.Height - ) - ) / -;
- if (Application.RenderWithVisualStyles)
- {
- VisualStyleRenderer glyphRenderer;
- if (this.IsExpanded)
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Opened);
- else
- glyphRenderer = new VisualStyleRenderer(VisualStyleElement.TreeView.Glyph.Closed);
- Rectangle glyphRectangle =new Rectangle(symbolX, symbolY, , );
- glyphRenderer.DrawBackground(graphics, glyphRectangle);
- }
- else
- {
- int h = ;
- int w = ;
- int x = symbolX;
- int y = symbolY;
- graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
- graphics.FillRectangle(new SolidBrush(Color.White),
- x + , y + , w - , h - );
- //画横线
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + w - , y + );
- //画竖线
- if (!this.IsExpanded)
- graphics.DrawLine(new Pen(new SolidBrush(Color.Black)),
- x + , y + , x + , y + h - );
- }
- }
- }
GroupGridView和GroupGridViewRow
能分组的GridView的更多相关文章
- Android 使用ContentProvider扫描手机中的图片,仿微信显示本地图片效果
版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/1873 ...
- Windows 8实例教程系列 - 数据绑定高级实例
原文:Windows 8实例教程系列 - 数据绑定高级实例 上篇Windows 8实例教程系列 - 数据绑定基础实例中,介绍Windows 8应用开发数据绑定基础,其中包括一些简单的数据绑定控件的使用 ...
- 扩展GridView控件——为内容项添加拖放及分组功能
引言 相信大家对GridView都不陌生,是非常有用的控件,用于平铺有序的显示多个内容项.打开任何WinRT应用或者是微软合作商的网站,都会在APP中发现GridView的使用.“Tiles”提供了一 ...
- Group GridView:用于.Net的分组显示的GridView
我的项目需要一个可以分组显示的GridView,我不会写,上网找了一圈,最终在国外的网站上找到的这个,比较符合我的要求,但它的分页得重写,它写了能分页,但我发现它的分页功能事实上并没有实现,也不知道是 ...
- Dev GridView 获取选中分组下的所有数据行 z
现在要在DevExpress 的GridView 中实现这样一个功能.就是判断当前的选中行是否是分组行,如果是的话就要获取该分组下的所有数据信息. 如下图(当选中红框中的分组行事.程序要获取该分组下的 ...
- 重新想象 Windows 8 Store Apps (12) - 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示
原文:重新想象 Windows 8 Store Apps (12) - 控件之 GridView 特性: 拖动项, 项尺寸可变, 分组显示 [源码下载] 重新想象 Windows 8 Store Ap ...
- GridControl/GridView的分组操作
今天在模块编写中碰到了对表格的分组,特意在这里把它记录下来. 一.背景:Dev14.1.3,GridControl,.NET4.0+C# 二.过程 1.GridControl设计 一共添加4列:在下面 ...
- Winform中GridView分组排序实现功能
由于客户最近要扩充公司的业务,之前基于Winform+web开发混合式的系统已经不能满足他们的需求,需要从新对系统进行分区处理. 考虑到系统模块里面用到的GridView视图比较多,我就结合了DevE ...
- 对Dev的GridControl/GridView控件进行分组并展开操作
今天在模块编写中碰到了对表格的分组,特意在这里把它记录下来. 一.背景:Dev14.1.3,GridControl,.NET4.0+C# 二.过程 1.GridControl设计 一共添加4列:在下面 ...
随机推荐
- 虚拟化平台cloudstack(7)——新版本的调试
调试环境 ubuntu 12.04 JDK1.7 apache-maven-3.10 eclipse 4.2 Juno mysql 5 源码下载及调试 上面的几个软件在上一篇中已经介绍了. 在新的版本 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题
从这篇文章中我们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不同用户的请求都来自同一个微信服务器,这使得常规的Session无法使用(始终面对同一个请求对象,况且还有对方服务器Co ...
- 每天一个linux命令(56):netstat命令
netstat命令用于显示与IP.TCP.UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况.netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UD ...
- JavaScript练习之for循环语句
for循环四要素:初始条件.循环条件.循环体.状态改变. 1.for(var a=i;i<=aa;i++) { 循环体(例sum=sum+i sum是输出的) } 例题 1-20关没关一分 2 ...
- 将不确定变成确定~Uri文本文件不用浏览器自动打开,而是下载到本地
回到目录 这个标题有点长,简单来说就是,对于一个文件下载来说,是否可以提示用户,让它去保存,而不是将它在浏览器中打开,在浏览器中打开有个致命问题,那就是,如果你的页面编码和文件的编码不一致时,打开的就 ...
- Lua 协程coroutine
协程和一般多线程的区别是,一般多线程由系统决定该哪个线程执行,是抢占式的,而协程是由每个线程自己决定自己什么时候不执行,并把执行权主动交给下一个线程. 协程是用户空间线程,操作系统其存在一无所知,所以 ...
- [Java面试十二]数据库概念相关
1. 什么是存储过程?它有什么优点? 答:存储过程是一组予编译的SQL语句,它的优点有: 允许模块化程序设计,就是说只需要创建一次过程,以后在程序中就可以调用该过程任意次. 允许更快执 ...
- [CSS]复选框单选框与文字对齐问题的研究与解决.
前言:今天碰到的这个问题, 恰好找到一个很好的博文, 在这里转载过来 学习下. 原文地址:复选框单选框与文字对齐问题的研究与解决. 目前中文网站上面的文字,就我的个人感觉而言,绝大多数网站的主流文字大 ...
- Android笔记——探究活动
1.活动是什么 活动(Activity)是最容易吸引到用户的地方了,它是一种可以包含用户界面的组件,主要用于和用户进行交互.一个应用程序中可以包含零个或多个活动,但不包含任何活动的应用程序 ...
- C# string.format、string.connect和+=运算 效率计算
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Stri ...