DataGridView控件
DataGridView是用于Windows Froms 2.0的新网格控件。它可以取代先前版本中DataGrid控件,它易于使用并高度可定制,支持很多我们的用户需要的特性。
关于本文档:
本文档不准备面面俱到地介绍DataGridView,而是着眼于深入地介绍一些技术点的高级特性。
本文档按逻辑分为5个章节,首先是结构和特性的概览,其次是内置的列/单元格类型的介绍,再次是数据操作相关的内容,然后是主要特性的综述,最后是最佳实践。
大部分章节含有一个“Q & A”部分,来回答该章节相关的一些常见问题。注意,某些问题会由于知识点的关联性重复出现在多个章节。这些问题、答案及其附带的示例代码都包含在本文档的附录部分。
内容
1 何为DataGridView.. 4
1.1 DataGridView和DataGrid 之间的区别... 4
1.2 DataGridView的亮点... 5
2 DataGridView的结构... 6
2.1 结构元素... 6
2.2 单元格和组... 6
2.3 DataGridView的单元格... 6
2.3.1 DataGridViewCell的工作机制... 7
2.4 DataGridView的列... 9
2.5 DataGridView的编辑控件... 9
2.6 DataGridView的行... 10
3 列/单元格类型揭密... 11
3.1 DataGridViewTextBoxColumn. 11
3.2 DataGridViewCheckBoxColumn. 12
3.3 DataGridViewImageColumn. 12
3.4 DataGridViewButtonColumn. 13
3.5 DataGridViewComboBoxColumn. 13
3.5.1 DataError与ComboBox列... 13
3.6 DataGridViewLinkColumn. 14
4 操作数据... 15
4.1 数据输入和验证的相关事件... 15
4.1.1 数据验证相关事件的顺序... 15
4.1.2 验证数据... 15
4.1.3 在新行中的数据输入... 16
4.2 关于Null值... 19
4.2.1 NullValue属性... 19
4.2.2 DataSourceNullValue属性... 19
4.3 DataError事件... 20
4.4 数据绑定模式... 21
4.4.1 非绑定模式... 21
4.4.2 绑定模式... 21
4.4.3 虚拟模式... 22
4.4.4 混合模式... 22
5 Overview of features. 24
5.1 Styling. 24
5.1.1 The DataGridViewCellStyle Class. 24
5.1.2 Using DataGridViewCellStyle Objects. 24
5.1.3 Style Inheritance. 25
5.1.4 Setting Styles Dynamically. 28
5.2 Custom painting. 28
5.2.1 Paint Parts. 28
5.2.2 Row Pre Paint and Post Paint 29
5.3 Autosizing. 30
5.3.1 Sizing Options in the Windows Forms DataGridView Control 30
5.3.2 Resizing with the Mouse. 31
5.3.3 Automatic Sizing. 32
5.3.4 Programmatic Resizing. 33
5.3.5 Customizing Content-based Sizing Behavior. 34
5.3.6 Content-based Sizing Options. 34
5.4 Selection modes. 34
5.4.1 Programmatic Selection. 35
5.5 Scrolling. 35
5.5.1 Scroll event 35
5.5.2 Scroll bars. 35
5.5.3 Scrolling Properties. 36
5.6 Sorting. 36
5.6.1 Programmatic Sorting. 37
5.6.2 Custom Sorting. 38
5.7 Border styles. 39
5.7.1 Standard Border Styles. 39
5.7.2 Advanced Border Styles. 39
5.8 Enter-Edit modes. 40
5.9 Clipboard copy modes. 40
5.10 Frozen columns/rows. 41
5.11 Implementing Custom cells and editing controls/cells. 41
5.11.1 IDataGridViewEditingControl 42
5.11.2 IDataGridViewEditingCell 42
5.12 Virtual mode. 42
5.12.1 Bound Mode and Virtual Mode. 42
5.12.2 Supplementing Bound Mode. 42
5.12.3 Replacing Bound Mode. 43
5.12.4 Virtual-Mode Events. 43
5.12.5 Best Practices in Virtual Mode. 44
5.13 Capacity. 44
6 Best Practices. 45
6.1 Using Cell Styles Efficiently. 45
6.2 Using Shortcut Menus Efficiently. 45
6.3 Using Automatic Resizing Efficiently. 45
6.4 Using the Selected Cells, Rows, and Columns Collections Efficiently. 46
6.5 Using Shared Rows. 46
6.6 Preventing Rows from Becoming Unshared. 47
附录 A – 常见问题:... 49
1. 如何使指定的单元格不可编辑?. 49
2. 如何让一个单元格不可用?. 49
3. 如何避免用户将焦点设置到指定的单元格?... 51
4. 如何使所有单元格总是显示控件(不论它是否处于编辑状态)?. 51
5. Why does the cell text show up with “square” characters where they should be new lines?. 51
6. 如何在单元格内同时显示图标和文本?... 51
7. 如何隐藏一列?... 53
8. 如何避免用户对列排序?... 53
9. 如何针对多个列排序?. 54
10. 如何为编辑控件添加事件处理函数?. 58
11. 应在何时移除编辑控件的事件处理函数?. 58
12. 如何处理ComboBox列中ComboBox控件的SelectIndexChanged事件?. 58
13. 如何通过拖放调整行的顺序?... 59
14. 如何调整最后一列的宽度使其占据网格的剩余客户区?... 60
15. 如何让TextBox类型的单元格支持换行?. 60
16. 如何使Image列不显示任何图像(字段值为null时)?. 61
17. 如何能够在ComboBox类型的单元格中输入数据?. 61
18. How do I have a combo box column display a sub set of data based upon the value of a different combo box column? 61
19. 如何在用户编辑控件的时候(而不是在验证时)就显示错误图标?. 62
20. 如何同时显示绑定数据和非绑定数据?... 65
21. How do I show data that comes from two tables?. 66
22. 如何显示主从表?... 66
23. 如何在同一DataGridView中显示主从表?... 68
24. 如何避免用户对列排序?. 68
25. 如何在点击工具栏按钮的时候将数据提交到数据库?... 68
26. 如何在用户删除记录时显示确认对话框?... 68
1 何为DataGridView
通过DataGridView控件,可以显示和编辑表格式的数据,而这些数据可以取自多种不同类型的数据源。
DataGridView控件具有很高的的可配置性和可扩展性,提供了大量的属性、方法和事件,可以用来对该控件的外观和行为进行自定义。当你需要在WinForm应用程序中显示表格式数据时,可以优先考虑DataGridView(相比于DataGrid等其它控件)。如果你要在小型网格中显示只读数据,或者允许用户编辑数以百万计的记录,DataGridView将为你提供一个易于编程和良好性能的解决方案。
DataGridView 用来替换先前版本中的DataGrid,拥有较DataGrid更多的功能;但DataGrid仍然得到保留,以备向后兼容和将来使用。如果你要在两者中选择,可以参考下面给出的DataGrid 和DataGridView之间区别的细节信息。
1.1 DataGridView和DataGrid 之间的区别
DataGridView提供了大量的DataGrid所不具备的基本功能和高级功能。此外,DataGridView 的结构使得它较之DataGrid控件更容易扩展和自定义。
下表描述了DataGridView提供而DataGrid未提供的几个主要功能。
DataGridView功能 |
描述 |
多种列类型 |
与DataGrid相比,DataGridView 提供了更多的内置列类型。这些列类型能够满足大部分常见需要,而且比DataGrid中的列类型易于扩展或替换。 |
多种数据显示方式 |
DataGrid仅限于显示外部数据源的数据。而DataGridView则能够显示非绑定的数据,绑定的数据源,或者同时显示绑定和非绑定的数据。你也可以在DataGridView中实现virtual mode,实现自定义的数据管理。 |
用于自定义数据显示的多种方式 |
DataGridView提供了很多属性和事件,用于数据的格式化和显示。比如,你可以根据单元格、行和列的内容改变其外观,或者使用一种类型的数据替代另一种类型的数据。 |
用于更改单元格、行、列、表头外观和行为的多个选项 |
DataGridView使你能够以多种方式操作单个网格组件。比如,你可以冻结行和列,避免它们因滚动而不可见;隐藏行、列、表头;改变行、列、表头尺寸的调整方式;为单个的单元格、行和列提供工具提示(ToolTip)和快捷菜单。 |
唯一的一个DataGrid提供而DataGridView未提供的特性是两个相关表中数据的分层次显示(比如常见的主从表显示)。你必须使用两个DataGridView来显示具有主从关系的两个表的数据。
1.2 DataGridView的亮点
下表着重显示了DataGridView的主要特性,稍后会介绍它们的详细信息。
DataGridView控件特性 |
描述 |
多种列类型 |
DataGridView提供有TextBox、CheckBox、Image、Button、ComboBox和Link类型的列及相应的单元格类型。 |
多种数据显示方式 |
DataGrid仅限于显示外部数据源的数据。而DataGridView则能够显示非绑定的数据,绑定的数据源,或者同时显示绑定和非绑定的数据。你也可以在DataGridView中实现virtual mode,实现自定义的数据管理。 |
自定义数据的显示和操作的多种方式 |
DataGridView提供了很多属性和事件,用于数据的格式化和显示。 此外,DataGridView提供了操作数据的多种方式,比如,你可以:
|
用于更改单元格、行、列、表头外观和行为的多个选项 |
DataGridView使你能够以多种方式操作单个网格组件。比如,你可以:
|
提供丰富的可扩展性的支持 |
DataGridView提供易于对网格进行扩展和自定义的基础结构,比如:
|
2 DataGridView的结构
DataGridView及其相关类被设计为用于显示和编辑表格数据式数据的灵活的、可扩展的体系。这些类都位于system.Windows.Forms命名空间,它们的名称也都有共同的前缀"DataGridView"。
2.1 结构元素(Architecture Elements)
主要的DataGridView相关类继承自DataGridViewElement类。
DataGridViewElement类有两个属性,一是DataGridView,该属性提供了对其所属的DataGridView的引用;二是State,该属性表示当前的状态,其值为DataGridViewElementStates枚举,该枚举支持位运算,这意味着可以设置组合状态。
2.2 单元格和组(Cells and Bands)
DataGridView由两种基本的对象组成:单元格(cell)和组(band)。所有的单元格都继承自DataGridViewCell基类。 两种类型的组(或称集合)DataGridViewColumn和DataGridViewRow都继承自DataGridViewBand 基类,表示一组结合在一起的单元格。
DataGridView会与一些类进行互操作,但最常打交道的则是如下三个:DataGridViewCell, DataGridViewColumn,DataGridViewRow。
2.3 DataGridView的单元格 (DataGridViewCell)
单元格(cell)是操作DataGridView的基本单位。Display is centered on cells, and data entry is often performed through cells。可以通过DataGridViewRow 类的Cells 集合属性访问一行包含的单元格,通过DataGridView的SelectedCells集合属性访问当前选中的单元格,通过DataGridView的CurrentCell属性访问当前的单元格。
DataGridViewCell 类图 |
Cell 相关类和属性 |
DataGridViewCell是一个抽象基类,所有的单元格类型都继承于此。DataGridViewCell及其继承类型并不是Windows Forms控件,但其中一些宿主于Windows Forms控件。单元格支持的编辑功能通常都由其宿主控件来处理。
DataGridViewCell对象不会像Windows Forms控件那样控制自己的外观和绘制(painting)特征,相反的,DataGridView会负责其包含的单元格的外观。通过DataGridView 控件的属性和事件,你可以深刻地影响单元格的外观和行为。如果你对单元格定制有特殊要求,超出了DataGridView提供的功能,可以继承DataGridViewCell或者它的某个子类来满足这些要求。
2.3.1 DataGridViewCell的工作机制
理解DataGridView结构的一个重要部分是理解DataGridViewCell的工作机制:
单元格的值(A Cell’s Value)
单元格的值是其根本所在。如果单元格所在列不是绑定列,并且所在的DataGridView也不是Virtual Mode,那么它的值就由它本身所持有并维护。对于那些由绑定产生的单元格,它们压根儿就不“知道”该持有什么值,当然也就不会去维护了;当DataGridView需要单元格的值的时候,它会到数据源中查询该单元格应当显示的值。在Virtual Mode下,除了会触发CellValueNeeded事件以获取相应单元格的值外,与数据绑定方式非常相似。在单元格级,所有这些由DataGridViewCell.GetValue() 方法来控制。
默认情况下,单元格的值的类型为object。当一个列被绑定后,会设置它的ValueType属性,它包含的单元格的ValueType也随之更新。而单元格的ValueType对于下一步的格式化非常重要。
格式化显示(Formatting for Display)
注意:当DataGridView需要了解“如何显示这个单元格”时,它需要的是单元格的FormattedValue ,而不是Value。这是一个复杂的过程,因为格式化屏幕上的一些内容通常需要将它转换为字符串。例如,尽管你将单元格的值(Value)设置为整型值155,在显示它的时候仍需要将其格式化。单元格和其所在的列的FormattedValueType 属性决定了显示它时所用的类型。多数列使用字符串类型,而Image和CheckBox类型的单元格/列则使用其它类型。Image类型的单元格和列使用Image作为默认的FormattedValueType,它的内置实现了解如何去显示一个Image。CheckBox类型的单元格/列的FormattedValueType属性则取决于属性ThreeState的值。在单元格级,所有这些由DataGridViewCell.GetFormattedValue()控制。
默认情况下,DataGridView使用TypeConverter将单元格的值(Value)转换为格式化的值(FormattedValue)。DataGridView会基于单元格的ValueType和FormattedValueType属性来获取合时的TypeConverter。
对于一个单元格,FormattedValue会得到多次请求(即会在多个地方用到):绘制单元格的时候,所在列根据单元格内容自动调整大小的时候,甚至是在判断鼠标是否经过单元格内容时。每次需要FormattedValue的时候,DataGridView会触发CellFormatting事件,这时你就有机会修改单元格的格式化显示了。
如果单元格不能获取它的格式化值,它会触发DataError事件。
格式化显示单元格还包含以怎样的首选尺寸显示它。这个首选尺寸是由单元格的FormattedValue,填充区域(padding),附加显示和边框合并而成。
绘制单元格的显示(Painting the Display)
在获得FormattedValue 后,单元格将负责绘制它的内容。单元格决定了绘制过程所使用的正确样式(参见本文档第五章的样式部分)并进行绘制。记住:如果单元格不去绘制自己,那么该单元格将不会有任何内容得到绘制(即单元格的绘制只由它自己负责),行、列不会负责绘制任何内容,因此要确保至少要绘制单元格的背景(background),否则单元格所在的矩形区域仍然是无效的(即未经绘制)。
解析单元格的显示(Parsing the Display)
用户开始与单元格交互后,可能会编辑单元格的值。有一件事要记住,用户编辑的实际上是单元格的FormattedValue。用户提交所编辑的值时,FormattedValue需要转换回单元格的值(Value),这个过程称为解析(parsing)。在单元格级上,所有这些工作由单元格的DataGridViewCell.ParseFormattedValue(int rowIndex)方法控制。
默认情况下,会再次使用TypeConverter来将FormattedValue解析为单元格的真实值,这时会触发DataGridView的CellParsing事件,这时你就有机会修改单元格的解析方式了。.
如果单元格不能得到正确地解析,会触发DataError事件。
2.3.2 常见问题
4) 如何使所有单元格总是显示控件(不论它们是否处于编辑状态)?
5) Why does the cell text show up with “square” characters where they should be new lines?
2.4 DataGridView的列(DataGridViewColumn)
DataGridView所附带的数据(这些数据可以通过绑定或非绑定方式附加到控件)的结构表现为DataGridView的列。你可以使用DataGridView的Columns集合属性访问DataGridView所包含的列,使用SelectedColumns 集合属性访问当前选中的列。
DataGridViewColumn 类图 |
Column 相关类和属性 |
一些主要的单元格类型拥有相应的列类型,这些列类型继承自DataGridViewColumn基类。
常见问题:
1) 如何隐藏一列?
2) 如何避免用户对列排序?
3) 如何针对多个列排序?
2.5 DataGridView的编辑控件(Editing Controls)
支持高级编辑功能的单元格一般都使用一个继承自Windows Forms控件的宿主控件,这些控件同时也实现了IDataGridViewEditingControl接口。
DataGridView Editing Control Class diagram |
Classes that implement Editing Controls |
下表说明了单元格类型、列类型、编辑控件间的关系:
单元格类型 |
宿主控件 |
列类型 |
DataGridViewButtonCell |
n/a |
DataGridViewButtonColumn |
DataGridViewCheckBoxCell |
n/a |
DataGridViewCheckBoxColumn |
DataGridViewComboBoxCell |
DataGridViewComboBoxEditingControl |
DataGridViewComboBoxColumn |
DataGridViewImageCell |
n/a |
DataGridViewImageColumn |
DataGridViewLinkCell |
n/a |
DataGridViewLinkColumn |
DataGridViewTextBoxCell |
DataGridViewTextBoxEditingControl |
DataGridViewTextBoxColumn |
常见问题(FAQ)
3) 如何处理ComboBox列中Combox控件的SelectIndexChanged事件?
4) 如何使所有单元格总是显示控件(不论它是否处于编辑状态)?
2.6 DataGridViewRow
DataGridViewRow类用于显示数据源的一行数据。可以通过DataGridView控件的Rows集合属性来访问其包含的行,通过SelectedRows集合属性访问当前选中的行。
DataGridViewRow类图 |
Row相关的类和属性 |
你可以继承DataGridViewRow类来实现自己的行类型,虽然多数情况下这并不必要。DataGridView 有几个行相关的事件和属性,用以自定义其包含的DataGridViewRow对象的行为。
如果你将DataGridView的AllowUserToAddRows属性设为true,一个专用于添加新行的特殊行会出现在最后一行的位置上,这一行也属于Rows集合,但它有一些需要你提起注意的特殊功能,要获得这方面的更多信息,请参看4.1.3节。
2.6.1 常见问题
3 列/单元格类型揭密(column/cell types)
DataGridView控件提供了几种列类型用以显示数据,并允许用户修改和添加数据。
当你对DataGridView进行了绑定,并将它的AutoGenerateColumns属性设置为true,它会根据数据源中列的数据类型自动生成列,这些列都使用相应的默认类型(与数据源列数据类型相适应)。
你也可以自行创建列的实例,将它们加入DataGridView的Columns集合中,这些列可用作非绑定列,也可以以手动方式让它们用于绑定数据。手动绑定的列非常有用,比如,自动生成的列都采用与数据源的列相应的默认类型,而你不想用默认列类型。
下表描述了DataGridView 的各种列对应的类:
列类型 |
描述 |
DataGridViewTextBoxColumn |
用于基于文本的值。绑定到数字和字符串值时会自动生成这种类型的列。 |
DataGridViewCheckBoxColumn |
用于显示Boolean和CheckState类型的值,绑定到上述类型值时会自动生成这种类型的列。 |
DataGridViewImageColumn |
用于显示图像。绑定到byte数组,Image对象,图标对象时会自动生成这种类型的列。 |
DataGridViewButtonColumn |
用于在单元格内显示按钮。在绑定时不会自动生成,一般用于非绑定列。 |
DataGridViewComboBoxColumn |
用于在单元格内显示下拉列表。在绑定时不会自动生成,一般地需要手工绑定。 |
DataGridViewLinkColumn |
用于在单元格内显示链接。在绑定时不会自动生成,一般地需要手工绑定。 |
自定义列类型 |
通过继承DataGridViewColumn 类或其子类,你可以创建自己的列类型,以提供自定义的外观、行为和宿主控件。 |
常见问题(FAQ)
1) 如何隐藏一列?
3.1 DataGridViewTextBoxColumn
DataGridViewTextBoxColumn是一种通用的列类型,用于表示基于文本的值,比如数字和字符串。在编辑模式下,会有一个TextBox控件出现在当前活动单元格,用户可以修改单元格的值。
单元格的值在显示时会自动转换为字符串。用户输入或修改的值在提交时则被自动解析为合适的数据类型以创建一个单元格的值。通过处理CellFoamatting和CellParsing事件,你可以自定义这些转换的方式。比如将数据源的日期字段以特定的形式显示,对某些特殊单元格作出特殊的标记。
对一列来说,它包含的单元格值的数据类型由该列的ValueType属性指定。
3.1.1 常见问题
2) Why does the cell text show up with “square” characters where they should be new lines?
3.2 DataGridViewCheckBoxColumn
DataGridViewCheckBoxColumn用于显示Boolean或CheckState类型的值。Boolean 值显示为二元(two-state)或三元 (three-state) 的CheckBox,而这取决于该列的ThreeState 属性的值。如果该类型的列绑定到CheckState类型的值,ThreeState属性的默认值为true。
一般情况下,CheckBox类型的单元格要么用于存储数据,就像其它类型的数据一样,要么用于进行一些重要操作。用户点击CheckBox单元格时,如果你希望对此立即做出反应,可以处理CellClick事件,但该事件发生在单元格的值更新之前。如果点击之时就希望获得新值,一种选择是根据当前值计算点击后的值;另一种方法是立即提交值的变化,然后在CellValueChanged事件处理函数中对此作出反应,而要在用户点击单元格时立即提交值的变化,你必须处理CurrentCellDirtyStateChanged事件,在这里,调用CommitEnd方法提交新值。
3.3 DataGridViewImageColumn
DataGridViewImageColumn 类型的列用于显示图像。这种类型的列有三种方法生成:绑定到数据源时自动生成;为非绑定列手动生成;在CellFormatting事件处理函数(该事件发生在单元格显示前)中动态生成。
绑定到数据源时自动生成Image列的方法适用于大量的图像格式,包括.NET中Image类支持的各种格式,还有Access数据库及Northwind范例数据库使用的OLE图片格式。
如果你想提供DataGridViewButtonColumn列的功能,又希望显示自定义的外观,手动生成Image列会很有用。在显示后,你可以处理CellClick事件以处理用户对单元格的点击(模拟按钮列)。
如果你要为计算值或非图片的值提供图片显示,在CellFormatting事件处理函数中动态生成Image列的方法会很有用。比如,你有一个表示风险值的列,它的值可能是”high”、”middle”或”low”,可以为它们显示不同的图标作为警示;或者你有一个名为”Image”的列,它的值时图片文件的位置而不是真实的图片内容,也可以用这种方法。
3.3.1 常见问题
1) 如何使Image列不显示任何图像(字段值为null时)?
3.4 DataGridViewButtonColumn
使用DataGridViewButtonColumn 列,可以在单元格内显示按钮。如果你要为用户操作特定行提供一种简单的方式,Button列会很有用,比如排序或在另一个窗体中显示子表记录。
在对DataGridView进行数据绑定时不会自动生成Button列,所以你必须手动创建它们,然后把它们添加到DataGridView控件的Columns集合中。
你可以处理CellClick事件以响应用户的点击动作。
3.5 DataGridViewComboBoxColumn
在DataGridViewComboBoxColumn类型的列中,你可以显示包含下拉列表的单元格。这在仅允许用户输入一些特定值的时候显得很有用,比如在SQL Server示例数据库Northwind中Products表的Category列,它表示产品的种类,这个应只允许选择现有的产品种类,此时就可以使用ComboBox列。
如果你了解如何为ComboBox控件生成下拉列表,就可以用相同的方式为ComboBox列中的所有单元格生成下拉列表。要么通过列的Items集合手动添加,要么通过DataSource,DisplayMember 和ValueMember属性绑定到一个数据源。要了解其中的更多信息,可以参考WinForms中ComboBox空间的用法。
你可以将ComboBox列的单元格的实际值绑定到DataGridView控件本身的数据源(注意不是ComboBox列的数据源),这需要设置该列的DataPropertyName属性(设置某个列的名称)。
ComboBox列不会在数据绑定时自动生成,所以你必须手动创建它们,然后将其添加到Columns集合属性中。另外,你也可以使用设计器,在设计时设置相应的属性,这个过程类似于在设计器中ComboBox控件的使用。
3.5.1 DataError事件和ComboBox列
在使用DataGridViewComboBoxColumn 时,有时会修改单元格的值或启动ComboBox控件的Items集合,这样可能会引发DataError事件。这是ComboBox列的设计使然,ComboBox列的单元格会进行数据验证。在ComboBox列的单元格尝试绘制包含的内容时,它需要将包含的值进行格式化(见第二章第三节),在此转换过程中,它会在ComboBox的Items集合中查找对应的值,如果查找失败,就会引发DataError事件。忽略了DataError事件可能会使单元格不能进行正确的格式化。
3.5.2 常见问题
2) How do I handle the SelectedIndexChanged event?
3.6 DataGridViewLinkColumn
使用DataGridViewLinkColumn列,你可以显示一列包含超链接的单元格。在显示数据源中的URL值,或者替代按钮列进行一些特殊行为,如打开另一个子记录窗体时会很有用。
Link列也不会在DataGridView数据绑定时自动生成。要使用它,你还得手动创建,然后将它添加到DataGridView控件的Columns集合中。
你可以处理CellContentClick事件来相应用户的点击动作。这个事件不同于CellClick 和CellMouseClick 事件,后两者在用户点击单元格任何位置(而不仅仅时链接)时都会触发。
DataGridViewLinkColumn 类提供了几个属性,用来修改链接的外观,包括点击前,点击时和点击后(类似于网页中的超链接)。
4 操作数据(Working with Data)
多数情况下,使用DataGridView的时候都需要跟数据打交道,这时有很多事情可能需要你去做。你需要验证用户输入的数据,或者需要对数据进行格式化。DataGridView能够以三种模式显示数据:bound、unboundand 和virtual。每种模式都有自己的特性和存在的理由。不管是否是数据绑定模式,在操作数据时,如果发生错误,DataGridView通常会触发DataError事件,理解该事件发生的原因能让你更好地利用它。
4.1 数据输入和验证的相关事件
用户输入数据时-对其所在的行或单元格,你可能希望验证这些数据,在遇到无效数据时通知用户。就像常见的Windows Forms控件,DataGridView的行和单元格也有Validating和Validated事件,验证事件可被取消。用户在单元格/行间移动时会触发Enter和Leave事件。最后,用户在开始编辑单元格时也会触发事件。了解所有这些程序的发生顺序会对你很有帮助。
4.1.1 数据验证相关事件的顺序
下面列出validation,enter/leave和begin/end这些事件的顺序(当EditMode为EditOnEnter时):
当从一个单元格移动至另一单元格(在同一行内):
1) Cell Leave (原来的单元格)
2) Cell Validating/ed (原来的单元格)
3) Cell EndEdit (原来的单元格)
4) Cell Enter (新的单元格)
5) Cell BeginEdit (新的单元格)
当从一行移动到另一行:
1) Cell Leave (原来的单元格),Row leave (原来的行)
2) Cell Validating/ed (原来的单元格)
3) Cell EndEdit (原来的单元格)
4) Row Validating/ed (原来的行)
5) Row Enter (新的行)
6) Cell Enter (新的单元格)
7) Cell BeginEdit (新的单元格)
4.1.2 验证数据
验证用户输入时,如果DataGridView采用非数据绑定模式,通常会对单元格进行验证;而如果采用数据绑定模式,则一般会对行进行验证。这与数据的组织方式密切相关,非数据绑定模式下,一行的单元格间关系一般比较“散”,而绑定模式下,数据源的数据一般以行来组织。但有时在数据绑定模式下会同时进行单元格级和行级的验证。
4.1.2.1 显示错误信息
一旦遭遇了无效的输入数据,你通常需要通知用户。这时有多种方式可以选择,传统的方式是使用信息对话框。DataGridView还能够为行或单元格显示一个错误图标来通知用户输入了无效数据。错误图标带有一个工具提示,它提供了该错误的相关信息:
4.1.2.2 常见问题(FAQ)
4.1.3 在新行中的数据输入(Data Entry in the New Row)
当在程序中使用DataGridView来编辑数据时,你往往希望提供让用户添加新行数据的功能。DataGridView控件支持这个功能,提供了一个用于添加新记录的行,而这一行总是显示为最后一行,并在该行的标题单元格标以星号(*)。 下面的几个小节会讨论一些在程序中使用这个新行时需要考虑的内容。(下面总是以 新行 表示 用于添加新记录的行 )
4.1.3.1 显示新行
使用AllowUserToAddRows属性以指示是否显示新行,其默认值为true。
新行处于网格的最后一行,标题带有星号:
在数据绑定的情况下,当DataGridView控件的AllowUserToAddRows属性和数据源的IBindingList.AllowNew 属性都为true时,新行才会显示,只要两者有一个为false,新行就不会显示。
4.1.3.2 为生成的新行添加默认值
当用户选择新行作为当前行,DataGridView会触发DefaultValuesNeeded事件。在该事件中可以访问新行,并为其生成默认值,为用户输入提供方便。
下面这段代码演示了如何在DefaultValuesNeeded事件中为新行指定默认值。
private void dataGridView1_DefaultValuesNeeded(object sender,
DataGridViewRowEventArgs e)
{
e.Row.Cells["Region"].Value = "WA";
e.Row.Cells["City"].Value = "Redmond";
e.Row.Cells["PostalCode"].Value = "98052-6399";
e.Row.Cells["Region"].Value = "NA";
e.Row.Cells["Country"].Value = "USA";
e.Row.Cells["CustomerID"].Value = NewCustomerId();
}
4.1.3.3 Rows集合与新行的关系
新行包含在DataGridView控件的Rows集合中,又因其总是处于最后一行,下面这行代码会返回新行:
DataGridViewRow row = dataGridView1.Rows[dataGridView1.Rows.Count - 1];
尽管新行也包含在Rows集合中,它与Rows集合中其它行的行为却不相同,表现在两点:
- 不能以编程的方式将新行从Rows集合中移除,如果你尝试这么做,会抛出InvalidOperationException类型的异常。用户也不能删除新行。DataGridViewRowCollection.Clear()方法也不能将新行从Rows集合中移除。
- 不能在新行之后添加行。如果你尝试这么做,会抛出InvalidOperationException 类型的异常。这种特性的结果是,新行总处于DataGridView的最后一行。当新行显示的时候,DataGridViewRowCollection 类中用于添加行的方法-Add,AddCopy以及AddCopies-在内部都调用用于插入的方法。
4.1.3.4 在新行中输入数据
用户开始在新行输入数据之前,新行的IsNewRow属性值为true;一旦用户开始输入,这一行就不再是新行了,DataGridView中会产生一个“新”的新行,看下面示意图:
在添加“新”的新行时,会触发UserAddedRow事件,它的事件处理函数的第二个参数有属性Row,指定了这个“新”的新行。如果用户此时按下Escape键,“新”的新行会被移除,这会触发UserDeletingRow事件,它的事件处理函数的第二个参数的属性Row指定了“新”的新行。
4.1.3.5 自定义新行的可视化效果
新行是基于RowTemplate模板创建的,如果没有指定它的单元格的样式,它们会采用继承的样式。要了解样式继承的更多信息,请参看第五章第一节的内容。
新行中单元格的初始值是由每个单元格的DefaultNewRowValue属性决定的。对于DataGridViewImageCell类型的单元格,其初始值为一个占位图片,其它类型的则为null。你可以重写这个属性以返回自定义值。但也可以在DefaultValuesNeeded事件处理函数中对默认值进行替换,该事件在焦点进入新行时触发。
新行标题的标准图标是箭头或者星号,并没有得到暴露。如果你要自定义这个图标,就需要创建一个自定义的DataGridViewRowHeaderCell 类。
新行的标题的标准图标使用标题单元格DataGridViewCellStyle的ForeColor属性。注意:如果没有足够的空间,图标就不会再显示。
如果为标题单元格设置了字符串值(通过Value属性),但没有足够的控件同时显示文本和图标,那么图标会被首先截掉。
4.1.3.6 新行的排序
在非绑定模式下,新行总是添加在DataGridView的最后一行,即使已经对数据排序。用户需要在添加新行后再次进行排序,以将新记录放在合适的位置;这种行为方式类似于ListView控件。
在绑定模式或虚拟模式(Virtual Mode)下,如果已对数据排序,那么插入数据时的行为取决于数据模型的实现方式。对于ADO.NET,新加的行会被自动排序至合适的位置。
4.1.3.7 关于新行,还要注意:
你不能将新行的Visible属性值设置为false,否则会触发一个InvalidOperationException类型的异常。
新行在创建时总是处于非选中(unselected)状态。
4.1.3.8 Virtual Mode下的新行
如果你正要实现虚拟模式(Virtual Mode),需要考虑数据模型添加新行和回滚添加操作的情况。该功能准确的实现方式取决于数据模型的实现方式及其事务机制,例如,提交的时候是针对单元格还是行。参看本文档后面关于Virtual Mode的主题。
4.2 关于Null值
在使用数据源的时候,比如数据库或业务对象,经常需要处理null值。null值可能是一个实际的null(VB中为Nothing),也可能是一个数据库的”null”值(DBNull.Value),当你遭遇了这些值,就需要考虑如何显示它们。另一方面,很多时候,你还需要向数据源写入null值。使用单元格Style的NullValue属性和DataSourceNullValue 属性,你可以改变DataGridView处理null值的方式。
4.2.1 NullValue属性
DataGridViewCellStyle.NullValue 属性本来要被命名为FormattedNullValue 的,但是后来没来得及作出这个更改。但它能给我们带来一点提示——顾名思义,在格式化时会用到它。如果一个单元格的值为”null”(等于null或DBNull.Value),它会使用你设置的NullValue属性来显示。该属性的默认值取决于所在列的类型,见下图:
DataGridView列类型 |
列的DefaultCellStyle.NullValue值 |
TextBoxColumn |
String.Empty (“”) |
ImageColumn |
空的图像( ) |
ComboBoxColumn |
String.Empty (“”) |
ButtonColumn |
String.Empty (“”) |
LinkColumn |
String.Empty (“”) |
CheckBoxColumn |
默认值取决于ThreeState属性的值,如果为true,默认值为CheckState.Indeterminate ,否则为unchecked。 |
有一点要了解,在用户输入数据时也会用到NullValue。例如,若用户向TextBox类型单元格输入了string.Empty,那么会将null作为该单元格的值。 查看下面的DataSourceNullValue属性以了解究竟是输入了什么作为单元格的值。
4.2.2 DataSourceNullValue属性
DataGridViewCellStyle.DataSourceNullValue属性要被命名为ParseNullValue的,如果NullValue属性被命名为FormattedNullValue的话,但最后还是采用了DataSourceNullValue,这样更直观准确。在将null值写入单元格的值时,就会用到DataSourceNullValue属性。在数据绑定情形下,这个null值将被写入数据库或业务对象,此处需要进行控制,因为对于数据库和业务对象来说,null的概念不尽相同。通常你会期望,使用业务对象时将DataSourceNullValue 设置为null,而使用数据库时则将其设置为DBNullValue。DataSourceNullValue的默认值为DBNull.Value。
4.3 DataError事件
将DataError事件独立出来作为一个主题,是因为在操作数据时,经常会遭遇DataError事件。在操作数据时,DataError主要发生在一下情况:不能读/写或转换单元格的数据;在尝试进行某种编辑操作时发生了异常。
编辑操作中的DataError 事件
下面的列表列出了可能会引发DataError事件的编辑操作:
|
|
|
|
|
|
|
DataError的上下文:
下面的列表显示了不同的DataError上下文环境,然后进一步说明了这些上下文环境合适可能发生:
DataErrorContext |
何时发生 |
Formatting |
When attempting to retrieve the cell's formatted value. |
Display |
When attempting to paint the cell or calculate the cell's tooltiptext. Note that these operations usually also require getting the cell's formatted value, so the error context is OR'd together. |
PreferredSize |
When calculating the preferred size of a cell. This |
RowDeletion |
Any exception raised when deleting a row. |
Parsing |
When exceptions occur when committing, ending or canceling an edit. Usually OR'd in with other error contexts |
Commit |
When exceptions occur when committing an edit. Usually OR'd with other error contexts |
InitialValueRestoration |
When exceptions occur while either initializing the editing control/cell's value, or Canceling an edit |
LeaveControl |
When exceptions occur while attempting to validate grid data when the grid is losing focus. Usually OR'd with other error contexts. |
CurrentCellChange |
When exceptions occur while validatingupdatingcommittinggetting cell content when the current cell changes. Usually OR'd with other error contexts. |
Scroll |
When exceptions occur while validatingupdatingcommittinggetting cell content when the current cell changes as a result of scrolling. |
ClipboardContent |
When exceptions occur while attempting to get the formatted value of a cell while creating the clipboard content. |
4.4 数据绑定模式(Databound modes)
4.4.1 非绑定模式(Unbound Mode)
如果你要在程序中管理数量相对较小的数据,那么非绑定模式会比较合适。此时你不是像绑定模式中那样将DataGridView控件直接指向一个数据源,而是手动去生成控件。一般需要用到DataGridViewRowCollection.Add 方法(该方法向DGV中添加行)。
非绑定模式在处理静态、只读的数据时特别有用,也可以用在以自己的方式与外部数据源交互的情况,但实际上,如果你希望你的用户与外部的数据源交互,一般还是用绑定模式(bound mode)更好。
4.4.2 绑定模式(Bound Mode)
如果你在程序中管理一些数据,并希望能与数据源自动进行交互,就应该使用绑定模式。此时你可以设置DataSource属性,将数据源绑定到DataGridView控件。如果控件使用了绑定模式,就不需要你去显式地对数据进行读写了。如果AutoGenerateColumns 属性为true,数据源中的每一列都会在DataGridView中生成一个相应的列(根据列的数据类型),如果你希望创建自己的列,可以将该属性设置为false,使用DataPropertyName属性将一列绑定到数据源的一列,这在你不想用自动生成的列类型时很有用。
4.4.2.1 有效的数据源
将数据绑定到DataGridView非常简单、直观,很多情况下,你只需要设置它的DataSource属性。如果使用的数据源包含多个列表(list)或数据表(table),你还需要设置控件的DataMember属性,该属性为字符串类型,用于指定要绑定的列表或数据表。
DataGridView控件支持标准的WinForm数据绑定模型,因此它可以绑定到下面列表中的类的实例:
- 任意实现了IList接口的类,包括一维数组;
- 任意实现了IListSource接口的类,比如DataTable和DataSet;
- 任意实现了IBindingList 接口的类,比如BindingList ;
- 任意实现了IBindingListView接口的类,比如BindingSource 。
列表更改通知(List Change Notification)
当你将数据绑定到列表时,最重要的功能之一便是支持列表更改通知了。这只有在你希望列表(即数据源)发生变化,如添加、修改和删除,DataGridView能够随之更新的时候,该功能才显得重要。只有实现了IBindingList接口的数据源支持更改通知。像数组和集合这样的列表默认情况下不支持更改通知。
在选择数据源时,BindingSource组件应该作为首选,因为它可以绑定到多种类型的数据源,并且能够自动处理很多数据绑定相关的事务。一般情况下,应该将DataGridView绑定到BindingSource组件,并将BindingSource组件绑定真正的数据源(它的作用就像DGV和数据源间的桥梁)。 BindingList<T>类也可以在一个类的基础上创建自定义列表(list)。
对象更改通知(Object Change Notification)
如果你有了一个数据源,那么数据源中的对象就可以实现对public属性的更改通知。这需要你为相应属性提供一个” PropertyNameChanged”事件,或者实现INotifyPropertyChanged接口。INotifyPropertyChanged 是在VS 2005 中新加的接口,可以与BindingList<T>一起使用来创建可绑定的列表(list)。但当你的数据源是BindingSource ,那就不用再额外实现更改通知了。
4.4.3 虚拟模式
使用虚拟模式,你可以实现自己的数据管理操作。在绑定模式下,如果要使用非绑定列,那么要想在对列排序时能够维护非绑定列的值,就需要虚拟模式。但虚拟模式的最主要的用途还是在操作大量数据时优化性能。
你将DataGridView绑定到缓存的数据,然后用代码控制数据行的存取。要保持使用内存量比较小,缓存的数据量应与当前要显示的行数相当。当用户滚动控件看到了新的行时,你的代码就从缓存中请求新的数据,并从内存中清除旧的数据。
如果你正要实现虚拟模式(Virtual Mode),需要考虑数据模型添加新行和回滚添加操作的情况。该功能准确的实现方式取决于数据模型的实现方式及其事务机制,例如,提交的时候是针对单元格还是行。参看本文档后面关于Virtual Mode的主题。
4.4.4 混合模式 – 绑定与非绑定模式
显示在DataGridView中的数据通常来自于某种类型的数据源,但是你可能也希望显示一个数据源之外的列。这种列称为非绑定列。
你可以在绑定模式下添加非绑定列,在你希望显示一个按钮列或者链接列让用户操作一些特定行时这显得很有用,另外也可以用非绑定列显示一些由绑定列计算而得到的值。你可以在CellFormatting事件处理函数中生成计算列的值。不过如果你使用的数据源是DataSet或DataTable,你可能希望使用DataColumn.Expression 属性来创建一个计算列,在这种情况下,在DGV看来,这一列就跟数据源中其它列是一样的。
在绑定模式下根据非绑定列排序是不受支持的。如果你在绑定模式下创建了非绑定列,你必须实现虚拟模式,这样在根据绑定列排序时可以维护非绑定列的值。
如果添加的非绑定列不能由数据源数据计算得来或者这些数据会频繁更新,你就应该使用虚拟模式。要了解虚拟模式的更多信息,请参看本文档后面的虚拟模式相关章节。
4.4.5 常见问题
2) How do I show data that comes from two tables?(TODO)
3) 如何显示主从表?
5) 如何避免对一列的排序?
6) 如何针对多个列排序?
5 特性综览(Overview of features)
5.1 样式(Styling)
DataGridView使得定义单元格的基本外观和格式化单元格显示变得简单。
您可以定义的外观和在特定的列和行,或在通过各种设置DataGridView控件属性访问的DataGridViewCellStyle对象的属性控制所有细胞的单个单元格的格式样式。此外,您可以修改,如通过处理CellFormatting事件的单元格值因素的基础上动态这些样式。
DataGridView控件中的每一个细胞都可以拥有如文本格式,背景色,前景色和字体自己的风格。但是,通常多个单元格将分享独特的风格特点。
细胞群体共享样式可能包括在特定行或列的所有单元格包含特定值,或控件中的所有细胞的所有细胞。由于这些群体重叠,每个单元可能会从多个位置的样式信息。例如,您可能会希望每个在DataGridView控件使用相同的字体细胞,只有细胞货币列,但使用货币格式,负数和货币细胞只使用红色前景色。
You can define appearance and formatting styles for individual cells, for cells in specific columns and rows, or for all cells in the control by setting the properties of the DataGridViewCellStyle objects accessed through various DataGridView control properties. Additionally, you can modify these styles dynamically based on factors such as the cell value by handling the CellFormatting event.
Each cell within the DataGridView control can have its own style, such as text format, background color, foreground color, and font. Typically, however, multiple cells will share particular style characteristics.
Groups of cells that share styles may include all cells within particular rows or columns, all cells that contain particular values, or all cells in the control. Because these groups overlap, each cell may get its styling information from more than one place. For example, you may want every cell in a DataGridView control to use the same font, but only cells in currency columns to use currency format, and only currency cells with negative numbers to use a red foreground color.
5.1.1 The DataGridViewCellStyle Class
The DataGridViewCellStyle class contains the following properties related to visual style:
BackColor and ForeColor, SelectionBackColor and SelectionForeColor, Font
This class also contains the following properties related to formatting:
Format and FormatProvider, NullValue and DataSourceNullValue, WrapMode, Alignment, Padding
DataGridViewCellStyle类包含以下有关视觉样式属性:
背景色和前景色,SelectionBackColor和SelectionForeColor,字体
此类还包含了相关的格式如下属性:
格式和FormatProvider,并DataSourceNullValue的NullValue,的WrapMode,对齐,填充
5.1.2 Using DataGridViewCellStyle Objects
You can retrieve DataGridViewCellStyle objects from various properties of the DataGridView, DataGridViewColumn, DataGridViewRow, and DataGridViewCell classes and their derived classes. If one of these properties has not yet been set, retrieving its value will create a new DataGridViewCellStyle object. You can also instantiate your own DataGridViewCellStyle objects and assign them to these properties.
You can avoid unnecessary duplication of style information by sharing DataGridViewCellStyle objects among multiple DataGridView elements. Because the styles set at the control, column, and row levels filter down through each level to the cell level, you can also avoid style duplication by setting only those style properties at each level that differ from the levels above. This is described in more detail in the Style Inheritance section that follows.
The following table lists the primary properties that get or set DataGridViewCellStyle objects.
Property |
Classes |
Description |
DefaultCellStyle |
DataGridView, DataGridViewColumn, DataGridViewRow, and derived classes |
Gets or sets default styles used by all cells in the entire control (including header cells), in a column, or in a row. |
RowsDefaultCellStyle |
DataGridView |
Gets or sets default cell styles used by all rows in the control. This does not include header cells. |
AlternatingRowsDefaultCellStyle |
DataGridView |
Gets or sets default cell styles used by alternating rows in the control. Used to create a ledger-like effect. |
RowHeadersDefaultCellStyle |
DataGridView |
Gets or sets default cell styles used by the control's row headers. Overridden by the current theme if visual styles are enabled. |
ColumnHeadersDefaultCellStyle |
DataGridView |
Gets or sets default cell styles used by the control's column headers. Overridden by the current theme if visual styles are enabled. |
Style |
DataGridViewCell and derived classes |
Gets or sets styles specified at the cell level. These styles override those inherited from higher levels. |
InheritedStyle |
DataGridViewCell, DataGridViewRow, DataGridViewColumn, and derived classes |
Gets all the styles currently applied to the cell, row, or column, including styles inherited from higher levels. |
As mentioned above, getting the value of a style property automatically instantiates a new DataGridViewCellStyle object if the property has not been previously set. To avoid creating these objects unnecessarily, the row and column classes have a HasDefaultCellStyle property that you can check to determine whether the DefaultCellStyle property has been set. Similarly, the cell classes have a HasStyle property that indicates whether the Style property has been set.
Each of the style properties has a corresponding PropertyNameChanged event on the DataGridView control. For row, column, and cell properties, the name of the event begins with "Row", "Column", or "Cell" (for example, RowDefaultCellStyleChanged). Each of these events occurs when the corresponding style property is set to a different DataGridViewCellStyle object. These events do not occur when you retrieve a DataGridViewCellStyle object from a style property and modify its property values. To respond to changes to the cell style objects themselves, handle the CellStyleContentChanged event.
5.1.3 Style Inheritance
Each DataGridViewCell gets its appearance from its InheritedStyle property. The DataGridViewCellStyle object returned by this property inherits its values from a hierarchy of properties of type DataGridViewCellStyle. These properties are listed below in the order in which the InheritedStyle for non-header cells obtains its values.
- DataGridViewCell.Style
- DataGridViewRow.DefaultCellStyle
- AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers)
- RowsDefaultCellStyle
- DataGridViewColumn.DefaultCellStyle
- DefaultCellStyle
For row and column header cells, the InheritedStyle property is populated by values from the following list of source properties in the given order.
- DataGridViewCell.Style
- ColumnHeadersDefaultCellStyle or RowHeadersDefaultCellStyle
- DefaultCellStyle
The following diagram illustrates this process.
You can also access the styles inherited by specific rows and columns. The column InheritedStyle property inherits its values from the following properties.
- DataGridViewColumn.DefaultCellStyle
- DefaultCellStyle
The row InheritedStyle property inherits its values from the following properties.
- DataGridViewRow.DefaultCellStyle
- AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers)
- RowsDefaultCellStyle
- DefaultCellStyle
For each property in a DataGridViewCellStyle object returned by an InheritedStyle property, the property value is obtained from the first cell style in the appropriate list that has the corresponding property set to a value other than the DataGridViewCellStyle class defaults.
The following table illustrates how the ForeColor property value for an example cell is inherited from its containing column.
Property of type DataGridViewCellStyle |
Example ForeColor value for retrieved object |
DataGridViewCell.Style |
Color.Empty |
DataGridViewRow.DefaultCellStyle |
Color.Red |
AlternatingRowsDefaultCellStyle |
Color.Empty |
RowsDefaultCellStyle |
Color.Empty |
DataGridViewColumn.DefaultCellStyle |
Color.DarkBlue |
DefaultCellStyle |
Color.Black |
In this case, the System.Drawing.Color.Red value from the cell's row is the first real value on the list. This becomes the ForeColor property value of the cell's InheritedStyle.
The following diagram illustrates how different DataGridViewCellStyle properties can inherit their values from different places.
By taking advantage of style inheritance, you can provide appropriate styles for the entire control without having to specify the same information in multiple places.
Although header cells participate in style inheritance as described, the objects returned by the ColumnHeadersDefaultCellStyle and RowHeadersDefaultCellStyle properties of the DataGridView control have initial property values that override the property values of the object returned by the DefaultCellStyle property. If you want the properties set for the object returned by the DefaultCellStyle property to apply to row and column headers, you must set the corresponding properties of the objects returned by the ColumnHeadersDefaultCellStyle and RowHeadersDefaultCellStyle properties to the defaults indicated for the DataGridViewCellStyle class.
Note: If visual styles are enabled, the row and column headers (except for the TopLeftHeaderCell) are automatically styled by the current theme, overriding any styles specified by these properties. Set the EnableHeadersVisualStyle property to false if you want headers to not use XP’s visual styles.
The DataGridViewButtonColumn, DataGridViewImageColumn, and DataGridViewCheckBoxColumn types also initialize some values of the object returned by the column DefaultCellStyle property. For more information, see the reference documentation for these types.
5.1.4 Setting Styles Dynamically
To customize the styles of cells with particular values, implement a handler for the CellFormatting event. Handlers for this event receive an argument of the DataGridViewCellFormattingEventArgs type. This object contains properties that let you determine the value of the cell being formatted along with its location in the DataGridView control. This object also contains a CellStyle property that is initialized to the value of the InheritedStyle property of the cell being formatted. You can modify the cell style properties to specify style information appropriate to the cell value and location.
Note: The RowPrePaint and RowPostPaint events also receive a DataGridViewCellStyle object in the event data, but in their case, it is a copy of the row InheritedStyle property for read-only purposes, and changes to it do not affect the control.
You can also dynamically modify the styles of individual cells in response to events such as the CellMouseEnter and CellMouseLeave events. For example, in a handler for the CellMouseEnter event, you could store the current value of the cell background color (retrieved through the cell's Style property), then set it to a new color that will highlight the cell when the mouse hovers over it. In a handler for the CellMouseLeave event, you can then restore the background color to the original value.
Note: Caching the values stored in the cell's Style property is important regardless of whether a particular style value is set. If you temporarily replace a style setting, restoring it to its original "not set" state ensures that the cell will go back to inheriting the style setting from a higher level. If you need to determine the actual style in effect for a cell regardless of whether the style is inherited, use the cell's InheritedStyle property.
5.2 Custom painting
The DataGridView control provides several properties that you can use to adjust the appearance and basic behavior (look and feel) of its cells, rows, and columns. If you have requirements that go beyond the capabilities of the DataGridViewCellStyle class, you can perform custom drawing of the cell or row content. To paint cells and rows yourself, you can handle various DataGridView painting events such as RowPrePaint, CellPainting and RowPostPaint.
5.2.1 Paint Parts
One important part of custom painting is the concept of paint parts. The DataGridViewPainParts enumeration is used to specify what parts a cell paints. Enum values can be combined together to have a cell paint or not paint specific parts. Here are the different parts:
PaintPart |
Example ForeColor value for retrieved object |
All |
All parts are painted |
Background |
The background of the cell is painted using the cell’s background color (1) |
Border |
The borders are painted |
ContentBackground |
The background part of the cell’s content is painted. (2) |
ContentForeground |
The foreground part of the cell’s content is painted (2) |
ErrorIcon |
The error icon is painted |
Focus |
The focus rectangle for the cell is painted |
None |
No parts are painted (1) |
SelectionBackground |
The background is painted selected if the cell is selected. |
Notes
1) If a cell does not paint its background then nothing is painted. A row or column performs no painting, so ensure that at least the cell’s background is painted or you perform your own custom background painting; otherwise the rectangle remains invalidated (unpainted).
2) Each cell determines what it paints as content foreground and content background as described by the following list:
Cell Type |
Content Foreground |
Content Background |
Text box |
Cell text is painted |
Nothing painted |
Button |
Cell text is painted |
Button is painted |
Combo box |
Cell text is painted |
Combo box is painted |
Check box |
Check box is painted |
Nothing painted |
Link |
Cell text is painted as a link |
Nothing is painted |
Image |
Cell image is painted |
Nothing painted |
Column Header |
Column header text |
Sort Glyph is painted |
Row Header |
Row header text |
Current row triangle, edit pencil and new row indicator is painted |
5.2.2 Row Pre Paint and Post Paint
You can control the appearance of DataGridView rows by handling one or both of the DataGridView.RowPrePaint and DataGridView.RowPostPaint events. These events are designed so that you can paint only what you want to while letting the DataGridView control paint the rest. For example, if you want to paint a custom background, you can handle the DataGridView.RowPrePaint event and let the individual cells paint their own foreground content. In the RowPrePaint event you can set the PaintParts event args property to easily customize how the cells paint. For example, if you want to keep cells from painting any selection or focus, your RowPrePaint event would set the PaintParts property like so:
e.PaintParts = DataGridViewPaintParts.All &
~(DataGridViewPaintParts.Focus |
DataGridViewPaintParts.SelectionBackground);
Which could also be written as:
e.PaintParts = (DataGridViewPaintParts.Background |
DataGridViewPaintParts.Border |
DataGridViewPaintParts.ContentBackground |
DataGridViewPaintParts.ContentForeground |
DataGridViewPaintParts.ErrorIcon);
Alternately, you can let the cells paint themselves and add custom foreground content in a handler for the DataGridView.RowPostPaint event. You can also disable cell painting and paint everything yourself in a DataGridView.RowPrePaint event handler
5.3 Autosizing
The DataGridView control provides numerous options for customizing the sizing behavior of its columns and rows. Typically, DataGridView cells do not resize based on their contents. Instead, they clip any display value that is larger than the cell. If the content can be displayed as a string, the cell displays it in a ToolTip.
By default, users can drag row, column, and header dividers with the mouse to show more information. Users can also double-click a divider to automatically resize the associated row, column, or header band based on its contents. Columns share the available width of the control by default, so if users can resize the control—for example, if it is docked to a resizable form—they can also change the available display space for all columns.
The DataGridView control provides properties, methods, and events that enable you to customize or disable all of these user-directed behaviors. Additionally, you can programmatically resize rows, columns, and headers to fit their contents, or you can configure them to automatically resize themselves whenever their contents change.
常见问题:
5.3.1 Sizing Options in the Windows Forms DataGridView Control
DataGridView rows, columns, and headers can change size as a result of many different occurrences. The following table shows these occurrences.
Occurrence |
Description |
User resize |
Users can make size adjustments by dragging or double-clicking row, column, or header dividers. |
Control resize |
In column fill mode, column widths change when the control width changes; for example, when the control is docked to its parent form and the user resizes the form. |
Cell value change |
In content-based automatic sizing modes, sizes change to fit new display values. |
Method call |
Programmatic content-based resizing lets you make opportunistic size adjustments based on cell values at the time of the method call. |
Property setting |
You can also set specific height and width values. |
By default, user resizing is enabled, automatic sizing is disabled, and cell values that are wider than their columns are clipped.
The following table shows scenarios that you can use to adjust the default behavior or to use specific sizing options to achieve particular effects.
Scenario |
Implementation |
Use column fill mode for displaying similarly sized data in a relatively small number of columns that occupy the entire width of the control without displaying the horizontal scroll bar. |
Set the AutoSizeColumnsMode property to Fill. |
Use column fill mode with display values of varying sizes. |
Set the AutoSizeColumnsMode property to Fill. Initialize relative column widths by setting the column FillWeight properties or by calling the control AutoResizeColumns method after filling the control with data. |
Use column fill mode with values of varying importance. |
Set the AutoSizeColumnsMode property to Fill. Set large MinimumWidth values for columns that must always display some of their data or use a sizing option other than fill mode for specific columns. |
Use column fill mode to avoid displaying the control background. |
Set the AutoSizeMode property of the last column to Fill and use other sizing options for the other columns. |
Display a fixed-width column, such as an icon or ID column. |
Set AutoSizeMode to None and Resizable to False for the column. Initialize its width by setting the Width property or by calling the control AutoResizeColumn method after filling the control with data. |
Adjust sizes automatically whenever cell contents change to avoid clipping and to optimize the use of space. |
Set an automatic sizing property to a value that represents a content-based sizing mode. To avoid a performance penalty when working with large amounts of data, use a sizing mode that calculates displayed rows only. |
Adjust sizes to fit values in displayed rows to avoid performance penalties when working with many rows. |
Use the appropriate sizing-mode enumeration values with automatic or programmatic resizing. To adjust sizes to fit values in newly displayed rows while scrolling, call a resizing method in a Scroll event handler. To customize user double-click resizing so that only values in displayed rows determine the new sizes, call a resizing method in a RowDividerDoubleClick or ColumnDividerDoubleClick event handler. |
Adjust sizes to fit cell contents only at specific times to avoid performance penalties or to enable user resizing. |
Call a content-based resizing method in an event handler. For example, use the DataBindingComplete event to initialize sizes after binding, and handle the CellValidated or CellValueChanged event to adjust sizes to compensate for user edits or changes in a bound data source. |
Adjust row heights for multiline cell contents. |
Ensure that column widths are appropriate for displaying paragraphs of text and use automatic or programmatic content-based row sizing to adjust the heights. Also ensure that cells with multiline content are displayed using a WrapMode cell style value of True. Typically, you will use an automatic column sizing mode to maintain column widths or set them to specific widths before row heights are adjusted. |
5.3.2 Resizing with the Mouse
By default, users can resize rows, columns, and headers that do not use an automatic sizing mode based on cell values. To prevent users from resizing with other modes, such as column fill mode, set one or more of the following DataGridView properties:
- AllowUserToResizeColumns
- AllowUserToResizeRows
- ColumnHeadersHeightSizeMode
- RowHeadersWidthSizeMode
You can also prevent users from resizing individual rows or columns by setting their Resizable properties. By default, the Resizable property value is based on the AllowUserToResizeColumns property value for columns and the AllowUserToResizeRows property value for rows. If you explicitly set Resizable to True or False, however, the specified value overrides the control value is for that row or column. Set Resizable to NotSet to restore the inheritance.
Because NotSet restores the value inheritance, the Resizable property will never return a NotSet value unless the row or column has not been added to a DataGridView control. If you need to determine whether the Resizable property value of a row or column is inherited, examine its State property. If the State value includes the ResizableSet flag, the Resizable property value is not inherited.
5.3.3 Automatic Sizing
There are two kinds of automatic sizing in the DataGridView control: column fill mode and content-based automatic sizing.
Column fill mode causes the visible columns in the control to fill the width of the control's display area. For more information about this mode, see the Column Fill Mode section below.
You can also configure rows, columns, and headers to automatically adjust their sizes to fit their cell contents. In this case, size adjustment occurs whenever cell contents change.
Note: If you maintain cell values in a custom data cache using virtual mode, automatic sizing occurs when the user edits a cell value but does not occur when you alter a cached value outside of a CellValuePushed event handler. In this case, call the UpdateCellValue method to force the control to update the cell display and apply the current automatic sizing modes.
If content-based automatic sizing is enabled for one dimension only—that is, for rows but not columns, or for columns but not rows—and WrapMode is also enabled, size adjustment also occurs whenever the other dimension changes. For example, if rows but not columns are configured for automatic sizing and WrapMode is enabled, users can drag column dividers to change the width of a column and row heights will automatically adjust so that cell contents are still fully displayed.
If you configure both rows and columns for content-based automatic sizing and WrapMode is enabled, the DataGridView control will adjust sizes whenever cell contents changed and will use an ideal cell height-to-width ratio when calculating new sizes.
To configure the sizing mode for headers and rows and for columns that do not override the control value, set one or more of the following DataGridView properties:
- ColumnHeadersHeightSizeMode
- RowHeadersWidthSizeMode
- AutoSizeColumnsMode
- AutoSizeRowsMode
To override the control's column sizing mode for an individual column, set its AutoSizeMode property to a value other than NotSet. The sizing mode for a column is actually determined by its InheritedAutoSizeMode property. The value of this property is based on the column's AutoSizeMode property value unless that value is NotSet, in which case the control's AutoSizeColumnsMode value is inherited.
Use content-based automatic resizing with caution when working with large amounts of data. To avoid performance penalties, use the automatic sizing modes that calculate sizes based only on the displayed rows rather than analyzing every row in the control. For maximum performance, use programmatic resizing instead so that you can resize at specific times, such as immediately after new data is loaded.
Content-based automatic sizing modes do not affect rows, columns, or headers that you have hidden by setting the row or column Visible property or the control RowHeadersVisible or ColumnHeadersVisible properties to false. For example, if a column is hidden after it is automatically sized to fit a large cell value, the hidden column will not change its size if the row containing the large cell value is deleted. Automatic sizing does not occur when visibility changes, so changing the column Visible property back to true will not force it to recalculate its size based on its current contents.
Programmatic content-based resizing affects rows, columns, and headers regardless of their visibility.
5.3.4 Programmatic Resizing
When automatic sizing is disabled, you can programmatically set the exact width or height of rows, columns, or headers through the following properties:
- RowHeadersWidth
- ColumnHeadersHeight
- DataGridViewRow.Height
- DataGridViewColumn.Width
You can also programmatically resize rows, columns, and headers to fit their contents using the following methods:
- AutoResizeColumn
- AutoResizeColumns
- AutoResizeColumnHeadersHeight
- AutoResizeRow
- AutoResizeRows
- AutoResizeRowHeadersWidth
These methods will resize rows, columns, or headers once rather than configuring them for continuous resizing. The new sizes are automatically calculated to display all cell contents without clipping. When you programmatically resize columns that have InheritedAutoSizeMode property values of Fill, however, the calculated content-based widths are used to proportionally adjust the column FillWeight property values, and the actually column widths are then calculated according to these new proportions so that all columns fill the available display area of the control.
Programmatic resizing is useful to avoid performance penalties with continuous resizing. It is also useful to provide initial sizes for user-resizable rows, columns, and headers, and for column fill mode.
You will typically call the programmatic resizing methods at specific times. For example, you might programmatically resize all columns immediately after loading data, or you might programmatically resize a specific row after a particular cell value has been modified.
5.3.5 Customizing Content-based Sizing Behavior
You can customize sizing behaviors when working with derived DataGridView cell, row, and column types by overriding the DataGridViewCell.GetPreferredSize(), DataGridViewRow.GetPreferredHeight(), or DataGridViewColumn.GetPreferredWidth() methods or by calling protected resizing method overloads in a derived DataGridView control. The protected resizing method overloads are designed to work in pairs to achieve an ideal cell height-to-width ratio, avoiding overly wide or tall cells. For example, if you call the AutoResizeRows(DataGridViewAutoSizeRowsMode,Boolean) overload of the AutoResizeRows method and pass in a value of false for the Boolean parameter, the overload will calculate the ideal heights and widths for cells in the row, but it will adjust the row heights only. You must then call the AutoResizeColumns method to adjust the column widths to the calculated ideal.
5.3.6 Content-based Sizing Options
The enumerations used by sizing properties and methods have similar values for content-based sizing. With these values, you can limit which cells are used to calculate the preferred sizes. For all sizing enumerations, values with names that refer to displayed cells limit their calculations to cells in displayed rows. Excluding rows is useful to avoid a performance penalty when you are working with a large quantity of rows. You can also restrict calculations to cell values in header or nonheader cells.
5.4 Selection modes
The DataGridView control provides you with a variety of options for configuring how users can select cells, rows, and columns. For example, you can enable single or multiple selection, selection of whole rows or columns when users click cells, or selection of whole rows or columns only when users click their headers, which enables cell selection as well. If you want to provide your own user interface for selection, you can disable ordinary selection and handle all selection programmatically. Additionally, you can enable users to copy the selected values to the Clipboard.
Sometimes you want your application to perform actions based on user selections within a DataGridView control. Depending on the actions, you may want to restrict the kinds of selection that are possible. For example, suppose your application can print a report for the currently selected record. In this case, you may want to configure the DataGridView control so that clicking anywhere within a row always selects the entire row, and so that only one row at a time can be selected.
You can specify the selections allowed by setting the SelectionMode property to one of the following DataGridViewSelectionMode enumeration values.
DataGridViewSelectionMode value |
Description |
CellSelect |
单击单元格以选中它,行列标题不能用于选择。 |
ColumnHeaderSelect |
单击单元格以选中它,单击列标题选中整列。此时列标题不能用于排序。 |
FullColumnSelect |
单击单元格或列标题会选中它们所在的列,此时列标题不能用于排序。 |
FullRowSelect |
单击单元格或行标题会选中它们所在的行。 |
RowHeaderSelect |
DGV的默认选择模式,单击单元格选中该单元格,单击行标题则选中整行。 |
注意: 在运行时改变选择模式会自动清除当前选择的内容。
By default, users can select multiple rows, columns, or cells by dragging with the mouse, pressing CTRL or SHIFT while selecting to extend or modify a selection, or clicking the top-left header cell to select all cells in the control. To prevent this behavior, set the MultiSelect property to false.
The FullRowSelect and RowHeaderSelect modes allow users to delete rows by selecting them and pressing the DELETE key. Users can delete rows only when the current cell is not in edit mode, the AllowUserToDeleteRows property is set to true, and the underlying data source supports user-driven row deletion. Note that these settings do not prevent programmatic row deletion.
5.4.1 Programmatic Selection
The current selection mode restricts the behavior of programmatic selection as well as user selection. You can change the current selection programmatically by setting the Selected property of any cells, rows, or columns present in the DataGridView control. You can also select all cells in the control through the SelectAll method, depending on the selection mode. To clear the selection, use the ClearSelection method.
If the MultiSelect property is set to true, you can add DataGridView elements to or remove them from the selection by changing the Selected property of the element. Otherwise, setting the Selected property to true for one element automatically removes other elements from the selection.
注意:改变CurrentCell属性的值不会改变当前选择的内容。
通过SelectedCells、SelectedRows和SelectedColumns属性你可以访问当前选中的单元格、行和列。不过当所有单元格都被选中的时候,使用这些属性效率会比较低,为此可首先使用AreAllCellsSelected方法查看是否已选中全部单元格。此外,访问这些属性来查看选中单元格、行和列的数目效率也比较低,此时应该使用GetCellCount、GetRowCount和GetColumnCount方法,传给它们的参数为DataGridViewElementStates.Selected。
5.5 滚动(Scrolling)
DataGridView毫无疑问地会提供对水平和垂直滚动条的支持,它同时也支持使用鼠标滚轮进行垂直滚动。水平方向的滚动基于像素值,而垂直方向的滚动则基于行的索引,DataGridView不支持垂直方向的基于像素值的滚动。
5.5.1 Scroll event
As you scroll the DataGridView raises the Scroll event that allows you to be notified that scrolling is occurring. The Orientation property on the scroll event args lets you know the scroll direction.
5.5.2 Scroll bars
The DataGridView provides access to the scrollbars that it displays via the protected HorizontalScrollBar and VerticalScrollBar properties. Accessing these ScrollBar controls directly allow you to have finer control over scrolling.
5.5.3 Scrolling Properties
There are a set of properties that provide greater level of details on how the DataGridView is scrolled. The diagram highlights these properties and their values at this state. The properties are read/write except for the FirstDisplayedScrollingColumnHiddenWidth and VerticalScrollingOffset properties.
5.6 Sorting
By default, users can sort the data in a DataGridView control by clicking the header of a text box column. You can modify the SortMode property of specific columns to allow users to sort by other column types when it makes sense to do so. You can also sort the data programmatically by any column, or by multiple columns.
DataGridView columns have three sort modes. The sort mode for each column is specified through the SortMode property of the column, which can be set to one of the following DataGridViewColumnSortMode enumeration values.
DataGridViewColumnSortMode value |
Description |
Automatic |
Default for text box columns. Unless column headers are used for selection, clicking the column header automatically sorts theDataGridView by this column and displays a glyph indicating the sort order. |
NotSortable |
Default for non–text box columns. You can sort this column programmatically; however, it is not intended for sorting, so no space is reserved for the sorting glyph. |
Programmatic |
You can sort this column programmatically, and space is reserved for the sorting glyph. |
You might want to change the sort mode for a column that defaults to NotSortable if it contains values that can be meaningfully ordered. For example, if you have a database column containing numbers that represent item states, you can display these numbers as corresponding icons by binding an image column to the database column. You can then change the numerical cell values into image display values in a handler for the CellFormatting event. In this case, setting the SortMode property to Automatic will enable your users to sort the column. Automatic sorting will enable your users to group items that have the same state even if the states corresponding to the numbers do not have a natural sequence. Check box columns are another example where automatic sorting is useful for grouping items in the same state.
You can sort a DataGridView programmatically by the values in any column or in multiple columns, regardless of the SortMode settings. Programmatic sorting is useful when you want to provide your own user interface (UI) for sorting or when you want to implement custom sorting. Providing your own sorting UI is useful, for example, when you set the DataGridView selection mode to enable column header selection. In this case, although the column headers cannot be used for sorting, you still want the headers to display the appropriate sorting glyph, so you would set the SortMode property to Programmatic.
Columns set to programmatic sort mode do not automatically display a sorting glyph. For these columns, you must display the glyph yourself by setting the DataGridViewColumnHeaderCell.SortGlyphDirection property. This is necessary if you want flexibility in custom sorting. For example, if you sort the DataGridView by multiple columns, you might want to display multiple sorting glyphs or no sorting glyph.
Although you can programmatically sort a DataGridView by any column, some columns, such as button columns, might not contain values that can be meaningfully ordered. For these columns, a SortMode property setting of NotSortable indicates that it will never be used for sorting, so there is no need to reserve space in the header for the sorting glyph.
When a DataGridView is sorted, you can determine both the sort column and the sort order by checking the values of the SortedColumn and SortOrder properties. These values are not meaningful after a custom sorting operation. For more information about custom sorting, see the Custom Sorting section later in this topic.
When a DataGridView control containing both bound and unbound columns is sorted, the values in the unbound columns cannot be maintained automatically. To maintain these values, you must implement virtual mode by setting the VirtualMode property to true and handling the CellValueNeeded and CellValuePushed events.
5.6.1 Programmatic Sorting
You can sort a DataGridView programmatically by calling its Sort method.
The Sort(DataGridViewColumn,ListSortDirection) overload of the Sort method takes a DataGridViewColumn and a ListSortDirection enumeration value as parameters. This overload is useful when sorting by columns with values that can be meaningfully ordered, but which you do not want to configure for automatic sorting. When you call this overload and pass in a column with a SortMode property value of DataGridViewColumnSortMode.Automatic, the SortedColumn and SortOrder properties are set automatically and the appropriate sorting glyph appears in the column header.
Note: When the DataGridView control is bound to an external data source by setting the DataSource property, the Sort(DataGridViewColumn,ListSortDirection) method overload does not work for unbound columns. Additionally, when the VirtualMode property is true, you can call this overload only for bound columns. To determine whether a column is data-bound, check the IsDataBound property value. Sorting unbound columns in bound mode is not supported.
5.6.2 Custom Sorting
You can customize DataGridView by using the Sort(IComparer) overload of the Sort method or by handling the SortCompare event.
The Sort(IComparer) method overload takes an instance of a class that implements the IComparer interface as a parameter. This overload is useful when you want to provide custom sorting; for example, when the values in a column do not have a natural sort order or when the natural sort order is inappropriate. In this case, you cannot use automatic sorting, but you might still want your users to sort by clicking the column headers. You can call this overload in a handler for the ColumnHeaderMouseClick event if you do not use column headers for selection.
Note: The Sort(IComparer) method overload works only when the DataGridView control is not bound to an external data source and the VirtualMode property value is false. To customize sorting for columns bound to an external data source, you must use the sorting operations provided by the data source. In virtual mode, you must provide your own sorting operations for unbound columns.
To use the Sort(IComparer) method overload, you must create your own class that implements the IComparer interface. This interface requires your class to implement the IComparer.Compare(Object) method, to which the DataGridView passes DataGridViewRow objects as input when the Sort(IComparer) method overload is called. With this, you can calculate the correct row ordering based on the values in any column.
The Sort(IComparer) method overload does not set the SortedColumn and SortOrder properties, so you must always set the DataGridViewColumnHeaderCell.SortGlyphDirection property to display the sorting glyph.
As an alternative to the Sort(IComparer) method overload, you can provide custom sorting by implementing a handler for the SortCompare event. This event occurs when users click the headers of columns configured for automatic sorting or when you call the Sort(DataGridViewColumn,ListSortDirection) overload of the Sort method. The event occurs for each pair of rows in the control, enabling you to calculate their correct order.
Note: The SortCompare event does not occur when the DataSource property is set or when the VirtualMode property value is true.
5.6.3 Common questions and scenarios
1) 如何避免用户对列排序?
2) 如何针对多个列排序?
5.7 Border styles
With the DataGridView control, you can customize the appearance of the control's border and gridlines to improve the user experience. You can modify the gridline color and the control border style in addition to the border styles for the cells within the control. The gridline color is controlled via the GridColor property. You can also apply different cell border styles for ordinary cells, row header cells, and column header cells. For advanced border styles the DataGridView provides the advanced border style properties as well.
Note: The gridline color is used only with the Single, SingleHorizontal, and SingleVertical values of the DataGridViewCellBorderStyle enumeration and the Single value of the DataGridViewHeaderBorderStyle enumeration. The other values of these enumerations use colors specified by the operating system. Additionally, when visual styles are enabled on Windows XP and above, the GridColor property value is not used.
5.7.1 Standard Border Styles
Standard border styles are controlled via the CellBorderStyle, RowHeadersBorderStyle, and ColumnHeadersBorderStyle properties.
The following table identifies the standard border styles available via the :
BorderStyle value |
Description |
Fixed3D |
A three-dimensional border. |
FixedSingle |
A single-line border. |
None |
No border. |
5.7.2 Advanced Border Styles
The DataGridView control allows you to fully customize its appearance, including the borders of the cells and headers. The DataGridView has CellBorderStyle, ColumnHeadersBorderStyle, and RowHeadersBorderStyle properties that allow you to set the appearance of the cell border. However, if you need to further customize the borders, the DataGridViewAdvancedBorderStyle class allows you to set the style of the border on the individual sides of the cells. The Left, Right, Top, and Bottom properties of DataGridViewAdvancedBorderStyle represent the left, right, top, and bottom border of a cell, respectively. You can set these properties on the AdvancedCellBorderStyle, AdvancedColumnHeadersBorderStyle, AdvancedRowHeadersBorderStyle properties of the DataGridView to produce various appearances for the borders between the cells.
The following table identifies the advanced border styles available that can be set for the left, right, top and bottom parts. Note that some combinations are not valid.
BorderStyle value |
Description |
Inset |
A three-dimensional border. |
InsetDouble |
A single-line border. |
None |
No border. |
NotSet |
The border is not set |
Outset |
A single-line raised border |
OutsetDouble |
A double-line raised border |
OutsetPartial |
A single-line border containing a raised portion |
Single |
A single-line border |
5.8 Enter-Edit modes
By default, users can edit the contents of the current DataGridView text box cell by typing in it or pressing F2. This puts the cell in edit mode if all of the following conditions are met:
- The underlying data source supports editing.
- The DataGridView control is enabled.
- The EditMode property value is not EditProgrammatically.
- The ReadOnly properties of the cell, row, column, and control are all set to false.
In edit mode, the user can change the cell value and press ENTER to commit the change or ESC to revert the cell to its original value.
You can configure a DataGridView control so that a cell enters edit mode as soon as it becomes the current cell. The behavior of the ENTER and ESC keys is unchanged in this case, but the cell remains in edit mode after the value is committed or reverted. You can also configure the control so that cells enter edit mode only when users type in the cell or only when users press F2. Finally, you can prevent cells from entering edit mode except when you call the BeginEdit method.
The following table describes the different edit modes available:
EditMode value |
Description |
EditOnEnter |
Editing begins when the cell receives focus. This mode is useful when pressing the TAB key to enter values across a row, or when pressing the ENTER key to enter values down a column. |
EditOnF2 |
Editing begins when F2 is pressed while the cell has focus. This mode places the selection point at the end of the cell contents. |
EditOnKeystroke |
Editing begins when any alphanumeric key is pressed while the cell has focus. |
EditOnKeystrokeOrF2 |
Editing begins when any alphanumeric key or F2 is pressed while the cell has focus. |
EditProgrammatically |
Editing begins only when the BeginEdit method is called. |
5.9 Clipboard copy modes
When you enable cell copying, you make the data in your DataGridView control easily accessible to other applications through the Clipboard. The DataGridView control copies the text representation of each selected cell to the Clipboard. This value is the cell value converted to a string or, for image cells, the value of the Description property. The content is then added to the Clipboard as tab-delimited text values for pasting into applications like Notepad and Excel, and as an HTML-formatted table for pasting into applications like Word.
You can configure cell copying to copy cell values only, to include row and column header text in the Clipboard data, or to include header text only when users select entire rows or columns.
The following table identifies the different clipboard copy modes:
Clipboard Copy modes |
Description |
Disable |
Copying to the Clipboard is disabled. |
EnableAlwaysIncludeHeaderText |
The text values of selected cells can be copied to the Clipboard. Header text is included for rows and columns that contain selected cells. |
EnableWithAutoHeaderText |
The text values of selected cells can be copied to the Clipboard. Row or column header text is included for rows or columns that contain selected cells only when the SelectionMode property is set to RowHeaderSelect or ColumnHeaderSelect and at least one header is selected. |
EnableWithoutHeaderText |
The text values of selected cells can be copied to the Clipboard. Header text is not included. |
Depending on the selection mode, users can select multiple disconnected groups of cells. When a user copies cells to the Clipboard, rows and columns with no selected cells are not copied. All other rows or columns become rows and columns in the table of data copied to the Clipboard. Unselected cells in these rows or columns are copied as blank placeholders to the Clipboard.
When users copy content, the DataGridView control adds a DataObject to the Clipboard. This data object is retrieved from the GetClipboardContent() method. You can call this method when you want to programmatically add the data object to the Clipboard. The GetClipboardContent() method retrieves values for individual cells by calling the DataGridViewCell.GetClipboardContent() method. You can override either or both of these methods in derived classes to customize the layout of copied cells or to support additional data formats.
5.10 Frozen columns/rows
When users view data sometimes they need to refer to a single column or set of columns frequently. For example, when displaying a table of customer information that contains many columns, it is useful to display the customer name at all times while enabling other columns to scroll outside the visible region.
To achieve this behavior, you can freeze columns in the control. This is done via setting the Frozen property on the column or row. When you freeze a column, all the columns to its left (or to its right in right-to-left language scripts) are frozen as well. Frozen columns remain in place while all other columns can scroll. Rows act in similar fashion: all rows before the frozen row are frozen as well and remain in place while the non frozen rows can scroll.
5.11 Implementing Custom cells and editing controls/cells
You can implement the IDataGridViewEditingCell interface in your derived cell class to create a cell type that has editing functionality but does not host a control in editing mode. To create a control that you can host in a cell in editing mode, you can implement the IDataGridViewEditingControl interface in a class derived from Control.
5.11.1 IDataGridViewEditingControl
Cells that support advanced editing functionality typically use a hosted control that is derived from a Windows Forms control. This interface is implemented by editing controls, such as DataGridViewComboBoxEditingControl and DataGridViewTextBoxEditingControl, that are hosted by the corresponding DataGridView cells, such as DataGridViewComboBoxCell and DataGridViewTextBoxCell, when they are in edit mode.
Cell types that can that host editing controls set their EditType property to a Type representing the editing control type.
5.11.2 IDataGridViewEditingCell
This interface is implemented by classes to provide a user interface (UI) for specifying values without hosting an editing control. The UI in this case is displayed regardless of whether the cell is in edit mode. The DataGridViewCheckBoxCell is an example of a cell that implements the IDataGridViewEditingCell interface.
Other cell types, such as DataGridViewButtonCell, provide a UI but do not store user-specified values. In this case, the cell type does not implement IDataGridViewEditingCell or host an editing control.
5.12 Virtual mode
With virtual mode, you can manage the interaction between the DataGridView control and a custom data cache. To implement virtual mode, set the VirtualMode property to true and handle one or more of the events described in this topic. You will typically handle at least the CellValueNeeded event, which enables the control look up values in the data cache.
5.12.1 Bound Mode and Virtual Mode
Virtual mode is necessary only when you need to supplement or replace bound mode. In bound mode, you set the DataSource property and the control automatically loads the data from the specified source and submits user changes back to it. You can control which of the bound columns are displayed, and the data source itself typically handles operations such as sorting.
5.12.2 Supplementing Bound Mode
You can supplement bound mode by displaying unbound columns along with the bound columns. This is sometimes called "mixed mode" and is useful for displaying things like calculated values or user-interface (UI) controls.
Because unbound columns are outside the data source, they are ignored by the data source's sorting operations. Therefore, when you enable sorting in mixed mode, you must manage the unbound data in a local cache and implement virtual mode to let the DataGridView control interact with it.
5.12.3 Common questions and scenarios
1) How do I show unbound data along with bound data?
2) How do I show data that comes from two tables?
5.12.4 Replacing Bound Mode
If bound mode does not meet your performance needs, you can manage all your data in a custom cache through virtual-mode event handlers. For example, you can use virtual mode to implement a just-in-time data loading mechanism that retrieves only as much data from a networked database as is necessary for optimal performance. This scenario is particularly useful when working with large amounts of data over a slow network connection or with client machines that have a limited amount of RAM or storage space.
5.12.5 Virtual-Mode Events
If your data is read-only, the CellValueNeeded event may be the only event you will need to handle. Additional virtual-mode events let you enable specific functionality like user edits, row addition and deletion, and row-level transactions.
Some standard DataGridView events (such as events that occur when users add or delete rows, or when cell values are edited, parsed, validated, or formatted) are useful in virtual mode, as well. You can also handle events that let you maintain values not typically stored in a bound data source, such as cell ToolTip text, cell and row error text, cell and row shortcut menu data, and row height data.
The following events occur only when the VirtualMode property is set to true.
Event |
Description |
CellValueNeeded |
Used by the control to retrieve a cell value from the data cache for display. This event occurs only for cells in unbound columns. |
CellValuePushed |
Used by the control to commit user input for a cell to the data cache. This event occurs only for cells in unbound columns. Call the UpdateCellValue method when changing a cached value outside of a CellValuePushed event handler to ensure that the current value is displayed in the control and to apply any automatic sizing modes currently in effect. |
NewRowNeeded |
Used by the control to indicate the need for a new row in the data cache. |
RowDirtyStateNeeded |
Used by the control to determine whether a row has any uncommitted changes. |
CancelRowEdit |
Used by the control to indicate that a row should revert to its cached values. |
The following events are useful in virtual mode, but can be used regardless of the VirtualMode property setting.
Events |
Description |
UserDeletingRow UserDeletedRow RowsRemoved RowsAdded |
Used by the control to indicate when rows are deleted or added, letting you update the data cache accordingly. |
CellFormatting CellParsing CellValidating CellValidated RowValidating RowValidated |
Used by the control to format cell values for display and to parse and validate user input. |
CellToolTipTextNeeded |
Used by the control to retrieve cell ToolTip text when theDataSource property is set or the VirtualMode property is true. Cell ToolTips are displayed only when the ShowCellToolTips property value is true. |
CellErrorTextNeeded RowErrorTextNeeded |
Used by the control to retrieve cell or row error text when theDataSource property is set or the VirtualMode property is true. Call the UpdateCellErrorText method or the UpdateRowErrorText method when you change the cell or row error text to ensure that the current value is displayed in the control. Cell and row error glyphs are displayed when the ShowCellErrors and ShowRowErrors property values are true. |
CellContextMenuStripNeeded RowContextMenuStripNeeded |
Used by the control to retrieve a cell or row ContextMenuStrip when the control DataSource property is set or theVirtualMode property is true. |
RowHeightInfoNeeded RowHeightInfoPushed |
Used by the control to retrieve or store row height information in the data cache. Call the UpdateRowHeightInfo method when changing the cached row height information outside of aRowHeightInfoPushed event handler to ensure that the current value is used in the display of the control. |
5.12.6 Best Practices in Virtual Mode
If you are implementing virtual mode in order to work efficiently with large amounts of data, you will also want to ensure that you are working efficiently with the DataGridView control itself. See below for more information on best practices
5.13 容量(Capacity)
In general, the DataGridView does not have any hard-coded capacity limits. The grid was designed so that more and more content can be added as machines become faster and have more memory. That said, the grid was not designed to deal with large number of columns. If you add more than 300 columns you will start to notice a degradation in performance as our performance tuning of the grid was not designed for this. If you need a grid with large amounts of columns then the DataGridView might not meet your needs. Regarding the number of rows supported, the DataGridView is bound by memory constraints. When using Virtual mode you can easily support over 2 million rows. Check out the best practices section below for information on things you can do (and not do) to improve memory usage and performance.
6 最佳实践(Best Practices)
The DataGridView control is designed to provide maximum scalability. If you need to display large amounts of data, you should follow the guidelines described in this topic to avoid consuming large amounts of memory or degrading the responsiveness of the user interface (UI).
6.1 Using Cell Styles Efficiently
Each cell, row, and column can have its own style information. Style information is stored in DataGridViewCellStyle objects. Creating cell style objects for many individual DataGridView elements can be inefficient, especially when working with large amounts of data. To avoid a performance impact, use the following guidelines:
- Avoid setting cell style properties for individual DataGridViewCell or DataGridViewRow objects. This includes the row object specified by the RowTemplate property. Each new row that is cloned from the row template will receive its own copy of the template's cell style object. For maximum scalability, set cell style properties at the DataGridView level. For example, set the DefaultCellStyle property rather than the DataGridViewCell.Style property.
- If some cells require formatting other than default formatting, use the same DataGridViewCellStyle instance across groups of cells, rows, or columns. Avoid directly setting properties of type DataGridViewCellStyle on individual cells, rows, and columns. For an example of cell style sharing, see How to: Set Default Cell Styles for the Windows Forms DataGridView Control. You can also avoid a performance penalty when setting cell styles individually by handling the CellFormatting event handler. For an example, see How to: Customize Data Formatting in the Windows Forms DataGridView Control.
- When determining a cell's style, use the DataGridViewCell.InheritedStyle property rather than the DataGridViewCell.Style property. Accessing the Style property creates a new instance of the DataGridViewCellStyle class if the property has not already been used. Additionally, this object might not contain the complete style information for the cell if some styles are inherited from the row, column, or control. For more information about cell style inheritance, see Cell Styles in the Windows Forms DataGridView Control.
6.2 Using Shortcut Menus Efficiently
Each cell, row, and column can have its own shortcut menu. Shortcut menus in the DataGridView control are represented by ContextMenuStrip controls. Just as with cell style objects, creating shortcut menus for many individual DataGridView elements will negatively impact performance. To avoid this penalty, use the following guidelines:
- Avoid creating shortcut menus for individual cells and rows. This includes the row template, which is cloned along with its shortcut menu when new rows are added to the control. For maximum scalability, use only the control's ContextMenuStrip property to specify a single shortcut menu for the entire control.
- If you require multiple shortcut menus for multiple rows or cells, handle the CellContextMenuStripNeeded or RowContextMenuStripNeeded events. These events let you manage the shortcut menu objects yourself, allowing you to tune performance.
6.3 Using Automatic Resizing Efficiently
Rows, columns, and headers can be automatically resized as cell content changes so that the entire contents of cells are displayed without clipping. Changing sizing modes can also resize rows, columns, and headers. To determine the correct size, the DataGridView control must examine the value of each cell that it must accommodate. When working with large data sets, this analysis can negatively impact the performance of the control when automatic resizing occurs. To avoid performance penalties, use the following guidelines:
- Avoid using automatic sizing on a DataGridView control with a large set of rows. If you do use automatic sizing, only resize based on the displayed rows. Use only the displayed rows in virtual mode as well.
- For rows and columns, use the DisplayedCells or DisplayedCellsExceptHeaders field of the DataGridViewAutoSizeRowsMode, DataGridViewAutoSizeColumnsMode, and DataGridViewAutoSizeColumnMode enumerations.
- For row headers, use the AutoSizeToDisplayedHeaders or AutoSizeToFirstHeader field of the DataGridViewRowHeadersWidthSizeMode enumeration.
- For maximum scalability, turn off automatic sizing and use programmatic resizing.
6.4 Using the Selected Cells, Rows, and Columns Collections Efficiently
The SelectedCells collection does not perform efficiently with large selections. The SelectedRows and SelectedColumns collections can also be inefficient, although to a lesser degree because there are many fewer rows than cells in a typical DataGridView control, and many fewer columns than rows. To avoid performance penalties when working with these collections, use the following guidelines:
- To determine whether all the cells in the DataGridView have been selected before you access the contents of the SelectedCells collection, check the return value of the AreAllCellsSelected method. Note, however, that this method can cause rows to become unshared. For more information, see the next section.
- Avoid using the Count property of the DataGridViewSelectedCellCollection to determine the number of selected cells. Instead, use the GetCellCount() method and pass in the DataGridViewElementStates.Selected value. Similarly, use the DataGridViewRowCollection.GetRowCount() and DataGridViewColumnCollection.GetColumnCount() methods to determine the number of selected elements, rather than accessing the selected row and column collections.
- Avoid cell-based selection modes. Instead, set the SelectionMode property to FullRowSelect or FullColumnSelect.
6.5 Using Shared Rows
Efficient memory use is achieved in the DataGridView control through shared rows. Rows will share as much information about their appearance and behavior as possible by sharing instances of the DataGridViewRow class.
While sharing row instances saves memory, rows can easily become unshared. For example, whenever a user interacts directly with a cell, its row becomes unshared. Because this cannot be avoided, the guidelines in this topic are useful only when working with very large amounts of data and only when users will interact with a relatively small part of the data each time your program is run.
A row cannot be shared in an unbound DataGridView control if any of its cells contain values. When the DataGridView control is bound to an external data source or when you implement virtual mode and provide your own data source, the cell values are stored outside the control rather than in cell objects, allowing the rows to be shared.
A row object can only be shared if the state of all its cells can be determined from the state of the row and the states of the columns containing the cells. If you change the state of a cell so that it can no longer be deduced from the state of its row and column, the row cannot be shared.
For example, a row cannot be shared in any of the following situations:
- The row contains a single selected cell that is not in a selected column.
- The row contains a cell with its ToolTipText or ContextMenuStrip properties set.
- The row contains a DataGridViewComboBoxCell with its Items property set.
In bound mode or virtual mode, you can provide ToolTips and shortcut menus for individual cells by handling the CellToolTipTextNeeded and CellContextMenuStripNeeded events.
The DataGridView control will automatically attempt to use shared rows whenever rows are added to the DataGridViewRowCollection. Use the following guidelines to ensure that rows are shared:
- Avoid calling the Add(Object[]) overload of the Add method and the Insert(Object[]) overload of the Insert method of the Rows collection. These overloads automatically create unshared rows.
- Be sure that the row specified in the RowTemplate property can be shared in the following cases:
- When calling the Add() or Add(Int) overloads of the Add method or the Insert(Int, Int) overload of the Insert method of the Rows collection.
- When increasing the value of the RowCount property.
- When setting the DataSource property.
- Be sure that the row indicated by the indexSource parameter can be shared when calling the AddCopy, AddCopies, InsertCopy, and InsertCopies methods of the Rows collection.
- Be sure that the specified row or rows can be shared when calling the Add(DataGridViewRow) overload of the Add method, the AddRange method, the Insert(Int32,DataGridViewRow) overload of the Insert method, and the InsertRange method of the Rows collection.
To determine whether a row is shared, use the DataGridViewRowCollection.SharedRow(Int) method to retrieve the row object, and then check the object's Index property. Shared rows always have an Index property value of –1.
6.6 Preventing Rows from Becoming Unshared
Shared rows can become unshared as a result of code or user action. To avoid a performance impact, you should avoid causing rows to become unshared. During application development, you can handle the RowUnshared event to determine when rows become unshared. This is useful when debugging row-sharing problems.
To prevent rows from becoming unshared, use the following guidelines:
- Avoid indexing the Rows collection or iterating through it with a foreach loop. You will not typically need to access rows directly. DataGridView methods that operate on rows take row index arguments rather than row instances. Additionally, handlers for row-related events receive event argument objects with row properties that you can use to manipulate rows without causing them to become unshared.
- If you need to access a row object, use the DataGridViewRowCollection.SharedRow(Int) method and pass in the row's actual index. Note, however, that modifying a shared row object retrieved through this method will modify all the rows that share this object. The row for new records is not shared with other rows, however, so it will not be affected when you modify any other row. Note also that different rows represented by a shared row may have different shortcut menus. To retrieve the correct shortcut menu from a shared row instance, use the GetContextMenuStrip method and pass in the row's actual index. If you access the shared row's ContextMenuStrip property instead, it will use the shared row index of -1 and will not retrieve the correct shortcut menu.
- Avoid indexing the DataGridViewRow.Cells collection. Accessing a cell directly will cause its parent row to become unshared, instantiating a new DataGridViewRow. Handlers for cell-related events receive event argument objects with cell properties that you can use to manipulate cells without causing rows to become unshared. You can also use the CurrentCellAddress property to retrieve the row and column indexes of the current cell without accessing the cell directly.
- Avoid cell-based selection modes. These modes cause rows to become unshared. Instead, set the SelectionMode property to DataGridViewSelectionMode.FullRowSelect or DataGridViewSelectionMode.FullColumnSelect.
- Do not handle the DataGridViewRowCollection.CollectionChanged or RowStateChanged events. These events cause rows to become unshared. Also, do not call the DataGridViewRowCollection.OnCollectionChanged(CollectionChangeEventArgs) or OnRowStateChanged(Int,DataGridViewRowStateChangedEventArgs) methods, which raise these events.
- Do not access the SelectedCells collection when the SelectionMode property value is FullColumnSelect, ColumnHeaderSelect, FullRowSelect, or RowHeaderSelect. This causes all selected rows to become unshared.
- Do not call the AreAllCellsSelected(boolean) method. This method can cause rows to become unshared.
- Do not call the SelectAll method when the SelectionMode property value is CellSelect. This causes all rows to become unshared.
- Do not set the ReadOnly or Selected property of a cell to false when the corresponding property in its column is set to true. This causes all rows to become unshared.
- Do not access the DataGridViewRowCollection.List property. This causes all rows to become unshared.
- Do not call the Sort(IComparer) overload of the Sort method. Sorting with a custom comparer causes all rows to become unshared.
你可以检索各种属性的DataGridView,DataGridViewColumn的,的DataGridViewRow,和DataGridViewCell类及其派生类DataGridViewCellStyle对象。如果其中一个属性尚未设置,检索其值将创建一个新的DataGridViewCellStyle对象。您还可以将自己的DataGridViewCellStyle对象,并将它们分配给这些属性。
您可以通过共享的DataGridViewCellStyle避免不必要的在多个DataGridView元素对象的样式信息的重复。因为在控制,列集的风格,和行各层面渗透到细胞水平的水平了,你还可以通过设置避免只在每个级别,从不同层次上的风格样式属性重复。这是进行了更详细的样式继承节如下。
下表列出了获取或设置DataGridViewCellStyle对象的主要属性。
物业类的描述
的DefaultCellStyle的DataGridView,DataGridViewColumn的,的DataGridViewRow和派生类获取或设置所有单元格中使用的整个控制(包括标题单元格)的默认风格,在一列,或在一排。
RowsDefaultCellStyle的DataGridView获取或设置默认单元格的控件中的所有行使用的样式。这不包括标题单元格。
AlternatingRowsDefaultCellStyle的DataGridView获取或设置默认单元格的行交替使用的样式的控制。用于创建一个总账般的效果。
RowHeadersDefaultCellStyle的DataGridView获取或设置该控件的默认单元格的行标题使用的样式。由当前主题重写如果启用视觉样式。
ColumnHeadersDefaultCellStyle的DataGridView获取或设置该控件的默认单元格的列标题使用的样式。由当前主题重写如果启用视觉样式。
风格的DataGridViewCell和派生类获取或设置在细胞水平上指定的样式。这些样式覆盖上级继承的。
InheritedStyle的DataGridViewCell,的DataGridViewRow,DataGridViewColumn和派生类获取所有的风格,包括从上级继承样式应用到当前单元格,行或列。
如上所述,得到了一个样式属性的值会自动实例化一个新的DataGridViewCellStyle对象如果属性尚未以前设置。为了避免不必要地创建这些对象,行和列类有一个HasDefaultCellStyle属性,您可以检查以确定是否DefaultCellStyle属性已设置。同样,细胞类具有HasStyle属性,指示是否Style属性已设置。
每个属性的样式上有一个相应的PropertyNameChanged DataGridView控件的事件。对于行,列和单元格属性,事件名称开头“行”,“列”,或“细胞”(例如,RowDefaultCellStyleChanged)。这些事件发生时,每一个对应的样式属性设置为不同的DataGridViewCellStyle对象。这些事件不会发生当您检索从样式属性的DataGridViewCellStyle对象,并修改其属性值。为了应对变化的单元格样式对象本身,处理CellStyleContentChanged事件。
5.1.3样式继承
每个DataGridViewCell的会从它的InheritedStyle属性它的外观。 DataGridViewCellStyle对象的此属性返回继承从类型DataGridViewCellStyle的属性层次的价值。下面列出了这些属性的顺序在其中非头细胞InheritedStyle获取其值。
1。 DataGridViewCell.Style
2。 DataGridViewRow.DefaultCellStyle
3。 AlternatingRowsDefaultCellStyle(只适用于奇数行单元格指数)
4。 RowsDefaultCellStyle
5。 DataGridViewColumn.DefaultCellStyle
6。的DefaultCellStyle
对于行和列标题单元格,InheritedStyle属性填充的值是从给定的顺序在下面的列表源属性。
1。 DataGridViewCell.Style
2。 ColumnHeadersDefaultCellStyle或RowHeadersDefaultCellStyle
3。的DefaultCellStyle
下图演示了这个过程。
您还可以通过特定的行和列继承的样式。列InheritedStyle财产继承了以下属性的值。
1。 DataGridViewColumn.DefaultCellStyle
2。的DefaultCellStyle
该行InheritedStyle财产继承了以下属性的值。
1。 DataGridViewRow.DefaultCellStyle
2。 AlternatingRowsDefaultCellStyle(只适用于奇数行单元格指数)
3。 RowsDefaultCellStyle
4。的DefaultCellStyle
对于每一个由InheritedStyle属性返回一个DataGridViewCellStyle对象属性,属性值是从第一个单元格样式列表,在适当的相应的属性设置为除DataGridViewCellStyle类的默认值等。
下表说明了一个例子细胞ForeColor属性的值是从包含列继承。
类型DataGridViewCellStyle的范例前景色为检索对象的价值属性
DataGridViewCell.Style Color.Empty
DataGridViewRow.DefaultCellStyle Color.Red
AlternatingRowsDefaultCellStyle Color.Empty
RowsDefaultCellStyle Color.Empty
DataGridViewColumn.DefaultCellStyle Color.DarkBlue
的DefaultCellStyle Color.Black
在这种情况下,从单元格的行System.Drawing.Color.Red值是第一个在名单上的实际价值。这成为该单元格的InheritedStyle ForeColor属性值。
下图说明了不同的DataGridViewCellStyle属性可以继承他们的价值观不同的地方。
通过利用样式继承的优势,可以提供,而无需指定相同的信息在多个地方为整个控制适当的样式。
虽然标题单元格样式继承中所描述的身份参加,由DataGridView控件的ColumnHeadersDefaultCellStyle和RowHeadersDefaultCellStyle属性返回的对象具有初始属性值覆盖由DefaultCellStyle属性返回的对象的属性值。如果你想由DefaultCellStyle属性返回的对象设置为适用于行和列标题的属性,你必须设置由ColumnHeadersDefaultCellStyle和RowHeadersDefaultCellStyle属性返回的DataGridViewCellStyle类为默认显示对象的相应属性。
注:如果启用视觉样式,行和列标题(除TopLeftHeaderCell)会自动由当前的主题风格,覆盖了这些属性所指定的任何样式。设置EnableHeadersVisualStyle属性为false,如果你想标题不使用XP的视觉样式。
该DataGridViewButtonColumn,DataGridViewImageColumn和DataGridViewCheckBoxColumn类型还初始化由列DefaultCellStyle属性返回的对象的一些值。有关详细信息,请参见这些类型的参考文件。
5.1.4设置样式动态
要自定义,特别值的单元格的样式,实施一项CellFormatting事件的处理程序。此事件的处理程序收到的DataGridViewCellFormattingEventArgs类型的参数。此对象包含的属性,让您确定单元格的值被格式化,其在DataGridView控制地沿。此对象还包含一个CellStyle属性,初始化为单元格的InheritedStyle属性值被格式化。您可以修改单元格样式属性来指定样式的信息适合单元格的值和位置。
注:RowPrePaint和RowPostPaint事件还接收事件数据的DataGridViewCellStyle对象,但他们的案件,这是该行InheritedStyle属性为只读目的副本,以及它的变更不会影响控制。
您还可以动态改变以因应如CellMouseEnter和CellMouseLeave活动活动单个细胞的风格。例如,在为CellMouseEnter事件处理程序中,你可以存储单元格的背景颜色(通过细胞的Style属性检索)的当前值,然后将其设置为一个新的色彩,将突出显示单元格时在它的鼠标悬停。在为CellMouseLeave事件处理程序,然后就可以恢复到原来的背景颜色值。
注:缓存在细胞的Style属性中存储的值是重要的,无论是否设置特定的样式值。如果您暂时替换样式设置,恢复到原来的“未设置”国家保障,细胞会返回从更高的层次继承的样式设置。如果您需要确定在一个单元的实际效果风格的风格无论是继承,使用单元格的InheritedStyle属性。
5.2风俗画
DataGridView控件提供了多个属性,您可以用它来调整外观和基本行为(外观和感觉)的单元格,行和列。如果您有要求,超越的DataGridViewCellStyle类的功能的时候,你可以执行单元格或行的内容自定义绘制。单元格和行画自己,你可以处理各种如RowPrePaint的DataGridView,CellPainting和RowPostPaint绘画活动。
5.2.1油漆件
自定义绘制的一个重要部分是油漆部件的概念。该DataGridViewPainParts枚举用于指定哪些部分细胞油漆。枚举值可结合在一起,有一个单元不油漆涂料或特定部分。这里是不同的部分:
PaintPart为例前景色为检索对象的价值
所有的所有部件都画
背景单元格的背景是画使用单元格的背景颜色(1)
边境的边界是画
ContentBackground单元格的内容是画背景的一部分。 (2)
ContentForeground单元格的内容的前景部分是画(2)
ErrorIcon错误图标画
重点加强对单元格焦点矩形画
没有任何部分是画(1)
SelectionBackground画的背景是,如果选中该单元格被选中。
注释
1)如果一个单元格不绘制其背景则没有什么是画。一个行或列执行任何作画,确保至少细胞的背景画,或者您执行您自己的自定义背景画,否则仍然是无效的矩形(着色)。
2)每个单元确定什么前景为内容的背景和内容,如下面的列表描述的那样涂料:
细胞类型的内容前景内容背景
文本框单元格的文字是画没有画
扣式电池文字画,画按钮
组合框单元格的文字是画,画组合框
选中复选框是画没有画
链接单元格文本链接是没有画成画
图像细胞图像是画没有画
标题栏标题栏文字排序雕画
行头行头文字Current行三角形,编辑铅笔和新行的指标是画
5.2.2行预油漆涂料和邮政业
您可以通过处理一个或DataGridView.RowPrePaint和DataGridView.RowPostPaint两个事件的DataGridView行的外观。这些活动的设计,让你可以画只有你想在DataGridView控制,而让其余的油漆。例如,如果你想画一个自定义的背景,你可以处理DataGridView.RowPrePaint事件,并让自己的单个细胞涂料前景的内容。在RowPrePaint事件你可以设置PaintParts事件参数属性来轻松定制的细胞如何油漆。例如,如果您想保留的任何选择,或从绘画的焦点细胞,你RowPrePaint事件将设置像这样PaintParts属性:
e.PaintParts = DataGridViewPaintParts.All&
〜(DataGridViewPaintParts.Focus |
DataGridViewPaintParts.SelectionBackground);
这也可以写成:
e.PaintParts =(DataGridViewPaintParts.Background |
DataGridViewPaintParts.Border |
DataGridViewPaintParts.ContentBackground |
DataGridViewPaintParts.ContentForeground |
DataGridViewPaintParts.ErrorIcon);
或者,也可以让自己和油漆的细胞中添加一个自定义事件处理程序的DataGridView.RowPostPaint前景的内容。您还可以禁用油漆和涂料的一切细胞在DataGridView.RowPrePaint自己的事件处理程序
5.3 Autosizing
DataGridView控件提供了自定义的列和行的调整大小行为的许多选项。通常情况下,DataGridView单元格不调整的基础上的内容。相反,她们还会给任何显示值比电池大。如果内容可以作为一个字符串显示,该单元格显示在工具提示。
默认情况下,用户可以用鼠标拖动来显示更多信息行,列和标题分隔。用户还可以双击一个分频器来自动调整相关的行,列或标题带其内容为基础。列共享默认情况下,控制可用宽度,所以,如果用户可以调整控制,例如,如果它是一个可调整大小的对接形式,他们也可以更改列的所有可用的展示空间。
DataGridView控件提供的属性,方法和事件,使您可以自定义或禁用这些用户导向的所有行为。此外,您可以通过编程方式调整行,列和标题,以适合他们的内容,也可以将其配置为自动调整自己只要其内容的变化。
常见问题:
1)如何调整最后一列的宽度使其占据网格的剩余客户区?
5.3.1在Windows窗体DataGridView控件调整大小选项
DataGridView行,列和标题可以改变许多不同的事件结果的大小。下表显示了这些事件。
发生说明
用户调整大小用户可以通过拖动或双击行,列或标题分隔大小的调整。
控制调整在列填充模式,列宽度变化时,控制宽度的改变,例如,当控件停靠到其父形式和用户调整的表格。
细胞在基于内容的自动调整大小模式值的变化,大小变化,以适应新的显示值。
方法调用的方案内容为基础的大小可以让用户调整大小的基础上伺机在方法调用时单元格值。
属性设置也可以设置特定的高度和宽度值。
默认情况下,启用用户调整大小,自动调整大小被禁用,是更广泛的单元格值比列剪裁。
下表显示的情况,你可以用它来调整预设的行为,或使用特定的调整大小选项来达到特定的效果。
方案实施
使用列填充显示同样,在一列,占据了整个宽度的控制数量相对较少,而不显示水平滚动条大小的数据模式。 AutoSizeColumnsMode属性设置为Fill。
使用列填充不同大小显示值模式。 AutoSizeColumnsMode属性设置为Fill。初始化设置列的FillWeight属性或调用控件AutoResizeColumns灌装后用数据控制方法相对列宽度。
使用列填充不同的重要性与价值模式。 AutoSizeColumnsMode属性设置为Fill。设置大量列的MinimumWidth值,必须始终显示的数据部分或使用一个尺寸的选择以外填补特定列模式。
使用列填充模式,以避免显示控件的背景。设置最后一列AutoSizeMode属性为Fill和使用其他尺寸的其他列选项。
显示一个固定宽度的列,如图标或ID列。 AutoSizeMode设置为None,可调整大小为False的列。初始化设置width属性,或者调用控件AutoResizeColumn后用数据填充它的宽度控制方法。
大小时自动调整单元格内容的变化,以避免裁减和优化使用空间。设置一个自动调整大小属性的值,表示一个基于内容的大小调整模式。为了避免性能下降时,大量的数据工作,使用一个尺寸模式,只计算显示的行。
调整大小以适应显示的行值,以避免性能下降时,许多行工作。使用自动或编程调整大小适当的调整大小模式枚举值。要调整大小,以适应在新显示的行滚动时,请在一个滚动的事件处理程序大小的方法价值。定制用户双击调整大小,以便显示的行的值只有在确定新的尺寸,要求在一个RowDividerDoubleClick或ColumnDividerDoubleClick事件处理程序大小的方法。
只有在特定时间调整大小以适应单元格内容,以避免性能罚款或启用用户调整大小。调用事件处理程序中的基于内容的大小的方法。例如,使用DataBindingComplete事件绑定后初始化大小和处理CellValidated或CellValueChanged事件调整大小,以弥补用户编辑或绑定的数据源的变化。
调整多行单元格内容的行高。确保该列的宽度是用于显示相应的文本段落并使用自动或编程的基于内容的行大小来调整高度。另外,还要确保与细胞显示多内容使用的WrapMode细胞式的真实价值。
通常,你会使用自动调整大小模式,以维持列列宽或将其设置为特定宽度前行高进行调整。
5.3.2用鼠标调整大小
默认情况下,用户可以调整行,列和标题不使用自动大小调整模式对细胞价值观为基础。为了防止其他模式,例如列填充模式,缩放用户设置一个或以下的DataGridView属性:
•AllowUserToResizeColumns
•AllowUserToResizeRows
•ColumnHeadersHeightSizeMode
•RowHeadersWidthSizeMode
您还可以防止大小设置其Resizable属性由单个行或列的用户。默认情况下,Resizable属性值是基于对列AllowUserToResizeColumns属性值和属性值的行AllowUserToResizeRows。如果你明确地设置大小可调整为True或False,但是,指定的值控制值覆盖该行或列中。设置调整大小to NotSet恢复继承。
由于NotSet还原值继承,Resizable属性永远不会返回NotSet值,除非该行或列并没有被添加到一个DataGridView控制。如果您需要确定是否行或列Resizable属性值继承,审查其国家的财产。如果该国值包括ResizableSet标志,Resizable属性值不继承。
5.3.3自动调整大小
有两种自动调整大小在DataGridView控制类型:列填充模式和基于内容的自动调整大小。
列填充模式导致在控件中可见列,以填补该控件的显示区域的宽度。如需这个模式的详细信息,请参阅列填充模式一节。
您还可以配置行,列和标题的大小自动调整以适应其单元格内容。在这种情况下,大小调整单元格内容时发生变化。
注意:如果你保持在自定义数据缓存单元格的值使用虚拟模式,自动调整大小时发生用户编辑单元格值,但不会发生改变时,外面的一CellValuePushed事件处理缓存值。在这种情况下,调用UpdateCellValue方法强制控制更新单元格的显示和应用当前的自动调整大小模式。
如果基于内容的自动调整大小仅用于也就是说,对于行,但不列,或列,但不是行和的WrapMode还启用一维启用,大小调整时,也会发生在其他方面的变化。例如,如果行,但不列自动调整大小和配置的WrapMode已启用,用户可以拖动列分隔来改变一个列和行高将自动调整使细胞内容仍然充分显示宽度。
如果配置基于内容的自动调整大小行和列和的WrapMode启用,DataGridView控件将调整单元格内容改变大小时,将使用一个理想的细胞高度对宽度的比例,当计算新的大小。
要配置标题和行和列不会覆盖控制值,浆纱模式设置一个或多个以下的DataGridView属性:
•ColumnHeadersHeightSizeMode
•RowHeadersWidthSizeMode
•AutoSizeColumnsMode
•AutoSizeRowsMode
若要重写控件的列大小的单个列模式,将其AutoSizeMode属性的值比NotSet等。一列大小调整模式实际上是取决于它的InheritedAutoSizeMode财产。这个属性的值是基于列的AutoSizeMode属性值,除非该值是NotSet,在这种情况下控制的AutoSizeColumnsMode值继承。
请谨慎使用基于内容的自动调整大小时,大量数据的工作。为了避免性能下降,使用自动调整大小模式,而不是分析计算中的每一行控制的基础上所显示的行唯一的大小。为获得最佳性能,使用编程调整大小,而不是让你在特定的时间可以调整,如新的数据后立即加载。
基于内容的自动调整大小模式不会影响行,列或标题,你已经通过设置行或列的Visible属性或控制RowHeadersVisible或ColumnHeadersVisible属性为false隐藏。例如,如果列是隐藏后,它会自动调整以适应一个大单元格的值,隐藏的列将不会改变它的大小,如果大所在的行单元格的值将被删除。自动调整大小时,不会出现能见度的变化,因此更改列的Visible属性返回true,将不会强迫它重新计算其大小的当前内容为基础。
方案内容为基础的大小影响的行,列和标题不论其知名度。
5.3.4编程调整大小
禁用自动调整大小时,您可以通过编程设置精确的宽度通过下列属性或行,列或标题的高度:
•RowHeadersWidth
•ColumnHeadersHeight
•DataGridViewRow.Height
•DataGridViewColumn.Width
您还可以通过编程调整行,列和标题,以适合他们的内容使用下列方法:
•AutoResizeColumn
•AutoResizeColumns
•AutoResizeColumnHeadersHeight
•AutoResizeRow
•AutoResizeRows
•AutoResizeRowHeadersWidth
这些方法将调整行,列或标题一次,而不是连续的大小配置它们。新的大小自动计算显示没有剪辑的所有单元格内容。当您以编程方式调整列有填充InheritedAutoSizeMode属性值,但是,计算出的基于内容的宽度按比例用于调整列FillWeight属性值,实际列宽,然后根据这些新的计算比例,让所有列填充该控件的可用显示区域。
编程调整大小可以有效避免连续调整大小的性能损失。它也为用户提供有用的调整大小的行,列和标题的初始大小,列填充模式。
你通常会在特定时间调用的方案调整方法。例如,您可能编程加载数据后,立即调整所有列,或者你可能一个特定的编程方式调整后的行某单元格值已被修改。
5.3.5自定义基于内容的调整大小行为
您可以自定义大小的行为时,派生的DataGridView单元格,行和列类型的工作通过覆盖DataGridViewCell.GetPreferredSize(),DataGridViewRow.GetPreferredHeight(),或DataGridViewColumn.GetPreferredWidth()方法或通过调用DataGridView的保护,在派生大小的方法重载控制。受保护的大小的方法重载的目的是在对工作,以实现理想的单元格高度与宽度的比例,避免过于宽或高的细胞。例如,如果调用AutoResizeRows(DataGridViewAutoSizeRowsMode,布尔)的AutoResizeRows方法重载并传入一个虚假的布尔参数的值,过载将计算在该行细胞的理想的高度和宽度,但它会调整行高而已。然后,您必须调用AutoResizeColumns方法来调整列宽度以计算的理想选择。
5.3.6基于内容的调整大小选项
由大小属性和方法使用的枚举有基于内容的大小相似的价值观。有了这些值,你可以限制哪些细胞是用来计算首选大小。对于所有大小枚举,其名称是指显示的单元格的值限制在他们的计算显示的行的单元格。不包括行是有用的,以避免性能损失,当您使用的是大量的行工作。您还可以限制的计算,以在页眉或nonheader细胞的细胞值。
5.4选择模式
DataGridView控件提供了一系列用于配置用户如何选择单元格,行和列的多种选择你。例如,您可以启用单一或多重选择,全行或列的选择,当用户单击单元格,行或整列选择或仅当用户点击他们的标题,也使小区选择。如果您要提供您的选择自己的用户界面,您可以禁用普通的选择和处理所有的编程选择。此外,还可以让用户选定的值复制到剪贴板。
有时候你希望你的应用程序来执行的DataGridView控制范围内用户的选择为基础的行动。根据不同的操作,您可能希望限制的种类的选择都是可能的。例如,假设你的应用程序可以打印出当前选中的记录报告。在这种情况下,您可能需要配置的DataGridView控件,以便在连续点击任何地方总是选择整行,所以这只能有一个时间行可以被选中。
您可以通过设置SelectionMode属性为下列DataGridViewSelectionMode枚举值之一允许的选择。
DataGridViewSelectionMode值描述
CellSelect单击单元格以选中它,行列标题不能用于选择。
ColumnHeaderSelect单击单元格以选中它,单击列标题选中整列。此时列标题不能用于排序。
FullColumnSelect单击单元格或列标题会选中它们所在的列,此时列标题不能用于排序。
FullRowSelect单击单元格或行标题会选中它们所在的行。
RowHeaderSelect DGV的默认选择模式,单击单元格选中该单元格,单击行标题则选中整行。
注意:在运行时改变选择模式会自动清除当前选择的内容。
默认情况下,用户可以选择用鼠标拖动,按Ctrl或Shift的同时选择延长或修改的选择,或者点击左上角的标题单元格来选择控件中的所有细胞的多个行,列或单元格。为了防止这种行为,设置为false MultiSelect属性。
该FullRowSelect和RowHeaderSelect模式允许用户通过选择删除,再按DELETE键的行。用户可以删除行,只有在当前单元格不处于编辑模式,AllowUserToDeleteRows属性设置为true,并且基础数据源支持用户驱动的行删除。请注意,这些设置不会防止纲领性行删除。
5.4.1编程选择
目前的选择模式限制了方案选择,以及用户的选择行为。你可以改变当前选择编程方式设置的任何单元格,行或列在DataGridView控制选录的财产。您还可以选择通过SelectAll方法控制所有单元格,选择模式而定。要清除的选择,使用ClearSelection方法。
如果MultiSelect属性设置为true,则可以添加或删除DataGridView元素从选择通过改变这些元素的Selected属性。否则,设置一个元素的Selected属性为true自动删除从选择的其他因素。
注意:改变CurrentCell属性的值不会改变当前选择的内容。
通过SelectedCells,SelectedRows和的SelectedColumns属性你可以访问当前选中的单元格,行和列。不过当所有单元格都被选中的时候,使用这些属性效率会比较低,为此可首先使用AreAllCellsSelected方法查看是否已选中全部单元格。此外,访问这些属性来查看选中单元格,行和列的数目效率也比较低,此时应该使用GetCellCount,GetRowCount和GetColumnCount方法,传给它们的参数为DataGridViewElementStates.Selected。
5.5滚动(滚动)
DataGridView中毫无疑问地会提供对水平和垂直滚动条的支持,它同时也支持使用鼠标滚轮进行垂直滚动。水平方向的滚动基于像素值,而垂直方向的滚动则基于行的索引,不支持垂直的DataGridView方向的基于像素值的滚动。
5.5.1 Scroll事件
当你滚动DataGridView的引发Scroll事件,让您被通知滚动发生。对滚动事件参数定位属性可以让你知道滚动的方向。
5.5.2滚动条
DataGridView的滚动条可以访问,它通过保护HorizontalScrollBar和VerticalScrollBar属性显示。 ScrollBar控件直接访问这些让你拥有滚动更好的控制。
5.5.3滚动属性
有许多的属性,提供更大的详细程度如何设置DataGridView的滚动。该图突出这些属性和在这种状态下它们的值。这些属性的读/写除了FirstDisplayedScrollingColumnHiddenWidth和VerticalScrollingOffset属性。
5.6排序
默认情况下,用户可以按一下文字方块的栏标题在DataGridView控件中的数据。您可以修改特定列SortMode属性,允许用户通过其他列类型进行排序时,这样做是有道理的。您还可以通过编程对数据进行排序任何列或多个列。
DataGridView列有三种排序模式。每个列的排序模式是通过指定的列,它可以设置为以下DataGridViewColumnSortMode枚举值之一SortMode属性。
DataGridViewColumnSortMode值描述
自动默认为文本框列。除非列标头用于选择,单击列标题此列自动排序,并显示一个指示排序顺序字形的DataGridView。
NotSortable默认非文本框列。您可以按该列编程,但是,它不适合排序,所以没有空间为排序标志符号保留。
编程您可以按该列编程和空间是为排序标志符号保留。
您可能要更改的列,默认为NotSortable如果它包含可以有意义有序值的排序方式。例如,如果你有一个数据库列包含表示项状态的数字,你可以显示一个图像列绑定到数据库列的这些数字对应的图标。然后,您可以改变一个CellFormatting事件处理程序将图像显示值的数值单元格值。在这种情况下,设置SortMode属性,使您的用户自动排序列。自动分拣将使您的用户组项目,具有相同的状态,即使各国所对应的数字没有一个自然顺序。复选框列是另一个例子,自动排序分组,在同一国家的项目有用。
你可以在任何编程方式进行排序列中的值或多个列的DataGridView,无论SortMode设置。编程排序是有用的当您想为排序或当你想实现自己的自定义排序用户界面(UI)。提供自己的排序用户界面是有用的,例如,当您设置了DataGridView选择模式,使列标题选择。在这种情况下,虽然列标头不能用于排序,你仍然想的标题来显示相应的排序标志符号,所以你会设置SortMode属性编程。
列设置为编程排序模式不会自动显示排序标志符号。对于这些列,你必须显示的字形通过设置DataGridViewColumnHeaderCell.SortGlyphDirection自己的财产。这是必要的,如果你想在自定义排序的灵活性。例如,如果按多列DataGridView的,你可能要显示多个排序标志符号或无排序标志符号。
虽然您可以通过编程任意列进行排序的DataGridView,一些栏目,如按钮列,可能不包含可以有意义的有序值。对于这些列,一个NotSortable SortMode属性设置表示,它将永远不会被用于排序的,所以没有必要储备为排序标志符号头空间。
当DataGridView的排序,你可以同时确定排序列和通过检查SortedColumn和SortOrder的属性的值进行排序。这些值不是一个自定义排序操作后,有意义的。有关自定义排序信息,请参见本主题中的自定义排序节后面。
当DataGridView控件同时包含绑定和未绑定列进行排序,在未绑定列的值不能自动维护。为了保持这些值,你必须执行VirtualMode属性设置为true,并处理CellValueNeeded和CellValuePushed事件虚拟模式。
5.6.1编程排序
您可以排序的DataGridView编程方式调用它的排序方法。
本的Sort(DataGridViewColumn,ListSortDirection)Sort方法重载采用DataGridViewColumn和一个枚举值作为参数ListSortDirection。此重载时非常有用,可以通过与有意义的命令,但你不想配置值的列自动分拣排序。当调用此重载并同一个DataGridViewColumnSortMode.Automatic的SortedColumn和SortOrder的性能SortMode属性值列通过自动设置和相应的排序标志符号出现在列标题。
注意:当DataGridView控件绑定通过设置DataSource属性到外部数据源,的Sort(DataGridViewColumn,ListSortDirection)方法重载不能用于未绑定列。此外,当VirtualMode属性为true,则可以只绑定列调用此重载。要确定是否列是数据绑定,检查IsDataBound属性值。在绑定模式下未绑定列排序不受支持。
5.6.2自定义排序
您可以通过使用自定义的Sort(IComparer)Sort方法重载或通过处理DataGridView的SortCompare事件。
的Sort(IComparer)方法重载采用一个实现类作为参数的IComparer接口的实例。此重载很有用,当您要提供自定义排序,例如,当在一列中的值没有自然排序顺序或者当自然排序顺序是不适当的。在这种情况下,您不能使用自动排序,但您可能仍然希望用户通过点击排序列标题。你还可以打电话为ColumnHeaderMouseClick此重载事件处理程序,如果你不使用选择栏标题。
注意:的Sort(IComparer)方法重载仅当DataGridView控件未绑定到外部数据源和VirtualMode属性值为false。要自定义绑定到外部数据源的列排序,你必须使用排序的数据源提供的操作。在虚拟模式下,你必须为自己的未绑定列排序操作。
要使用的Sort(IComparer)方法重载,您必须创建自己的类实现IComparer接口。此接口要求您的类来实现IComparer.Compare(Object)方法,对此,作为输入传递时的DataGridView的Sort(IComparer)方法重载被称为DataGridViewRow对象。有了这个,你可以计算出正确的行排序的基础上在任一列的值。
的Sort(IComparer)方法重载不设置SortedColumn和SortOrder的属性,所以你必须总是设置DataGridViewColumnHeaderCell.SortGlyphDirection属性以显示排序标志符号。
作为对的Sort(IComparer)方法重载替代方法,可以通过实施提供了SortCompare事件处理程序自定义排序。此事件发生在用户单击列或配置自动分拣头当调用Sort方法的Sort(DataGridViewColumn,ListSortDirection)重载。事件发生时,每行一对在控制,使您能够计算它们的正确顺序。
注:SortCompare事件不会发生当DataSource属性设置或当VirtualMode属性值为true。
5.6.3常见问题及案例
1)如何避免用户对列排序?
2)如何针对多个列排序?
5.7边框样式
使用DataGridView控件,您可以自定义该控件的边框和网格线,以改善用户体验的外观。您可以修改除了为细胞内控制边境网格线的颜色和样式的控件的边框样式。网格线颜色控制,通过GridColor财产。您还可以申请普通细胞,行标题单元格和列标题单元格不同的单元格边框样式。对于先进的边框样式的DataGridView提供先进的边框样式的属性。
注:网格线颜色仅用于与DataGridViewCellBorderStyle枚举和枚举的DataGridViewHeaderBorderStyle单值单,SingleHorizontal和SingleVertical值。这些枚举的其他值使用由操作系统指定的颜色。此外,当视觉样式的Windows XP及以上的启用,GridColor属性值不被使用。
5.7.1标准边框样式
边框样式控制标准通过CellBorderStyle,RowHeadersBorderStyle和ColumnHeadersBorderStyle属性。
下表列出了标准通过所提供的边框样式:
边框值描述
Fixed3D一个三维边框。
FixedSingle单行边框。
无无边框。
5.7.2高级边境风格
DataGridView控件允许你完全自定义其外观,包括细胞和头的边界。 DataGridView的有CellBorderStyle,ColumnHeadersBorderStyle和RowHeadersBorderStyle属性,让您设置单元格边框的外观。但是,如果您需要进一步定制边界,DataGridViewAdvancedBorderStyle类允许您设置单元格的个人双方的边框样式。对DataGridViewAdvancedBorderStyle左,右,顶部和底部属性代表左,右,上,一个细胞和底部边框,分别为。您可以设置在AdvancedCellBorderStyle,AdvancedColumnHeadersBorderStyle,AdvancedRowHeadersBorderStyle DataGridView的属性这些属性产生的细胞之间的边界,展现多种风采。
下表列出了可用的先进的边框样式,可以设置为左,右,顶部和底部部分。请注意,某些组合是无效的。
边框值描述
嵌入一个三维边框。
InsetDouble单行边框。
无无边框。
NotSet边界是没有设置
一开始就是单行凸起边框
OutsetDouble一个双线凸起边框
OutsetPartial单行边界包含凸起部分
单单行边界
5.8输入,编辑模式
默认情况下,用户可以通过在编辑,或按F2键当前DataGridView的文本框格的内容。这使得在编辑模式下,如果下列条件全部得到满足手机:
•基础数据源支持编辑。
•DataGridView控件已启用。
•将EditMode属性值不是EditProgrammatically。
•单元格,行,列的ReadOnly属性和控制,都设置为false。
在编辑模式下,用户可以更改单元格的值,然后按Enter键提交更改或ESC细胞恢复到其原始值。
您可以配置一个DataGridView控件,以使单元格进入编辑模式,一旦它成为当前单元格。该ENTER键和ESC键的行为在这种情况下保持不变,但细胞仍然处于编辑模式后,该值被提交或还原。您还可以配置控制,使细胞进入编辑模式仅当用户键入单元格或只有当用户按下F2键。最后,您可以阻止其进入编辑,除非你调用BeginEdit方法模式细胞。
下表描述了不同的编辑模式可供选择:
编辑模式值描述
EditOnEnter编辑开始时,细胞接收焦点。这种模式是有用的当按下TAB键,进入跨越行值,或当按下回车键,进入下一个列值。
EditOnF2编辑开始时按下F2键时,单元格具有焦点。此模式放置在单元格内容的末尾的选择点。
开始编辑EditOnKeystroke当任何字母数字键被按下,而细胞具有焦点。
EditOnKeystrokeOrF2编辑开始时,任何字母数字键或F2键被按下,而细胞具有焦点。
EditProgrammatically编辑时,才开始BeginEdit方法被调用。
5.9剪贴板拷贝模式
当你使细胞复制,你才能在DataGridView控件的数据很容易接触到其他应用程序通过剪贴板。 DataGridView控件复制到选定的单元格的每个剪贴板的文本表示。此值是单元格的值转换为图像细胞,Description属性的值的字符串或。其内容后加入为制表符分隔的文本值的剪贴簿在诸如记事本和Excel应用程序粘贴,并作为应用程序,如Word粘贴到HTML格式的表格。
您可以配置单元格值复制到复制只,包括在剪贴板上的数据行和列标题文本,或包含标题文本仅当用户选择整个行或列。
下表列出了不同的剪贴板复制模式:
剪贴板拷贝模式说明
禁用复制到剪贴板被禁用。
EnableAlwaysIncludeHeaderText所选单元格的文本值可以被复制到剪贴板。标题文字是否列入行和包含选定单元格的列。
EnableWithAutoHeaderText所选单元格的文本值可以被复制到剪贴板。行或列标题的文本包含或包含的行只选择当SelectionMode属性设置为RowHeaderSelect或ColumnHeaderSelect和至少一个头被选中单元格的列。
EnableWithoutHeaderText所选单元格的文本值可以被复制到剪贴板。标题文字是否不包括在内。
在选择模式的不同,用户可以选择多个不连续的细胞群。当用户复制到剪贴板细胞,行和列,没有选定的单元格不会被复制。所有其他行或列成为复制到剪贴板上的数据表的行和列。在这些行或列未选定的单元格被复制到剪贴板作为空白占位符。
当用户复制内容时,DataGridView控件添加到剪贴板DataObject中。此数据对象是取自GetClipboardContent()方法。你可以调用这个方法时,您希望以编程方式将数据添加对象到剪贴板。该GetClipboardContent()方法通过调用DataGridViewCell.GetClipboardContent检索()方法为个别单元格的值。你可以重写派生类中任一这些方法或两个自定义复制的单元格的布局,或支持格式的其他数据。
5.10冻结的列/行
当用户查看数据有时他们需要参考一列或列集频繁。例如,当显示的客户信息表,其中包含许多列,显示是非常有用的在任何时候,客户名称,同时使其他列可见区域之外的滚动。
为了实现这一行为,您可以冻结在控制列。这是通过设置在列或行冻结的财产。当你冻结一列,所有列在它的左边(或在从右到左的语言脚本右),冻结。冻结列留在原地,而所有其他列可以滚动。行以类似的方式行事:前行中的所有行被冻结的冻结,以及维持不变,而在非冰冻行可以滚动。
5.11实现自定义和编辑控制细胞/细胞
您可以实现在你的派生类来创建一个细胞的细胞类型具有编辑功能,但不承载的编辑模式控制IDataGridViewEditingCell接口。要创建一个控件,你可以在一个宿主细胞中的编辑模式,可以实现从Control派生的类IDataGridViewEditingControl接口。
5.11.1 IDataGridViewEditingControl
支持先进的单元格编辑功能通常使用一个托管控件是从Windows窗体控件派生的。此接口由编辑控件,如DataGridViewComboBoxEditingControl和DataGridViewTextBoxEditingControl,这是由相应的DataGridView单元格,如的DataGridViewComboBoxCell和DataGridViewTextBoxCell,当他们处于编辑模式主持。
单元格可以承载编辑控件设置其EditType属性类型,表示一个类型的编辑控件的类型。
5.11.2 IDataGridViewEditingCell
此接口的类没有提供存取指定的编辑控制值的用户界面(UI)。在这种情况下用户界面显示无论是在细胞处于编辑模式。该DataGridViewCheckBoxCell的是一个细胞,它实现了IDataGridViewEditingCell接口的例子。
其他细胞类型,如的DataGridViewButtonCell,提供一个用户界面,但不存储用户指定的值。在这种情况下,细胞类型不落实IDataGridViewEditingCell或主机一个编辑控制。
5.12虚拟模式
使用虚拟模式,您可以管理之间的DataGridView控件和自定义数据缓存交互。为了实现虚拟模式,设置VirtualMode属性为true,并处理一个或本主题描述的事件更多。您通常处理至少CellValueNeeded事件,它使控件的外观在数据缓存值。
5.12.1绑定模式和虚拟模式
虚拟模式只有当你需要补充或替换绑定模式。在绑定模式下,可以设置DataSource属性和控制自动加载从指定的源数据和提交给它的用户更改回来。您可以控制哪些绑定列的显示方式,和一般的数据源本身处理,如排序操作。
5.12.2补充绑定模式
您可以通过显示补充随着绑定列绑定列绑定模式。这有时也被称为“混合模式”,是用来显示像计算值或用户界面(UI)控制的东西有用。
由于未绑定列之外的数据源,他们是忽视了数据源的排序操作。因此,当您在混合模式下启用排序,你必须管理一个本地缓存中绑定数据,并实现虚拟模式,让DataGridView控件交互。
5.12.3常见问题及案例
1)如何显示绑定的数据绑定以及数据?
2)我怎样的数据显示,从两个表来?
5.12.4更换绑定模式
如果绑定模式无法满足您的性能需求,您可以通过虚拟管理模式的自定义事件处理程序缓存中的所有数据。例如,你可以使用虚拟模式来实现一个公正的实时数据加载的机制,只是从一个网络数据库,获得最佳性能所必需的数据检索。这种情况是非常有用的大量时,通过速度较慢的网络连接或与客户机的数据有一个内存或存储空间有限的工作。
5.12.5虚拟模式事件
如果您的数据是只读的,CellValueNeeded事件可能是唯一的事件,你将需要处理。额外的虚拟模式事件让你启用特定的功能,如用户编辑,添加和删除行和行级的交易。
一些标准的DataGridView事件(如发生的事件当用户添加或删除行,或在编辑单元格值时,解析,验证,或者格式化)在虚拟模式中非常有用,以及。你也可以处理事件,让你保持在一个通常不绑定的数据源中存储的值,如细胞提示文本,单元格和行的错误文本,单元格和行的快捷菜单数据,和行高的数据。
下列事件发生时,才VirtualMode属性设置为true。
事件描述
CellValueNeeded由控制用于检索从显示数据高速缓存单元格的值。此事件只发生在未绑定列细胞。
CellValuePushed由控制用于提交,可以向用户输入的数据高速缓存单元。此事件只发生在未绑定列细胞。
调用方法时UpdateCellValue更改之外的CellValuePushed事件处理缓存值,以确保当前值显示在控件中的作用,并适用于目前所有自动调整大小模式。
NewRowNeeded由控件用来指示一个数据高速缓存中的新行的需要。
RowDirtyStateNeeded的控制,用来确定行是否有任何未提交的更改。
CancelRowEdit使用的控制,表明该行应恢复其缓存的值。
以下事件在虚拟模式中非常有用,但也可以使用了VirtualMode属性设置无关。
事件的说明
UserDeletingRow
UserDeletedRow
RowsRemoved
RowsAdded由控件用来指示行被删除或添加,让您更新相应的数据高速缓存。
CellFormatting
CellParsing
CellValidating
CellValidated
RowValidating
RowValidated使用的显示格式为单元格值和解析和验证用户输入控制。
CellToolTipTextNeeded由控制单元用于检索工具提示文本当DataSource属性设置或VirtualMode属性为true。
工具提示显示细胞只有在ShowCellToolTips属性值为true。
CellErrorTextNeeded
RowErrorTextNeeded的控制,用来检索单元格或行的错误文本当DataSource属性设置或VirtualMode属性为true。
调用方法或UpdateRowErrorText UpdateCellErrorText方法,当你更改单元格或行的错误文本,以确保当前值在控件中显示。
细胞与行的错误标志符号时显示ShowCellErrors和ShowRowErrors属性值是正确的。
CellContextMenuStripNeeded
RowContextMenuStripNeeded由控制用于检索单元格或行的ContextMenuStrip当控件的DataSource属性设置或VirtualMode属性为true。
RowHeightInfoNeeded
RowHeightInfoPushed由控制用于检索或存储数据的高速缓存行中高度信息。调用方法时改变UpdateRowHeightInfo缓存行之外的RowHeightInfoPushed事件处理的高度信息,以确保当前值在控制显示器使用。
5.12.6在虚拟模式下的最佳实践
如果要实现虚拟模式,以工作效率的大量数据,你也想确保您正在使用DataGridView控件本身的效率。请参阅下面的最佳做法的信息
5.13容量(容量)
一般来说,在DataGridView没有硬编码容量限制。网格的设计,使越来越多的内容可以添加的机器变得更快,并有更多的内存。尽管如此,格并不是用来处理大量列。如果您添加超过300行,您会开始注意到在随着我们对电网的表现却不是这样的优化性能的退化。如果你需要一个大量的列格,然后在DataGridView可能不符合您的需求。关于支持的行数时,DataGridView是受内存限制。当使用虚拟模式,您可以轻松支持超过200万行。看看你可以做的事情(不要做),以提高内存的使用情况和性能的最佳做法的信息,下面一节。
6个最佳实践(最佳做法)
DataGridView控件的设计提供最大的可扩展性。如果你需要显示大量数据,你应该按照本主题中所述,以避免内存或有辱人格的用户界面(UI)的响应消耗大量的指导方针。
6.1使用高效单元格样式
每个单元格,行和列可以有自己的样式信息。样式信息存储在DataGridViewCellStyle对象。创造许多个人DataGridView元素单元格样式的对象可以是低效的,特别是当大量数据的工作。为了避免性能的影响,请遵循下列准则:
•避免为单个DataGridViewCell或DataGridViewRow对象的单元格样式属性。这包括由RowTemplate行对象属性中指定。每个新行是从行模板克隆将接收其模板的单元格样式对象的副本。为了获得最大的可扩展性,设置在DataGridView的单元格样式属性的水平。例如,设置DefaultCellStyle属性,而不是DataGridViewCell.Style财产。
•如果某些细胞需要的格式以外的默认格式,在使用相同的单元格,行或列组的DataGridViewCellStyle实例。避免直接设置个别类型的单元格,行和列DataGridViewCellStyle属性。对于一个单元格样式共享的例子,请参见如何:设置单元格样式的默认为Windows窗体DataGridView控件。您也可避免性能下降时,通过处理CellFormatting设置事件处理个别单元格样式。有关示例,请参见如何:自定义的数据格式在Windows窗体DataGridView控件。
•当确定一个单元格样式,使用DataGridViewCell.InheritedStyle财产,而不是DataGridViewCell.Style财产。访问Style属性创建一个DataGridViewCellStyle类的新实例如果该属性还没有被使用。此外,这个对象可能不包含完整的样式为单元格的信息,如果有些样式从行,列或控件继承。欲了解更多有关单元格样式继承的详细信息,请参阅细胞在Windows窗体DataGridView控件样式。
6.2使用高效快捷菜单
每个单元格,行和列可以有它自己的快捷菜单。在DataGridView控制快捷菜单ContextMenuStrip控件代表。这正好与单元格样式对象作为,创造许多个人DataGridView元素的快捷菜单将产生负面影响性能。为了避免这种损失,请使用下列准则:
•避免为单个单元格和行的快捷菜单。这包括行模板,这是克隆了它的快捷方式菜单时,新行被添加到控件一起。为了获得最大的可扩展性,仅使用控件的ContextMenuStrip属性来指定整个控制单一的快捷菜单。
•如果您需要多个行或多种细胞的快捷菜单,处理CellContextMenuStripNeeded或RowContextMenuStripNeeded事件。这些事件让您管理自己的快捷菜单对象,让您调整性能。
6.3使用自动调整大小高效
行,列和标题可以自动调整大小的单元格内容的变化,使细胞中的全部内容都没有剪辑显示。更改调整大小模式也可以调整行,列和标题。要确定正确的大小,DataGridView控件必须检查每一个细胞,它必须适应值。当处理大量数据时,这种分析可以产生负面影响控制性能的自动调整大小时发生。为了避免性能下降,请遵循下列准则:
•避免使用带有大量行集的DataGridView控制自动调整大小。如果你使用自动大小调整,只调整的基础上所显示的行。在虚拟模式下只使用所显示的行以及。
对行和列•,使用DataGridViewAutoSizeRowsMode,DataGridViewAutoSizeColumnsMode和DataGridViewAutoSizeColumnMode枚举的DisplayedCells或DisplayedCellsExceptHeaders领域。
•对于行头,使用该DataGridViewRowHeadersWidthSizeMode枚举AutoSizeToDisplayedHeaders或AutoSizeToFirstHeader领域。
为了获得最大的可扩展性•,关闭自动调整大小尺寸和使用方案。
6.4使用选定的单元格,行和列的集合高效
SelectedCells集合不执行效率大选择。收藏的SelectedRows和SelectedColumns也可以是低效的,但在较小的程度,因为有许多比细胞中的行数少一个典型的DataGridView控件,比列行少得多。为了避免性能下降与这些藏品时,请遵循下列准则:
•要确定是否所有在DataGridView单元格已被选中,然后再访问该SelectedCells集合的内容,检查AreAllCellsSelected方法的返回值。请注意,但是,这种方法可能会导致行成为非共享。有关详细信息,请参阅下一节。
•避免使用的DataGridViewSelectedCellCollection Count属性来确定所选细胞的数量。相反,使用GetCellCount()方法并传入DataGridViewElementStates.Selected价值。同样,使用DataGridViewRowCollection.GetRowCount()和DataGridViewColumnCollection.GetColumnCount()方法来确定所选元素,而不是访问选定的行和列集合,数量。
•避免细胞为基础的选择模式。相反,SelectionMode属性设置为FullRowSelect或FullColumnSelect。
6.5使用共享行
实现有效的内存使用在通过共享行的DataGridView控制。作为行会分享他们的外观和行为,尽可能通过DataGridViewRow类的共享实例的信息。
虽然共享行实例节省内存,很容易成为非共享行。例如,每当一个直接与用户交互的一个单元,它的行成为非共享。因为这是无法避免,在这个主题中的准则是有用的,只有当工作与数据量非常大,只有当用户将与每一个数据你的程序运行时间的一小部分。
阿行不能共享在未绑定的DataGridView控制,如果它的任何单元格包含值。当DataGridView控件绑定到外部数据源,或当您实现虚拟模式,并提供您自己的数据源,该单元格值存储以外的控制,而不是在单元格对象,允许行被共享。
行对象只能共享,如果它的所有细胞的状态可以从该行的状态和细胞列载的状态决定。如果您更改单元格的状态,这样它可以不再从它的行和列的状态推断,该行不能被共享。
例如,行不能共享在下列情形之一:
•该行包含一个选定的单元格是不是在选定的列。
•该行包含一个与它的ToolTipText或ContextMenuStrip属性设置单元。
•该行包含其项目属性的DataGridViewComboBoxCell集。
在绑定模式或虚拟模式,您可以通过处理CellToolTipTextNeeded提供CellContextMenuStripNeeded事件和个别细胞工具提示和快捷菜单。
DataGridView控件将自动尝试使用共享每当行添加到DataGridViewRowCollection行。使用下面的指引,以确保行共享:
•避免调用Add(Object []的)的添加方法和插入(对象[])的插入的行的集合方法重载超载。这些重载自动创建非共享行。
•确保在RowTemplate属性指定的行可以在下列情况下,共享:
当调用add()或Add方法添加或插入(智力,智力)的行的集合插入方法重载(智力)重载。
当增加RowCount属性的值。
当设置DataSource属性。
•确保该行的indexSource参数指定当呼叫可以共享的行集合AddCopy,AddCopies,InsertCopy和InsertCopies方法。
•请确定指定的行或列时,可以共享调用Add(的DataGridViewRow)Add方法的重载,AddRange方法,插入(Int32的,的DataGridViewRow)方法重载的插入,和Rows集合InsertRange方法。
要确定行是否是共享的,使用DataGridViewRowCollection.SharedRow(int)方法来检索行对象,然后检查对象的Index属性。共享行总是为-1 Index属性值。
6.6防止行成为非共享
共享成为非共享行可以作为一个代码或用户操作的结果。为了避免影响性能,你应该避免造成行成为非共享。在应用开发,你可以处理RowUnshared事件来确定行成为非共享。这是非常有用的调试行共享问题。
为了防止行成为非共享,请使用下列准则:
•避免索引中的行集或通过它迭代与foreach循环。你不会通常需要直接访问行。 DataGridView的操作方法,对行,而不是采取行实例行索引参数。此外,对于行相关的事件处理程序接收行属性,您可以用它来操作,而不会造成他们成为非共享行的事件参数对象。
•如果您需要访问的行对象,请使用DataGridViewRowCollection.SharedRow(int)方法并传入行的实际索引。请注意,但是,修改一个共享行对象通过此方法检索将修改所有行共享此对象。在新记录行不共享,所以这是不会受到影响,当您修改任何其他行中的其他行。还要注意的是一个共享行代表不同的行可能有不同的快捷菜单。以检索共享行实例的正确快捷菜单中,使用GetContextMenuStrip方法并传入行的实际索引。如果您访问共享行的ContextMenuStrip属性,而是将使用-1共享行的索引,将不检索正确的快捷菜单。
•避免索引DataGridViewRow.Cells集合。访问一个细胞将直接导致其父行成为非共享,实例化一个新的DataGridViewRow。为细胞相关的事件处理程序接收单元属性,你可以用它来操作不会导致行成为非共享细胞事件参数对象。您也可以使用CurrentCellAddress属性来检索,而不用访问细胞直接当前单元格的行和列索引。
•避免细胞为基础的选择模式。这些模式导致行成为非共享。相反,将SelectionMode属性设置DataGridViewSelectionMode.FullRowSelect或DataGridViewSelectionMode.FullColumnSelect。
•不处理DataGridViewRowCollection.CollectionChanged或RowStateChanged事件。这些事件会导致行成为非共享。另外,不要叫DataGridViewRowCollection.OnCollectionChanged(CollectionChangeEventArgs)或OnRowStateChanged(智力,DataGridViewRowStateChangedEventArgs)方法,提高了这些事件。
•不访问SelectedCells集合时SelectionMode属性值是FullColumnSelect,ColumnHeaderSelect,FullRowSelect或RowHeaderSelect。这会导致所有行成为非共享选择。
•不要调用AreAllCellsSelected(布尔)方法。这种方法可能会导致行成为非共享。
•不要调用SelectAll方法当SelectionMode属性值是CellSelect。这会导致所有行成为非共享。
•不要设置只读或选定的一对假时,在其列对应的属性设置为true单元属性。这会导致所有行成为非共享。
•不访问DataGridViewRowCollection.List财产。这会导致所有行成为非共享。
•不要调用Sort方法的Sort(IComparer接口)超载。一个自定义比较排序会导致所有行成为非共享。
附录 A – FAQ
该附录包含的代码示例和片段集中解答了前面散落的常见问题:
1. 如何使指定的单元格不可编辑?
ReadOnly属性决定了单元格中的数据是否可以编辑,可以设置单元格的ReadOnly 属性,也可以设置DataGridViewRow.ReadOnly 或DataGridViewColumn.ReadOnly使得一行或一列所包含的单元格都是只读的。 默认情况下,如果一行或一列是只读的,那么其包含的单元格也会使只读的。
不过你仍可以操作一个只读的单元格,比如选中它,将其设置为当前单元格,但用户不能修改单元格的内容。注意,即使单元格通过ReadOnly属性设置为只读,仍然可以通过编程的方式修改它,另外ReadOnly也不会影响用户是否可以删除行。
2. 如何让一个单元格不可用(disable)?
单元格可以设置为只读而不可编辑,但DataGridView却没提供使单元格不可用的支持。一般意义上,不可用意味着用户不能进行操作,通常会带有外观的暗示,如灰色。没有一种简单的方法来创建那种不可操作的单元格,但提供一个暗示性的外观告诉用户某单元格不可用还是可行的。内置的单元格类型没有进行不可用设置的属性,下面的例子扩展了DataGridViewButtonCell ,参照常见控件的Enabled属性,为其添加了Enabled属性,如果该属性设置为false,那么其外观状态将类似于普通按钮的不可用状态。
public class DataGridViewDisableButtonColumn : DataGridViewButtonColumn
{
public DataGridViewDisableButtonColumn()
{
this.CellTemplate = new DataGridViewDisableButtonCell();
}
}
public class DataGridViewDisableButtonCell : DataGridViewButtonCell
{
private bool enabledValue;
public bool Enabled
{
get {
return enabledValue;
}
set {
enabledValue = value;
}
}
// Override the Clone method so that the Enabled property is copied.
public override object Clone()
{
DataGridViewDisableButtonCell cell =
(DataGridViewDisableButtonCell)base.Clone();
cell.Enabled = this.Enabled;
return cell;
}
// By default, enable the button cell.
public DataGridViewDisableButtonCell()
{
this.enabledValue = true;
}
protected override void Paint(Graphics graphics,
Rectangle clipBounds, Rectangle cellBounds, int rowIndex,
DataGridViewElementStates elementState, object value,
object formattedValue, string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
// The button cell is disabled, so paint the border,
// background, and disabled button for the cell.
if (!this.enabledValue)
{
// Draw the cell background, if specified.
if ((paintParts & DataGridViewPaintParts.Background) ==
DataGridViewPaintParts.Background)
{
SolidBrush cellBackground =
new SolidBrush(cellStyle.BackColor);
graphics.FillRectangle(cellBackground, cellBounds);
cellBackground.Dispose();
}
// Draw the cell borders, if specified.
if ((paintParts & DataGridViewPaintParts.Border) ==
DataGridViewPaintParts.Border)
{
PaintBorder(graphics, clipBounds, cellBounds, cellStyle,
advancedBorderStyle);
}
// Calculate the area in which to draw the button.
Rectangle buttonArea = cellBounds;
Rectangle buttonAdjustment =
this.BorderWidths(advancedBorderStyle);
buttonArea.X += buttonAdjustment.X;
buttonArea.Y += buttonAdjustment.Y;
buttonArea.Height -= buttonAdjustment.Height;
buttonArea.Width -= buttonAdjustment.Width;
// Draw the disabled button.
ButtonRenderer.DrawButton(graphics, buttonArea,
PushButtonState.Disabled);
// Draw the disabled button text.
if (this.FormattedValue is String)
{
TextRenderer.DrawText(graphics,
(string)this.FormattedValue,
this.DataGridView.Font,
buttonArea, SystemColors.GrayText);
}
}
else
{
// The button cell is enabled, so let the base class
// handle the painting.
base.Paint(graphics, clipBounds, cellBounds, rowIndex,
elementState, value, formattedValue, errorText,
cellStyle, advancedBorderStyle, paintParts);
}
}
}
3. 如何避免用户将焦点设置到指定的单元格?
默认情况下DataGridView的操作(navigation)模型在限制用户将焦点置于指定的单元格方面没有提供任何支持。你可以实现自己的操作逻辑,这需要重写合适的键盘、导航、鼠标方法,如DataGridView.OnKeyDown, DataGridView.ProcessDataGridViewKey, DataGridView.SetCurrentCellAddressCore, DataGridView.SetSelectedCellCore, DataGridView.OnMouseDown。
4. 如何使所有单元格总是显示控件(不论它是否处于编辑状态)?
DataGridView 控件只支持在单元格处于编辑状态时显示真实的控件(如TextBox)。DataGridView 没有被设计为显示多控件或为每行重复显示控件。DataGridView 在单元格不被编辑时为其绘制对应控件的外观,该外观可能是你想要的。例如,DataGridViewButtonCell 类型的单元格,不管它是否处于编辑状态,总是表现为一个按钮。
5. Why does the cell text show up with “square” characters where they should be new lines(TODO,未能实现该效果)?
By default, text in a DataGridViewTextBoxCell does not wrap. This can be controlled via the WrapMode property on the cell style (e.g. DataGridView.DefaultCellStyle.WrapMode). Because text doesn’t wrap, new line characters in the text do not apply and so they are displayed as a “non-printable” character. This is similar to setting a TextBox’s Text property to the same text when the TextBox’s MultiLine property is false.
6. 如何在单元格内同时显示图标和文本?
DataGridView控件没有对在同一单元格内同时显示图标和文本提供支持。但通过实现自定义的绘制事件,如CellPaint 事件,你可以轻松实现这个效果。
下面这段代码扩展了DataGridViewTextBoxColumn 和DataGridViewTextBoxCell类,将一个图片显示在文本旁边。这个示例使用了DataGridViewCellStyle.Padding 属性来调整文本的位置,重写了Paint 方法来绘制图片。该示例可以得到简化,方法是处理CellPainting 事件,在这里实现类似的功能。
public class TextAndImageColumn:DataGridViewTextBoxColumn
{
private Image imageValue;
private Size imageSize;
public TextAndImageColumn()
{
this.CellTemplate = new TextAndImageCell();
}
public override object Clone()
{
TextAndImageColumn c = base.Clone() as TextAndImageColumn;
c.imageValue = this.imageValue;
c.imageSize = this.imageSize;
return c;
}
public Image Image
{
get { return this.imageValue; }
set
{
if (this.Image != value) {
this.imageValue = value;
this.imageSize = value.Size;
if (this.InheritedStyle != null) {
Padding inheritedPadding = this.InheritedStyle.Padding;
this.DefaultCellStyle.Padding = new Padding(imageSize.Width,
inheritedPadding.Top, inheritedPadding.Right,
inheritedPadding.Bottom);
}
}
}
}
private TextAndImageCell TextAndImageCellTemplate
{
get { return this.CellTemplate as TextAndImageCell; }
}
internal Size ImageSize
{
get { return imageSize; }
}
}
public class TextAndImageCell : DataGridViewTextBoxCell
{
private Image imageValue;
private Size imageSize;
public override object Clone()
{
TextAndImageCell c = base.Clone() as TextAndImageCell;
c.imageValue= this.imageValue;
c.imageSize = this.imageSize;
return c;
}
public Image Image
{
get {
if (this.OwningColumn == null ||
this.OwningTextAndImageColumn == null) {
return imageValue;
}
else if (this.imageValue != null) {
return this.imageValue;
}
else {
return this.OwningTextAndImageColumn.Image;
}
}
set {
if (this.imageValue != value) {
this.imageValue = value;
this.imageSize = value.Size;
Padding inheritedPadding = this.InheritedStyle.Padding;
this.Style.Padding = new Padding(imageSize.Width,
inheritedPadding.Top, inheritedPadding.Right,
inheritedPadding.Bottom);
}
}
}
protected override void Paint(Graphics graphics, Rectangle clipBounds,
Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState,
object value, object formattedValue, string errorText,
DataGridViewCellStyle cellStyle,
DataGridViewAdvancedBorderStyle advancedBorderStyle,
DataGridViewPaintParts paintParts)
{
// Paint the base content
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,
value, formattedValue, errorText, cellStyle,
advancedBorderStyle, paintParts);
if (this.Image != null) {
// Draw the image clipped to the cell.
System.Drawing.Drawing2D.GraphicsContainer container =
graphics.BeginContainer();
graphics.SetClip(cellBounds);
graphics.DrawImageUnscaled(this.Image, cellBounds.Location);
graphics.EndContainer(container);
}
}
private TextAndImageColumn OwningTextAndImageColumn
{
get { return this.OwningColumn as TextAndImageColumn; }
}
}
7. 如何隐藏一列?
有时希望仅显示DataGridView的部分列,将其它列隐藏。比如DataGridView含有一列包含员工薪水信息,你可能希望仅将这些信息显示给具有一定信用级别的人,其他人则隐藏。
通过编程方式隐藏
DataGridViewColumn类的Visible 属性决定了是否显示该列。
通过设计器隐藏
1) 右击DataGridView控件,选择Edit Columns;
2) 在列列表中选择一列;
3) 在列属性网格中,将Visible属性设置为false。
8. 如何避免用户对列排序?
对于DataGridView 控件,默认情况下,TextBox类型的列会自动排序,而其它类型的列则不会自动排序。这种自动排序有时会把数据变得比较乱,这时你会想更改这些默认设置。
DataGridViewColumn的属性SortMode决定了列的排序方式,将其设置为DataGridViewColumnSortMode.NotSortable就可以避免默认的排序行为。
9. 如何针对多个列排序?
默认情况下DataGridView不支持针对多列排序。下面针对是否将数据绑定到DataGridView来分别演示如何为其添加多列排序功能。
9.1 将数据绑定到DataGridView时
DataGridView进行数据绑定的时候,数据源(如DataView)可对多个列排序。DataGridView会保留这种排序,但只有第一个排序列会显示排序符号(向上或向下的箭头),此外SortedColumn属性也只会返回第一个排序列。
一些数据源内置了对多列排序的支持。如果你的数据源实现了IBindingListView接口,提供了对Sort属性的支持,那么该数据源就支持多列排序。为了明确指出DataGridView对多列排序,手动为已排序列设置正确的SortGlyphDirection属性,指示该列已经排序。
下面这个示例使用DataTable作为数据源,使用其DefaultView的 Sort 属性对第二列和第三列排序;该示例同时演示了如何设置列的SortGlyphDirection属性。该示例假定在你的窗体上有一个DataGridView控件和一个BindingSource组件:
DataTable dt = new DataTable();
dt.Columns.Add("C1", typeof(int));
dt.Columns.Add("C2", typeof(string));
dt.Columns.Add("C3", typeof(string));
dt.Rows.Add(1, "1", "Test1");
dt.Rows.Add(2, "2", "Test2");
dt.Rows.Add(2, "2", "Test1");
dt.Rows.Add(3, "3", "Test3");
dt.Rows.Add(4, "4", "Test4");
dt.Rows.Add(4, "4", "Test3");
DataView view = dt.DefaultView;
view.Sort = "C2 ASC, C3 ASC";
bindingSource.DataSource = view;
DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn();
col0.DataPropertyName = "C1";
dataGridView1.Columns.Add(col0);
col0.SortMode = DataGridViewColumnSortMode.Programmatic;
col0.HeaderCell.SortGlyphDirection = SortOrder.None;
DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();
col1.DataPropertyName = "C2";
dataGridView1.Columns.Add(col1);
col1.SortMode = DataGridViewColumnSortMode.Programmatic;
col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending;
DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
col2.DataPropertyName = "C3";
dataGridView1.Columns.Add(col2);
col2.SortMode = DataGridViewColumnSortMode.Programmatic;
col2.HeaderCell.SortGlyphDirection = SortOrder.Ascending;
9.2 Unbound DataGridView
To provide support for sorting on multiple columns you can handle the SortCompare event or call the Sort(IComparer) overload of the Sort method for greater sorting flexibility.
9.2.1 Custom Sorting Using the SortCompare Event
The following code example demonstrates custom sorting using a SortCompare event handler. The selected DataGridViewColumn is sorted and, if there are duplicate values in the column, the ID column is used to determine the final order.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
class Form1 : Form
{
private DataGridView dataGridView1 = new DataGridView();
// Establish the main entry point for the application.
[STAThreadAttribute()]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
public Form1()
{
// Initialize the form.
// This code can be replaced with designer generated code.
dataGridView1.AllowUserToAddRows = false;
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.SortCompare += new DataGridViewSortCompareEventHandler(
this.dataGridView1_SortCompare);
Controls.Add(this.dataGridView1);
this.Text = "DataGridView.SortCompare demo";
PopulateDataGridView();
}
// Replace this with your own population code.
public void PopulateDataGridView()
{
// Add columns to the DataGridView.
dataGridView1.ColumnCount = 3;
// Set the properties of the DataGridView columns.
dataGridView1.Columns[0].Name = "ID";
dataGridView1.Columns[1].Name = "Name";
dataGridView1.Columns[2].Name = "City";
dataGridView1.Columns["ID"].HeaderText = "ID";
dataGridView1.Columns["Name"].HeaderText = "Name";
dataGridView1.Columns["City"].HeaderText = "City";
// Add rows of data to the DataGridView.
dataGridView1.Rows.Add(new string[] { "1", "Parker", "Seattle" });
dataGridView1.Rows.Add(new string[] { "2", "Parker", "New York" });
dataGridView1.Rows.Add(new string[] { "3", "Watson", "Seattle" });
dataGridView1.Rows.Add(new string[] { "4", "Jameson", "New Jersey" });
dataGridView1.Rows.Add(new string[] { "5", "Brock", "New York" });
dataGridView1.Rows.Add(new string[] { "6", "Conner", "Portland" });
// Autosize the columns.
dataGridView1.AutoResizeColumns();
}
private void dataGridView1_SortCompare(object sender,
DataGridViewSortCompareEventArgs e)
{
// Try to sort based on the cells in the current column.
e.SortResult = System.String.Compare(
e.CellValue1.ToString(), e.CellValue2.ToString());
// If the cells are equal, sort based on the ID column.
if (e.SortResult == 0 && e.Column.Name != "ID")
{
e.SortResult = System.String.Compare(
dataGridView1.Rows[e.RowIndex1].Cells["ID"].Value.ToString(),
dataGridView1.Rows[e.RowIndex2].Cells["ID"].Value.ToString());
}
e.Handled = true;
}
}
9.2.2 Custom Sorting Using the IComparer Interface
The following code example demonstrates custom sorting using the Sort(IComparer) overload of the Sort method, which takes an implementation of the IComparer interface to perform a multiple-column sort.
using System;
using System.Drawing;
using System.Windows.Forms;
class Form1 : Form
{
private DataGridView DataGridView1 = new DataGridView();
private FlowLayoutPanel FlowLayoutPanel1 = new FlowLayoutPanel();
private Button Button1 = new Button();
private RadioButton RadioButton1 = new RadioButton();
private RadioButton RadioButton2 = new RadioButton();
// Establish the main entry point for the application.
[STAThreadAttribute()]
public static void Main()
{
Application.Run(new Form1());
}
public Form1()
{
// Initialize the form.
// This code can be replaced with designer generated code.
AutoSize = true;
Text = "DataGridView IComparer sort demo";
FlowLayoutPanel1.FlowDirection = FlowDirection.TopDown;
FlowLayoutPanel1.Location = new System.Drawing.Point(304, 0);
FlowLayoutPanel1.AutoSize = true;
FlowLayoutPanel1.Controls.Add(RadioButton1);
FlowLayoutPanel1.Controls.Add(RadioButton2);
FlowLayoutPanel1.Controls.Add(Button1);
Button1.Text = "Sort";
RadioButton1.Text = "Ascending";
RadioButton2.Text = "Descending";
RadioButton1.Checked = true;
Controls.Add(FlowLayoutPanel1);
Controls.Add(DataGridView1);
}
protected override void OnLoad(EventArgs e)
{
PopulateDataGridView();
Button1.Click += new EventHandler(Button1_Click);
base.OnLoad(e);
}
// Replace this with your own code to populate the DataGridView.
private void PopulateDataGridView()
{
DataGridView1.Size = new Size(300, 300);
// Add columns to the DataGridView.
DataGridView1.ColumnCount = 2;
// Set the properties of the DataGridView columns.
DataGridView1.Columns[0].Name = "First";
DataGridView1.Columns[1].Name = "Last";
DataGridView1.Columns["First"].HeaderText = "First Name";
DataGridView1.Columns["Last"].HeaderText = "Last Name";
DataGridView1.Columns["First"].SortMode =
DataGridViewColumnSortMode.Programmatic;
DataGridView1.Columns["Last"].SortMode =
DataGridViewColumnSortMode.Programmatic;
// Add rows of data to the DataGridView.
DataGridView1.Rows.Add(new string[] { "Peter", "Parker" });
DataGridView1.Rows.Add(new string[] { "James", "Jameson" });
DataGridView1.Rows.Add(new string[] { "May", "Parker" });
DataGridView1.Rows.Add(new string[] { "Mary", "Watson" });
DataGridView1.Rows.Add(new string[] { "Eddie", "Brock" });
}
private void Button1_Click(object sender, EventArgs e)
{
if (RadioButton1.Checked == true)
{
DataGridView1.Sort(new RowComparer(SortOrder.Ascending));
}
else if (RadioButton2.Checked == true)
{
DataGridView1.Sort(new RowComparer(SortOrder.Descending));
}
}
private class RowComparer : System.Collections.IComparer
{
private static int sortOrderModifier = 1;
public RowComparer(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Descending)
{
sortOrderModifier = -1;
}
else if (sortOrder == SortOrder.Ascending)
{
sortOrderModifier = 1;
}
}
public int Compare(object x, object y)
{
DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x;
DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y;
// Try to sort based on the Last Name column.
int CompareResult = System.String.Compare(
DataGridViewRow1.Cells[1].Value.ToString(),
DataGridViewRow2.Cells[1].Value.ToString());
// If the Last Names are equal, sort based on the First Name.
if (CompareResult == 0)
{
CompareResult = System.String.Compare(
DataGridViewRow1.Cells[0].Value.ToString(),
DataGridViewRow2.Cells[0].Value.ToString());
}
return CompareResult * sortOrderModifier;
}
}
}
10. 如何为编辑控件添加事件处理函数?
有时候你需要处理单元格包含的编辑控件的特定事件。你需要处理DataGridView.EditingControlShowing 事件,它的第二个参数的Control属性能让你访问该单元格包含的编辑控件。如果你要处理的事件不属于它的基类Control,还需要将该控件转换为特定的控件(一般为ComboBox控件或TextBox控件)。
注意:如果类型相同,DataGridView会重用该编辑控件,因此,你应该确保不会添加已存在的事件处理函数,否则会调用相同的函数多次(可以在添加前先将其移除,请参考我的示例代码)。
11. 应在何时移除编辑控件的事件处理函数?
如果你只是想临时为编辑控件添加事件处理函数(可能是针对特定列的特定单元格),你可以在CellEndEdit事件中移除该处理函数。你也可以在添加之前移除任何已存在的事件处理函数。
12. 如何处理ComboBox列中控件的SelectIndexChanged事件?
有时知道用户何时选择了ComboBox编辑控件的项(item)会比较有用。对于窗体上的ComboBox 控件,你通常会处理它的SelectedIndexChanged事件,对于DataGridViewComboBox,通过处理DataGridView.EditingControlShowing事件你可以完成相同的事情。下面这段示例代码演示了这一点。注意:它同时也演示了如何避免添加多个相同的事件处理函数(即在添加前先移除已存在的事件处理函数,可以参考问题11)。
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
// first remove event handler to keep from attaching multiple:
cb.SelectedIndexChanged -= new
EventHandler(cb_SelectedIndexChanged);
// now attach the event handler
cb.SelectedIndexChanged += new
EventHandler(cb_SelectedIndexChanged);
}
}
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Selected index changed");
}
13. 如何通过拖放调整行的顺序?
通过拖放调整行的顺序不是DataGridView的内置功能,但使用标准的拖放处理代码,你可以很容易的实现这个功能。下面这个代码片断演示了这个过程,假定你的窗体上有一个name为dataGridView1的DataGridView,它的AllowDrop属性为true,还要为它添加必要的事件处理方法。(我试运行了这段代码,如果通过数据绑定为DataGridView添加数据,那么下面的代码将不会生效,因为它只能为非绑定方式添加的行排序,如果要以绑定方式添加数据,请参看我的示例程序)
private Rectangle dragBoxFromMouseDown;
private int rowIndexFromMouseDown;
private int rowIndexOfItemUnderMouseToDrop;
private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
DragDropEffects dropEffect = dataGridView1.DoDragDrop(
dataGridView1.Rows[rowIndexFromMouseDown],
DragDropEffects.Move);
}
}
}
private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{
// Get the index of the item the mouse is below.
rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;
if (rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
Size dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
e.Y - (dragSize.Height / 2)),
dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
dragBoxFromMouseDown = Rectangle.Empty;
}
private void dataGridView1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
Point clientPoint = dataGridView1.PointToClient(new Point(e.X, e.Y));
// Get the row index of the item the mouse is below.
rowIndexOfItemUnderMouseToDrop =
dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
// If the drag operation was a move then remove and insert the row.
if (e.Effect== DragDropEffects.Move)
{
DataGridViewRow rowToMove = e.Data.GetData(
typeof(DataGridViewRow)) as DataGridViewRow;
dataGridView1.Rows.RemoveAt(rowIndexFromMouseDown);
dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop, rowToMove);
}
}
14. 如何调整最后一列的宽度使其占据网格的剩余客户区?
以默认方式填充DataGridView时,可能会发生因列的宽度不够,而暴露出控件的灰色背景的情况,很不美观。将最后一列的AutoSizeMode属性设置为Fill会使该列调整大小来填充网格的剩余客户区(client area)。作为一个可选的方式,你可以设置最后一列MinimumWidth属性,以保持该列的宽度不至于太小。
15. 如何让TextBox类型的单元格支持换行?
默认情况下,DataGridViewTextBoxCell不支持换行,这个可以由DataGridViewCellStyle的WrapMode属性来控制。 (如DataGridView.DefaultCellStyle.WrapMode)。将WrapMode 属性DataGridViewTriState枚举的三个取值之一。
下面的代码示例使用DataGridView.DefaultCellStyle属性设置整个控件所包含的单元格的WrapMode属性(即设置所有单元格的换行模式)。
this.dataGridView1.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
16. 如何使Image列不显示任何图像(字段值为null时)?
默认情况下Image类型的列和单元格将null值转换为标准的“X”图像( ),将Image列的NullValue属性设置为null可使该列不显示任何图像。下面这行代码演示了如何设置Image列的NullValue属性。
this.dataGridViewImageColumn1.DefaultCellStyle.NullValue = null;
17. 如何能够在ComboBox类型的单元格中输入数据?
默认情况下,DataGridViewComboBoxCell不接受用户的输入值。但有时确实有向ComboxBox输入数据的需要。实现这个功能,你需要做两件事。一是将ComboBox编辑控件的DropDownStyle属性设置为DropDown,使用户可以进行输入(否则只能进行选择);二是确保用户输入的值能够添加到ComboBox的Items集合。这是因为ComboBoxCell的值必须在Items集合中,否则会触发DataError事件(参看3.5.1节),而适合添加新值到Items集合的地方是CellValidating事件处理函数:
private void dataGridView1_CellValidating(object sender,
DataGridViewCellValidatingEventArgs e)
{
if (e.ColumnIndex == comboBoxColumn.DisplayIndex)
{
if (!this.comboBoxColumn.Items.Contains(e.FormattedValue))
{
this.comboBoxColumn.Items.Add(e.FormattedValue);
}
}
}
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
if (this.dataGridView1.CurrentCellAddress.X == comboBoxColumn.DisplayIndex)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
cb.DropDownStyle = ComboBoxStyle.DropDown;
}
}
}
18. How do I have a combo box column display a sub set of data based upon the value of a different combo box column(TODO)?
Sometimes data that you want to display in the DataGridView has a relationship between two tables such as a category and subcategory. You want to let the user select the category and then choose between a subcategory based upon the category. This is possible with the DataGridView by using two combo box columns. To enable this, two versions of the filtered list (subcategory) needs to be created. One list has no filter applied while the other one will be filtered only when the user is editing a subcategory cell. Two lists are required due to the requirement described in3.5.1 section that a combo box cells value must be in the items collection or else a DataError event is raised. In this case, since all combo box cells in the column use the same datasource if you filter the datasource for one row then a combo box cell in another row might not have its value visible in the datasource, thus causing a DataError event.
The below example uses the Northwind database to display related data from the Territory and Region tables (a territory is in a specific region.) Using the category and subcategory concept, the Region is the category and the Territory is the subcategory.
private void Form1_Load(object sender, EventArgs e)
{
this.territoriesTableAdapter.Fill(this.northwindDataSet.Territories);
this.regionTableAdapter.Fill(this.northwindDataSet.Region);
// Setup BindingSource for filtered view.
filteredTerritoriesBS = new BindingSource();
DataView dv = new DataView(northwindDataSet.Tables["Territories"]);
filteredTerritoriesBS.DataSource = dv;
}
private void dataGridView1_CellBeginEdit(object sender,
DataGridViewCellCancelEventArgs e)
{
if (e.ColumnIndex == territoryComboBoxColumn.Index)
{
// Set the combobox cell datasource to the filtered BindingSource
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1
[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = filteredTerritoriesBS;
// Filter the BindingSource based upon the region selected
this.filteredTerritoriesBS.Filter = "RegionID = " +
this.dataGridView1[e.ColumnIndex - 1, e.RowIndex].Value.ToString();
}
}
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == this.territoryComboBoxColumn.Index)
{
// Reset combobox cell to the unfiltered BindingSource
DataGridViewComboBoxCell dgcb = (DataGridViewComboBoxCell)dataGridView1
[e.ColumnIndex, e.RowIndex];
dgcb.DataSource = territoriesBindingSource; //unfiltered
this.filteredTerritoriesBS.RemoveFilter();
}
}
19. 如何在用户编辑控件的时候(而不是在验证时)就显示错误图标?
在使用错误文本和图标时,有时你希望为用户提供一个即时反馈,以提示当前的输入不正确。默认情况下,即使设置了ErrorText属性,如果单元格仍处于编辑模式下,那么错误图标也不会显示,比如TextBox和ComboBox。
下面的示例演示了如何在CellValidating事件中填充(padding)一个单元格为错误图标提供空间。因为默认情况下填充行为会影响错误图标的位置,该示例(TODO)。The below sample demonstrates how you can set a cell’s padding in the CellValidating event to provide spacing for the error icon. Since padding by default affects the location of the error icon the sample uses the CellPainting to move the position of the icon for painting. Lastly, the sample uses the tooltip control to display a custom tooltip when the mouse is over the cell to indicate what the problem is. This sample could also be written as a custom cell that overrides GetErrorIconBounds method to provide a location for the error icon that was independent of the padding.
private ToolTip errorTooltip;
private Point cellInError = new Point(-2, -2);
public Form1()
{
InitializeComponent();
dataGridView1.ColumnCount = 3;
dataGridView1.RowCount = 10;
}
private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (dataGridView1.IsCurrentCellDirty)
{
if (e.FormattedValue.ToString() == "BAD")
{
DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];
cell.ErrorText = "Invalid data entered in cell";
// increase padding for icon. This moves the editing control
if (cell.Tag == null)
{
cell.Tag = cell.Style.Padding;
cell.Style.Padding = new Padding(0, 0, 18, 0);
cellInError = new Point(e.ColumnIndex, e.RowIndex);
}
if (errorTooltip == null)
{
errorTooltip = new ToolTip();
errorTooltip.InitialDelay = 0;
errorTooltip.ReshowDelay = 0;
errorTooltip.Active = false;
}
e.Cancel = true;
}
}
}
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (dataGridView1.IsCurrentCellDirty && !String.IsNullOrEmpty(e.ErrorText))
{
// paint everything except error icon
e.Paint(e.ClipBounds, DataGridViewPaintParts.All &
~(DataGridViewPaintParts.ErrorIcon));
// now move error icon over to fill in the padding space
GraphicsContainer container = e.Graphics.BeginContainer();
e.Graphics.TranslateTransform(18, 0);
e.Paint(this.ClientRectangle, DataGridViewPaintParts.ErrorIcon);
e.Graphics.EndContainer(container);
e.Handled = true;
}
}
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView1[e.ColumnIndex, e.RowIndex].ErrorText != String.Empty)
{
DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];
cell.ErrorText = String.Empty;
cellInError = new Point(-2,-2);
// restore padding for cell. This moves the editing control
cell.Style.Padding = (Padding)cell.Tag;
// hide and dispose tooltip
if (errorTooltip != null)
{
errorTooltip.Hide(dataGridView1);
errorTooltip.Dispose();
errorTooltip = null;
}
}
}
// show and hide the tooltip for error
private void dataGridView1_CellMouseMove(object sender,
DataGridViewCellMouseEventArgs e)
{
if (cellInError.X == e.ColumnIndex &&
cellInError.Y == e.RowIndex)
{
DataGridViewCell cell = dataGridView1[e.ColumnIndex, e.RowIndex];
if (cell.ErrorText != String.Empty)
{
if (!errorTooltip.Active)
{
errorTooltip.Show(cell.ErrorText, dataGridView1, 1000);
}
errorTooltip.Active = true;
}
}
}
private void dataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
if (cellInError.X == e.ColumnIndex &&
cellInError.Y == e.RowIndex)
{
if (errorTooltip.Active)
{
errorTooltip.Hide(dataGridView1);
errorTooltip.Active = false;
}
}
}
20. 如何同时显示绑定数据和非绑定数据?
The data you display in the DataGridView control will normally come from a data source of some kind, but you might want to display a column of data that does not come from the data source. This kind of column is called an unbound column. Unbound columns can take many forms. As discussed in the data section above, you can use virtual mode to display additional data along with bound data.
The following code example demonstrates how to create an unbound column of check box cells to enable the user to select database records to process. The grid is put into virtual mode and responds to the necessary events. The selected records are kept by ID in a dictionary to allow the user to sort the content but not lose the checked rows.
private System.Collections.Generic.Dictionary<int, bool> checkState;
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = customerOrdersBindingSource;
// The check box column will be virtual.
dataGridView1.VirtualMode = true;
dataGridView1.Columns.Insert(0, new DataGridViewCheckBoxColumn());
// Initialize the dictionary that contains the boolean check state.
checkState = new Dictionary<int, bool>();
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
// Update the status bar when the cell value changes.
if (e.ColumnIndex == 0 && e.RowIndex != -1)
{
// Get the orderID from the OrderID column.
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
checkState[orderID] = (bool)dataGridView1.Rows[e.RowIndex].Cells[0].Value;
}
private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
// Handle the notification that the value for a cell in the virtual column
// is needed. Get the value from the dictionary if the key exists.
if (e.ColumnIndex == 0)
{
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
if (checkState.ContainsKey(orderID))
{
e.Value = checkState[orderID];
}
else
e.Value = false;
}
}
private void dataGridView1_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)
{
// Handle the notification that the value for a cell in the virtual column
// needs to be pushed back to the dictionary.
if (e.ColumnIndex == 0)
{
// Get the orderID from the OrderID column.
int orderID = (int)dataGridView1.Rows[e.RowIndex].Cells["OrderID"].Value;
// Add or update the checked value to the dictionary depending on if the
// key (orderID) already exists.
if (!checkState.ContainsKey(orderID))
{
checkState.Add(orderID, (bool)e.Value);
}
else
checkState[orderID] = (bool)e.Value;
}
}
21. How do I show data that comes from two tables(TODO)?
The DataGridView does not provide any new features apart from virtual mode to enable this. What you can do is use the JoinView class described in the following articlehttp://support.microsoft.com/default.aspx?scid=kb;en-us;325682. Using this class you can join two or more DataTables together. This JoinView can then be databound to the DataGridView.
22. 如何显示主从表?
使用DataGridView时最常见的情况之一就是主从表单,这时要显示具有主从关系的两个数据表。在主表中选择一行记录,从表中也会随之变化,显示相应的记录。
通过DataGridView控件和BindingSource 组件的交互作用来实现主从表单是非常简单的。下面的示例演示的是SQL Server的范例数据库Northwind 中的两个表:Customers 和Orders。在主DataGridView中选择一个顾客,那么该顾客的所有订单会显示在从DataGridView 中。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
public class Form1 : System.Windows.Forms.Form
{
private DataGridView masterDataGridView = new DataGridView();
private BindingSource masterBindingSource = new BindingSource();
private DataGridView detailsDataGridView = new DataGridView();
private BindingSource detailsBindingSource = new BindingSource();
[STAThreadAttribute()]
public static void Main()
{
Application.Run(new Form1());
}
// Initializes the form.
public Form1()
{
masterDataGridView.Dock = DockStyle.Fill;
detailsDataGridView.Dock = DockStyle.Fill;
SplitContainer splitContainer1 = new SplitContainer();
splitContainer1.Dock = DockStyle.Fill;
splitContainer1.Orientation = Orientation.Horizontal;
splitContainer1.Panel1.Controls.Add(masterDataGridView);
splitContainer1.Panel2.Controls.Add(detailsDataGridView);
this.Controls.Add(splitContainer1);
this.Load += new System.EventHandler(Form1_Load);
this.Text = "DataGridView master/detail demo";
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Bind the DataGridView controls to the BindingSource
// components and load the data from the database.
masterDataGridView.DataSource = masterBindingSource;
detailsDataGridView.DataSource = detailsBindingSource;
GetData();
// Resize the master DataGridView columns to fit the newly loaded data.
masterDataGridView.AutoResizeColumns();
// Configure the details DataGridView so that its columns automatically
// adjust their widths when the data changes.
detailsDataGridView.AutoSizeColumnsMode =
DataGridViewAutoSizeColumnsMode.AllCells;
}
private void GetData()
{
try
{
// Specify a connection string. Replace the given value with a
// valid connection string for a Northwind SQL Server sample
// database accessible to your system.
String connectionString =
"Integrated Security=SSPI;Persist Security Info=False;" +
"Initial Catalog=Northwind;Data Source=localhost";
SqlConnection connection = new SqlConnection(connectionString);
// Create a DataSet.
DataSet data = new DataSet();
data.Locale = System.Globalization.CultureInfo.InvariantCulture;
// Add data from the Customers table to the DataSet.
SqlDataAdapter masterDataAdapter = new
SqlDataAdapter("select * from Customers", connection);
masterDataAdapter.Fill(data, "Customers");
// Add data from the Orders table to the DataSet.
SqlDataAdapter detailsDataAdapter = new
SqlDataAdapter("select * from Orders", connection);
detailsDataAdapter.Fill(data, "Orders");
// Establish a relationship between the two tables.
DataRelation relation = new DataRelation("CustomersOrders",
data.Tables["Customers"].Columns["CustomerID"],
data.Tables["Orders"].Columns["CustomerID"]);
data.Relations.Add(relation);
// Bind the master data connector to the Customers table.
masterBindingSource.DataSource = data;
masterBindingSource.DataMember = "Customers";
// Bind the details data connector to the master data connector,
// using the DataRelation name to filter the information in the
// details table based on the current row in the master table.
detailsBindingSource.DataSource = masterBindingSource;
detailsBindingSource.DataMember = "CustomersOrders";
}
catch (SqlException)
{
MessageBox.Show("To run this example, replace the value of the " +
"connectionString variable with a connection string that is " +
"valid for your system.");
}
}
}
23. 如何在同一DataGridView中显示主从表?
DataGridView 不支持在同一DataGridView 中显示主从表。Windows Forms的先前版本中的DataGrid控件或许是你需要的一个解决方案。
24. 如何避免用户对列排序?
对于DataGridView 控件,默认情况下,TextBox类型的列会自动排序,而其它类型的列则不会自动排序。这种自动排序有时会把数据变得比较乱,这时你会想更改这些默认设置。
DataGridViewColumn的属性SortMode决定了列的排序方式,将其设置为DataGridViewColumnSortMode.NotSortable就可以避免默认的排序行为。
25. 如何在点击工具栏按钮的时候将数据提交到数据库?
默认情况下,操作工具栏或菜单不会导致对控件的验证。但对于绑定控件来说,提交数据前进行验证是必要的。而一旦窗体和其中的所有控件得到验证,当前编辑过的数据就需要提交。最后,数据适配器(如SqlDataAdapter)需要将数据的修改写入数据库。要达到这个效果,将下面三行代码加到相应的事件处理函数(指工具栏按钮或菜单项的事件)内:
this.Validate();
this.customersBindingSource.EndEdit(); this.customersTableAdapter.Update(this.northwindDataSet.Customers);
26. 如何在用户删除记录时显示确认对话框?
当用户选择DataGridView的一行,按下Delete键时就会触发UserDeletingRow 事件。你可以提示用户是否确定要删除该行记录,建议仅在用户要删除已存在的记录(而不是用户添加的新行)时才进行这种提示。将下面这些代码添加到UserDeletingRow事件的处理方法中就可以实现这种功能:
if (!e.Row.IsNewRow)
{
DialogResult response = MessageBox.Show("Are you sure?", "Delete row?",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
if (response == DialogResult.No)
e.Cancel = true;
}