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