C# DataGridView控件使用完全攻略


DataGridView 控件.doc C# DataGridView 控件使用完全攻略 DataGridView 是用于 Windows Froms 2.0 的新网格控件。它可以取代先前版本中 DataGrid 控 件,它易于使用并高度可定制,支持很多我们的用户需要的特性。 关于本文档: 本文档不准备面面俱到地介绍 DataGridView,而是着眼于深入地介绍一些技术点的高级特 性。 本文档按逻辑分为 5 个章节,首先是结构和特性的概览,其次是内置的列/单元格类型的介 绍,再次是数据操作相关的内容,然后是主要特性的综述,最后是最佳实践。 大部分章节含有一个“Q & A”部分,来回答该章节相关的一些常见问题。注意,某些问题会由 于知识点的关联性重复出现在多个章节。这些问题、答案及其附带的示例代码都包含在本文档的附 录部分。 注意:本文档的大部分内容来自 MSDN 中 DataGridView 控件的相关文档。 内容 1 何为DataGridView.........................................................................................................................................4 1.1 DataGridView和DataGrid 之间的区别 ...................................................................................................4 1.2 DataGridView的亮点................................................................................................................................4 2 DataGridView的结构...................................................................................................错误!未定义书签。 2.1 结构元素....................................................................................................................................................6 2.2 单元格和组................................................................................................................................................6 2.3 DataGridView的单元格............................................................................................................................6 2.3.1 DataGridViewCell的工作机制 .......................................................................................................7 2.4 DataGridView的列 ...................................................................................................................................8 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.................................................................................................................12 3.5 DataGridViewComboBoxColumn..........................................................................................................13 3.5.1 DataError与ComboBox列 .............................................................................................................13 3.6 DataGridViewLinkColumn ....................................................................................................................13 4 操作数据 .......................................................................................................................................................15 4.1 数据输入和验证的相关事件..................................................................................................................15 4.1.1 数据验证相关事件的顺序 ............................................................................................................15 4.1.2 验证数据........................................................................................................................................15 4.1.3 在新行中的数据输入 ....................................................................................................................16 4.2 关于Null值 ..............................................................................................................................................18 4.2.1 NullValue属性...............................................................................................................................19 4.2.2 DataSourceNullValue属性 ............................................................................................................19 4.3 DataError事件.........................................................................................................................................19 4.4 数据绑定模式..........................................................................................................................................20 4.4.1 非绑定模式 ....................................................................................................................................20 Page 1 of 67 DataGridView 控件.doc 4.4.2 绑定模式........................................................................................................................................21 4.4.3 虚拟模式........................................................................................................................................21 4.4.4 混合模式........................................................................................................................................22 5 Overview of features ....................................................................................................................................23 5.1 Styling......................................................................................................................................................23 5.1.1 The DataGridViewCellStyle Class ................................................................................................23 5.1.2 Using DataGridViewCellStyle Objects.........................................................................................23 5.1.3 Style Inheritance ...........................................................................................................................24 5.1.4 Setting Styles Dynamically............................................................................................................26 5.2 Custom painting......................................................................................................................................27 5.2.1 Paint Parts......................................................................................................................................27 5.2.2 Row Pre Paint and Post Paint.......................................................................................................28 5.3 Autosizing................................................................................................................................................28 5.3.1 Sizing Options in the Windows Forms DataGridView Control..................................................29 5.3.2 Resizing with the Mouse...............................................................................................................30 5.3.3 Automatic Sizing ...........................................................................................................................31 5.3.4 Programmatic Resizing .................................................................................................................32 5.3.5 Customizing Content-based Sizing Behavior...............................................................................32 5.3.6 Content-based Sizing Options.......................................................................................................33 5.4 Selection modes.......................................................................................................................................33 5.4.1 Programmatic Selection ................................................................................................................34 5.5 Scrolling...................................................................................................................................................34 5.5.1 Scroll event ....................................................................................................................................34 5.5.2 Scroll bars.......................................................................................................................................34 5.5.3 Scrolling Properties .......................................................................................................................34 5.6 Sorting .....................................................................................................................................................35 5.6.1 Programmatic Sorting ...................................................................................................................36 5.6.2 Custom Sorting ..............................................................................................................................37 5.7 Border styles............................................................................................................................................37 5.7.1 Standard Border Styles ..................................................................................................................38 5.7.2 Advanced Border Styles ................................................................................................................38 5.8 Enter-Edit modes ....................................................................................................................................39 5.9 Clipboard copy modes.............................................................................................................................39 5.10 Frozen columns/rows..............................................................................................................................40 5.11 Implementing Custom cells and editing controls/cells .........................................................................40 5.11.1 IDataGridViewEditingControl......................................................................................................40 5.11.2 IDataGridViewEditingCell............................................................................................................41 5.12 Virtual mode............................................................................................................................................41 5.12.1 Bound Mode and Virtual Mode ....................................................................................................41 5.12.2 Supplementing Bound Mode.........................................................................................................41 5.12.3 Replacing Bound Mode .................................................................................................................41 5.12.4 Virtual-Mode Events.....................................................................................................................42 5.12.5 Best Practices in Virtual Mode......................................................................................................43 5.13 Capacity...................................................................................................................................................43 6 Best Practices ................................................................................................................................................44 6.1 Using Cell Styles Efficiently ...................................................................................................................44 6.2 Using Shortcut Menus Efficiently ..........................................................................................................44 6.3 Using Automatic Resizing Efficiently....................................................................................................44 6.4 Using the Selected Cells, Rows, and Columns Collections Efficiently.................................................45 Page 2 of 67 DataGridView 控件.doc 6.5 Using Shared Rows..................................................................................................................................45 6.6 Preventing Rows from Becoming Unshared..........................................................................................46 附录 A – 常见问题: ............................................................................................................................................48 1. 如何使指定的单元格不可编辑? ............................................................................................................48 2. 如何让一个单元格不可用? ....................................................................................................................48 3. 如何避免用户将焦点设置到指定的单元格?......................................................................................50 4. 如何使所有单元格总是显示控件(不论它是否处于编辑状态)?..........................................................50 5. Why does the cell text show up with “square” characters where they should be new lines? ............50 6. 如何在单元格内同时显示图标和文本?..............................................................................................50 7. 如何隐藏一列?......................................................................................................................................52 8. 如何避免用户对列排序?......................................................................................................................52 9. 如何针对多个列排序? ............................................................................................................................52 10. 如何为编辑控件添加事件处理函数? ....................................................................................................57 11. 应在何时移除编辑控件的事件处理函数?.............................................................................................57 12. 如何处理ComboBox列中ComboBox控件的SelectIndexChanged事件?..............................................57 13. 如何通过拖放调整行的顺序?..............................................................................................................58 14. 如何调整最后一列的宽度使其占据网格的剩余客户区? ..................................................................59 15. 如何让TextBox类型的单元格支持换行?...............................................................................................59 16. 如何使Image列不显示任何图像(字段值为null时)? .............................................................................59 17. 如何能够在ComboBox类型的单元格中输入数据? ..............................................................................60 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? .............................................................................................................................................................60 19. 如何在用户编辑控件的时候(而不是在验证时)就显示错误图标? ......................................................61 20. 如何同时显示绑定数据和非绑定数据?..............................................................................................63 21. How do I show data that comes from two tables?.................................................................................65 22. 如何显示主从表?..................................................................................................................................65 23. 如何在同一DataGridView中显示主从表?..........................................................................................67 24. 如何避免用户对列排序? ........................................................................................................................67 25. 如何在点击工具栏按钮的时候将数据提交到数据库? ......................................................................67 26. 如何在用户删除记录时显示确认对话框?..........................................................................................67 Page 3 of 67 DataGridView 控件.doc 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的主要特性,稍后会介绍它们的详细信息。 Page 4 of 67 DataGridView 控件.doc DataGridView控件特性 描述 多种列类型 DataGridView提供有TextBox、CheckBox、Image、Button、ComboBox和 Link类型的列及相应的单元格类型。 多种数据显示方式 DataGrid仅限于显示外部数据源的数据。而DataGridView则能够显示非绑定 的数据,绑定的数据源,或者同时显示绑定和非绑定的数据。你也可以在 DataGridView中实现virtual mode,实现自定义的数据管理。 自定义数据的显示和操作的多种方式 DataGridView提供了很多属性和事件,用于数据的格式化和显示。 此外,DataGridView提供了操作数据的多种方式,比如,你可以: ƒ 对数据排序,并显示相应的排序符号(带方向的箭头表示升降序) ƒ 对行、列和单元格的多种选择模式;多项选择和单项选择 ƒ 以多种格式将数据拷贝到剪贴板,包括text,CSV (以逗号隔开的值) 和 HTML ƒ 改变用户编辑单元格内容的方式 用于更改单元格、行、列、表头外观 和行为的多个选项 DataGridView使你能够以多种方式操作单个网格组件。比如,你可以: ƒ 冻结行和列,避免它们因滚动而不可见; ƒ 隐藏行、列、表头; ƒ 改变行、列、表头尺寸的调整方式; ƒ 改变用户对行、列、单元格的选择模式; ƒ 为单个的单元格、行和列提供工具提示(ToolTip)和快捷菜单。 ƒ 自定义单元格、行和列的边框样式。 提供丰富的可扩展性的支持 DataGridView提供易于对网格进行扩展和自定义的基础结构,比如: ƒ 处理自定义的绘制事件可以为单元格、列和行提供自定义的观感; ƒ 继承一个内置的单元格类型以为其提供更多的行为; ƒ 实现自定义的接口以提供新的编辑体验。 Page 5 of 67 DataGridView 控件.doc 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属性访问当前的单元格。 Page 6 of 67 DataGridView 控件.doc 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属性则取决于 Page 7 of 67 DataGridView 控件.doc 属性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 常见问题 1) 如何使指定的单元格不可编辑? 2) 如何让一个单元格不可用(disable)? 3) 如何避免用户将焦点设置到指定的单元格? 4) 如何使所有单元格总是显示控件(不论它们是否处于编辑状态)? 5) Why does the cell text show up with “square” characters where they should be new lines? 6) 如何在单元格内同时显示图标和文本? 2.4 DataGridView的列(DataGridViewColumn) DataGridView所附带的数据(这些数据可以通过绑定或非绑定方式附加到控件)的结构表现为 DataGridView的列。你可以使用DataGridView的Columns集合属性访问DataGridView所包含的列, 使用SelectedColumns 集合属性访问当前选中的列。 Page 8 of 67 DataGridView 控件.doc 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 Page 9 of 67 DataGridView 控件.doc 常见问题(FAQ) 1) 如何为编辑控件添加事件处理函数? 2) 应在何时移除编辑控件的事件处理函数? 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 常见问题 1) 如何通过拖放调整行的顺序? Page 10 of 67 DataGridView 控件.doc 3 列/单元格类型揭密(column/cell types) DataGridView控件提供了几种列类型用以显示数据,并允许用户修改和添加数据。 当你对DataGridView进行了绑定,并将它的AutoGenerateColumns属性设置为true,它会根据 数据源中列的数据类型自动生成列,这些列都使用相应的默认类型(与数据源列数据类型相适应)。 你也可以自行创建列的实例,将它们加入DataGridView的Columns集合中,这些列可用作非绑 定列,也可以以手动方式让它们用于绑定数据。手动绑定的列非常有用,比如,自动生成的列都采 用与数据源的列相应的默认类型,而你不想用默认列类型。 下表描述了DataGridView 的各种列对应的类: 列类型 描述 DataGridViewTextBoxColumn 用于基于文本的值。绑定到数字和字符串值时会自动生成这种类型的列。 DataGridViewCheckBoxColumn 用于显示Boolean和CheckState类型的值,绑定到上述类型值时会自动生成这种 类型的列。 DataGridViewImageColumn 用于显示图像。绑定到byte数组,Image对象,图标对象时会自动生成这种类型 的列。 DataGridViewButtonColumn 用于在单元格内显示按钮。在绑定时不会自动生成,一般用于非绑定列。 DataGridViewComboBoxColumn 用于在单元格内显示下拉列表。在绑定时不会自动生成,一般地需要手工绑定。 DataGridViewLinkColumn 用于在单元格内显示链接。在绑定时不会自动生成,一般地需要手工绑定。 自定义列类型 通过继承DataGridViewColumn 类或其子类,你可以创建自己的列类型,以提供 自定义的外观、行为和宿主控件。 常见问题(FAQ) 1) 如何隐藏一列? 2) 如何使特定的单元格不可编辑? 3) 如何避免用户将焦点设置到指定的单元格? 4) 如何调整最后一列的宽度使其占据网格的剩余客户区? 3.1 DataGridViewTextBoxColumn DataGridViewTextBoxColumn是一种通用的列类型,用于表示基于文本的值,比如数字和字符 串。在编辑模式下,会有一个TextBox控件出现在当前活动单元格,用户可以修改单元格的值。 单元格的值在显示时会自动转换为字符串。用户输入或修改的值在提交时则被自动解析为合适 的数据类型以创建一个单元格的值。通过处理CellFoamatting和CellParsing事件,你可以自定义这些 转换的方式。比如将数据源的日期字段以特定的形式显示,对某些特殊单元格作出特殊的标记。 Page 11 of 67 DataGridView 控件.doc 对一列来说,它包含的单元格值的数据类型由该列的ValueType属性指定。 3.1.1 常见问题 1) 如何让TextBox类型的单元格支持换行? 2) Why does the cell text show up with “square” characters where they should be new lines? 3) 如何在单元格内同时显示图标和文本? 4) 如何避免用户将焦点设置到指定的单元格? 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列会很有用,比如排序或在另一个窗体中显示子表记录。 Page 12 of 67 DataGridView 控件.doc 在对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 常见问题 1) 如何能够在ComboBox类型的单元格中输入数据? 2) How do I handle the SelectedIndexChanged event? 3) How do I have a combo box column display a sub set of data based upon the value of a different combo box column? 3.6 DataGridViewLinkColumn 使用DataGridViewLinkColumn列,你可以显示一列包含超链接的单元格。在显示数据源中的 URL值,或者替代按钮列进行一些特殊行为,如打开另一个子记录窗体时会很有用。 Link列也不会在DataGridView数据绑定时自动生成。要使用它,你还得手动创建,然后将它添 加到DataGridView控件的Columns集合中。 Page 13 of 67 DataGridView 控件.doc 你可以处理CellContentClick事件来相应用户的点击动作。这个事件不同于CellClick 和 CellMouseClick 事件,后两者在用户点击单元格任何位置(而不仅仅时链接)时都会触发。 DataGridViewLinkColumn 类提供了几个属性,用来修改链接的外观,包括点击前,点击时和 点击后(类似于网页中的超链接)。 Page 14 of 67 DataGridView 控件.doc 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还能够为行或单元格显示一个错误图标来通知用户输入了无效数 据。错误图标带有一个工具提示,它提供了该错误的相关信息: Page 15 of 67 DataGridView 控件.doc 4.1.2.2 常见问题(FAQ) 1) 如何在用户编辑单元格的时候显示错误图标? 4.1.3 在新行中的数据输入(Data Entry in the New Row) 当在程序中使用DataGridView来编辑数据时,你往往希望提供让用户添加新行数据的功能。 DataGridView控件支持这个功能,提供了一个用于添加新记录的行,而这一行总是显示为最后一 行,并在该行的标题单元格标以星号(*)。 下面的几个小节会讨论一些在程序中使用这个新行时需 要考虑的内容。(下面总是以 新行 表示 用于添加新记录的行 ) 4.1.3.1 显示新行 使用AllowUserToAddRows属性以指示是否显示新行,其默认值为true。 新行处于网格的最后一行,标题带有星号: 在数据绑定的情况下,当DataGridView控件的AllowUserToAddRows属性和数据源的 IBindingList.AllowNew 属性都为true时,新行才会显示,只要两者有一个为false,新行就不会显 示。 Page 16 of 67 DataGridView 控件.doc 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中会产生一个“新”的新行,看下面示意图: Page 17 of 67 DataGridView 控件.doc 在添加“新”的新行时,会触发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值的方式。 Page 18 of 67 DataGridView 控件.doc 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 (“”) 默认值取决于ThreeState属性的值,如果为true,默认值 为CheckState.Indeterminate ,否则为unchecked。 CheckBoxColumn 有一点要了解,在用户输入数据时也会用到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事件的编辑操作: ƒ 刷新一个编辑 (通过调用RefreshEdit方 法) ƒ 取消编辑(Canceling an edit) ƒ 结束编辑(Ending an edit) ƒ 尝试将单元格的值写入数据源 ƒ 初始化编辑控件\单元格的值(通过设置 单元格的FormattedValue属性或调用单 元格的InitializeEditingControl方法) ƒ 提交编辑(Committing an edit) ƒ 删除一行(Deleting a row) Page 19 of 67 DataGridView 控件.doc DataError的上下文: 下面的列表显示了不同的DataError上下文环境,然后进一步说明了这些上下文环境合适可能发 生: DataErrorContext 何时发生 Formatting When attempting to retrieve the cell's formatted value. 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. Display When calculating the preferred size of a cell. This usually also requires getting the cell's formatted value also. PreferredSize RowDeletion Any exception raised when deleting a row. When exceptions occur when committing, ending or canceling an edit. Usually OR'd in with other error contexts Parsing When exceptions occur when committing an edit. Usually OR'd with other error contexts Commit When exceptions occur while either initializing the editing control/cell's value, or Canceling an edit InitialValueRestoration When exceptions occur while attempting to validate grid data when the grid is losing focus. Usually OR'd with other error contexts. LeaveControl When exceptions occur while validating\updating\committing\getting cell content when the current cell changes. Usually OR'd with other error contexts. CurrentCellChange When exceptions occur while validating\updating\committing\getting cell content when the current cell changes as a result of scrolling. Scroll When exceptions occur while attempting to get the formatted value of a cell while creating the clipboard content. ClipboardContent 4.4 数据绑定模式(Databound modes) 4.4.1 非绑定模式(Unbound Mode) 如果你要在程序中管理数量相对较小的数据,那么非绑定模式会比较合适。此时你不是像绑定 模式中那样将DataGridView控件直接指向一个数据源,而是手动去生成控件。一般需要用到 DataGridViewRowCollection.Add 方法(该方法向DGV中添加行)。 非绑定模式在处理静态、只读的数据时特别有用,也可以用在以自己的方式与外部数据源交互 的情况,但实际上,如果你希望你的用户与外部的数据源交互,一般还是用绑定模式(bound mode) 更好。 Page 20 of 67 DataGridView 控件.doc 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类也可以在一个类的基础上创建自定义列表(list)。 对象更改通知(Object Change Notification) 如果你有了一个数据源,那么数据源中的对象就可以实现对public属性的更改通知。这需要你 为相应属性提供一个” PropertyNameChanged”事件,或者实现INotifyPropertyChanged接口。 INotifyPropertyChanged 是在VS 2005 中新加的接口,可以与BindingList一起使用来创建可绑定 的列表(list)。但当你的数据源是BindingSource ,那就不用再额外实现更改通知了。 4.4.3 虚拟模式 使用虚拟模式,你可以实现自己的数据管理操作。在绑定模式下,如果要使用非绑定列,那么 要想在对列排序时能够维护非绑定列的值,就需要虚拟模式。但虚拟模式的最主要的用途还是在操 作大量数据时优化性能。 你将DataGridView绑定到缓存的数据,然后用代码控制数据行的存取。要保持使用内存量比较 小,缓存的数据量应与当前要显示的行数相当。当用户滚动控件看到了新的行时,你的代码就从缓 存中请求新的数据,并从内存中清除旧的数据。 Page 21 of 67 DataGridView 控件.doc 如果你正要实现虚拟模式(Virtual Mode),需要考虑数据模型添加新行和回滚添加操作的情 况。该功能准确的实现方式取决于数据模型的实现方式及其事务机制,例如,提交的时候是针对单 元格还是行。参看本文档后面关于Virtual Mode的主题。 4.4.4 混合模式 – 绑定与非绑定模式 显示在DataGridView中的数据通常来自于某种类型的数据源,但是你可能也希望显示一个数据 源之外的列。这种列称为非绑定列。 你可以在绑定模式下添加非绑定列,在你希望显示一个按钮列或者链接列让用户操作一些特定 行时这显得很有用,另外也可以用非绑定列显示一些由绑定列计算而得到的值。你可以在 CellFormatting事件处理函数中生成计算列的值。不过如果你使用的数据源是DataSet或DataTable, 你可能希望使用DataColumn.Expression 属性来创建一个计算列,在这种情况下,在DGV看来,这 一列就跟数据源中其它列是一样的。 在绑定模式下根据非绑定列排序是不受支持的。如果你在绑定模式下创建了非绑定列,你必须 实现虚拟模式,这样在根据绑定列排序时可以维护非绑定列的值。 如果添加的非绑定列不能由数据源数据计算得来或者这些数据会频繁更新,你就应该使用虚拟 模式。要了解虚拟模式的更多信息,请参看本文档后面的虚拟模式相关章节。 4.4.5 常见问题 1) 如何同时显示绑定数据和非绑定数据? 2) How do I show data that comes from two tables?(TODO) 3) 如何显示主从表? 4) 如何在同一DataGridView内显示主从表? 5) 如何避免对一列的排序? 6) 如何针对多个列排序? 7) 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) 8) 如何在点击工具栏按钮的时候将数据提交到数据库? 9) 如何在用户删除记录时显示确认对话框? Page 22 of 67 DataGridView 控件.doc 5 特性综览(Overview of features) 5.1 样式(Styling) 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 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. Page 23 of 67 DataGridView 控件.doc DataGridView Gets or sets default cell styles used by all rows in the control. This does not include header cells. RowsDefaultCellStyle DataGridView Gets or sets default cell styles used by alternating rows in the control. Used to create a ledger-like effect.AlternatingRowsDefaultCellStyle DataGridView Gets or sets default cell styles used by the control's row headers. Overridden by the current theme if visual styles are enabled. RowHeadersDefaultCellStyle DataGridView Gets or sets default cell styles used by the control's column headers. Overridden by the current theme if visual styles are enabled. ColumnHeadersDefaultCellStyle DataGridViewCell and derived classes Gets or sets styles specified at the cell level. These styles override those inherited from higher levels. Style DataGridViewCell, DataGridViewRow, DataGridViewColumn, and derived classes Gets all the styles currently applied to the cell, row, or column, including styles inherited from higher levels. InheritedStyle 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. 1. DataGridViewCell.Style 2. DataGridViewRow.DefaultCellStyle 3. AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers) 4. RowsDefaultCellStyle 5. DataGridViewColumn.DefaultCellStyle 6. 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. 1. DataGridViewCell.Style 2. ColumnHeadersDefaultCellStyle or RowHeadersDefaultCellStyle Page 24 of 67 DataGridView 控件.doc 3. 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. 1. DataGridViewColumn.DefaultCellStyle 2. DefaultCellStyle The row InheritedStyle property inherits its values from the following properties. 1. DataGridViewRow.DefaultCellStyle 2. AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers) 3. RowsDefaultCellStyle 4. 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. Example ForeColor value for retrieved object Property of type DataGridViewCellStyle DataGridViewCell.Style Color.Empty DataGridViewRow.DefaultCellStyle Color.Red AlternatingRowsDefaultCellStyle Color.Empty RowsDefaultCellStyle Color.Empty DataGridViewColumn.DefaultCellStyle Color.DarkBlue Page 25 of 67 DataGridView 控件.doc 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 Page 26 of 67 DataGridView 控件.doc 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 Page 27 of 67 DataGridView 控件.doc 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 text Current row triangle, edit pencil and new row indicator is painted Row Header 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 Page 28 of 67 DataGridView 控件.doc 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. 常见问题: 1) 如何调整最后一列的宽度使其占据网格的剩余客户区? 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. 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. Control resize Cell value change In content-based automatic sizing modes, sizes change to fit new display values. Programmatic content-based resizing lets you make opportunistic size adjustments based on cell values at the time of the method call. 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 Page 29 of 67 DataGridView 控件.doc data. 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 with values of varying importance. 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. 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. Display a fixed-width column, such as an icon or ID column. 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 automatically whenever cell contents change to avoid clipping and to optimize the use of space. 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 values in displayed rows to avoid performance penalties when working with many rows. 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 sizes to fit cell contents only at specific times to avoid performance penalties or to enable user resizing. 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. Adjust row heights for multiline cell contents. 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 Page 30 of 67 DataGridView 控件.doc 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. Page 31 of 67 DataGridView 控件.doc 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(), Page 32 of 67 DataGridView 控件.doc 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. DataGridViewSelecti onMode 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. Page 33 of 67 DataGridView 控件.doc 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. Page 34 of 67 DataGridView 控件.doc 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 Default for text box columns. Unless column headers are used for selection, clicking the column header automatically sorts the DataGridView by this column and displays a glyph indicating the sort order. Automatic 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. NotSortable You can sort this column programmatically, and space is reserved for the sorting glyph. Programmatic 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 Page 35 of 67 DataGridView 控件.doc 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, Page 36 of 67 DataGridView 控件.doc 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 Page 37 of 67 DataGridView 控件.doc 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 Page 38 of 67 DataGridView 控件.doc 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 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. EditOnEnter Editing begins when F2 is pressed while the cell has focus. This mode places the selection point at the end of the cell contents. EditOnF2 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: Page 39 of 67 DataGridView 控件.doc Clipboard Copy modes Description Disable Copying to the Clipboard is disabled. The text values of selected cells can be copied to the Clipboard. Header text is included for rows and columns that contain selected cells. EnableAlwaysIncludeHeaderText 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. EnableWithAutoHeaderText The text values of selected cells can be copied to the Clipboard. Header text is not included. EnableWithoutHeaderText 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 5.11 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. 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 Page 40 of 67 DataGridView 控件.doc 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. Page 41 of 67 DataGridView 控件.doc 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 Used by the control to retrieve a cell value from the data cache for display. This event occurs only for cells in unbound columns. CellValueNeeded 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. CellValuePushed NewRowNeeded Used by the control to indicate the need for a new row in the data cache. Used by the control to determine whether a row has any uncommitted changes. RowDirtyStateNeeded 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. Used by the control to retrieve cell ToolTip text when the DataSource property is set or the VirtualMode property is true. Cell ToolTips are displayed only when the ShowCellToolTips property value is true. CellToolTipTextNeeded CellErrorTextNeeded Used by the control to retrieve cell or row error text when the DataSource Page 42 of 67 DataGridView 控件.doc RowErrorTextNeeded 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 the VirtualMode 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 a RowHeightInfoPushed 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. Page 43 of 67 DataGridView 控件.doc 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 Page 44 of 67 DataGridView 控件.doc • 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. Page 45 of 67 DataGridView 控件.doc 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 Page 46 of 67 DataGridView 控件.doc 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. Page 47 of 67 DataGridView 控件.doc 附录 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() { Page 48 of 67 DataGridView 控件.doc 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); } Page 49 of 67 DataGridView 控件.doc } } 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; } Page 50 of 67 DataGridView 控件.doc 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; Page 51 of 67 DataGridView 控件.doc 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. 如何针对多个列排序? Page 52 of 67 DataGridView 控件.doc 默认情况下 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 Page 53 of 67 DataGridView 控件.doc 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(); } Page 54 of 67 DataGridView 控件.doc 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; Page 55 of 67 DataGridView 控件.doc 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) { Page 56 of 67 DataGridView 控件.doc 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 dataGri , dView1_EditingControlShowing(object sender DataGridViewEditingControlShowingEventArgs e) { ComboBox cb = e.Control as ComboBox; if (cb != null) { Page 57 of 67 DataGridView 控件.doc // 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), Page 58 of 67 DataGridView 控件.doc 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 nverted to client coordinates. // co 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时)? Page 59 of 67 DataGridView 控件.doc 默认情况下 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 in 3.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. Page 60 of 67 DataGridView 控件.doc 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) { datasour ngSource // Set the combobox cell ce to the filtered Bindi 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. Page 61 of 67 DataGridView 控件.doc 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)); icon over to fill in the padding space // now move error 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) Page 62 of 67 DataGridView 控件.doc { 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. Page 63 of 67 DataGridView 控件.doc 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 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(); } 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)) Page 64 of 67 DataGridView 控件.doc { 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 article http://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"; } Page 65 of 67 DataGridView 控件.doc 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;" + Catalog=Northwind; ocalhost"; "Initial Data Source=l 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 " + Page 66 of 67 DataGridView 控件.doc Page 67 of 67 "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; }
还剩66页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

tangsz

贡献于2012-05-15

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf