DataGridView控件使用大全

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提供了操作數據的多種方式,比如,你可以:

  • 對數據排序,並顯示相應的排序符號(帶方向的箭頭表示升降序)
  • 對行、列和單元格的多種選擇模式;多項選擇和單項選擇
  • 以多種格式將數據拷貝到剪貼板,包括text,CSV (以逗號隔開的值) 和 HTML
  • 改變用戶編輯單元格內容的方式

用於更改單元格、行、列、表頭外觀和行爲的多個選項

DataGridView使你能夠以多種方式操作單個網格組件。比如,你可以:

  • 凍結行和列,避免它們因滾動而不可見;
  • 隱藏行、列、表頭;
  • 改變行、列、表頭尺寸的調整方式;
  • 改變用戶對行、列、單元格的選擇模式;
  • 爲單個的單元格、行和列提供工具提示(ToolTip)和快捷菜單。
  • 自定義單元格、行和列的邊框樣式。

提供豐富的可擴展性的支持

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       常見問題

1)       如何使指定的單元格不可編輯?

2)       如何讓一個單元格不可用(disable)?

3)       如何避免用戶將焦點設置到指定的單元格?

4)       如何使所有單元格總是顯示控件(不論它們是否處於編輯狀態)?

5)       Why does the cell text show up with “square” characters where they should be new lines?

6)       如何在單元格內同時顯示圖標和文本?

2.4       DataGridView的列(DataGridViewColumn)

DataGridView所附帶的數據(這些數據可以通過綁定或非綁定方式附加到控件)的結構表現爲DataGridView的列。你可以使用DataGridView的Columns集合屬性訪問DataGridView所包含的列,使用SelectedColumns 集合屬性訪問當前選中的列。

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)

1)       如何爲編輯控件添加事件處理函數?

2)       應在何時移除編輯控件的事件處理函數?

3)       如何處理ComboBox列中Combox控件的SelectIndexChanged事件?

4)       如何使所有單元格總是顯示控件(不論它是否處於編輯狀態)?

2.6       DataGridViewRow

DataGridViewRow類用於顯示數據源的一行數據。可以通過DataGridView控件的Rows集合屬性來訪問其包含的行,通過SelectedRows集合屬性訪問當前選中的行。

DataGridViewRow類圖

Row相關的類和屬性

你可以繼承DataGridViewRow類來實現自己的行類型,雖然多數情況下這並不必要。DataGridView 有幾個行相關的事件和屬性,用以自定義其包含的DataGridViewRow對象的行爲。

如果你將DataGridView的AllowUserToAddRows屬性設爲true,一個專用於添加新行的特殊行會出現在最後一行的位置上,這一行也屬於Rows集合,但它有一些需要你提起注意的特殊功能,要獲得這方面的更多信息,請參看4.1.3節。

2.6.1       常見問題

1)       如何通過拖放調整行的順序?

3          列/單元格類型揭密(column/cell types)

DataGridView控件提供了幾種列類型用以顯示數據,並允許用戶修改和添加數據。

當你對DataGridView進行了綁定,並將它的AutoGenerateColumns屬性設置爲true,它會根據數據源中列的數據類型自動生成列,這些列都使用相應的默認類型(與數據源列數據類型相適應)。

你也可以自行創建列的實例,將它們加入DataGridView的Columns集合中,這些列可用作非綁定列,也可以以手動方式讓它們用於綁定數據。手動綁定的列非常有用,比如,自動生成的列都採用與數據源的列相應的默認類型,而你不想用默認列類型。

下表描述了DataGridView 的各種列對應的類:

列類型

描述

DataGridViewTextBoxColumn

用於基於文本的值。綁定到數字和字符串值時會自動生成這種類型的列。

DataGridViewCheckBoxColumn

用於顯示Boolean和CheckState類型的值,綁定到上述類型值時會自動生成這種類型的列。

DataGridViewImageColumn

用於顯示圖像。綁定到byte數組,Image對象,圖標對象時會自動生成這種類型的列。

DataGridViewButtonColumn

用於在單元格內顯示按鈕。在綁定時不會自動生成,一般用於非綁定列。

DataGridViewComboBoxColumn

用於在單元格內顯示下拉列表。在綁定時不會自動生成,一般地需要手工綁定。

DataGridViewLinkColumn

用於在單元格內顯示鏈接。在綁定時不會自動生成,一般地需要手工綁定。

自定義列類型

通過繼承DataGridViewColumn 類或其子類,你可以創建自己的列類型,以提供自定義的外觀、行爲和宿主控件。

常見問題(FAQ)

1)       如何隱藏一列?

2)       如何使特定的單元格不可編輯?

3)       如何避免用戶將焦點設置到指定的單元格?

4)     如何調整最後一列的寬度使其佔據網格的剩餘客戶區?

3.1       DataGridViewTextBoxColumn

DataGridViewTextBoxColumn是一種通用的列類型,用於表示基於文本的值,比如數字和字符串。在編輯模式下,會有一個TextBox控件出現在當前活動單元格,用戶可以修改單元格的值。

單元格的值在顯示時會自動轉換爲字符串。用戶輸入或修改的值在提交時則被自動解析爲合適的數據類型以創建一個單元格的值。通過處理CellFoamatting和CellParsing事件,你可以自定義這些轉換的方式。比如將數據源的日期字段以特定的形式顯示,對某些特殊單元格作出特殊的標記。

對一列來說,它包含的單元格值的數據類型由該列的ValueType屬性指定。

3.1.1       常見問題

1)     如何讓TextBox類型的單元格支持換行?

2)       Why does the cell text show up with “square” characters where they should be new lines?

3)       如何在單元格內同時顯示圖標和文本?

4)       如何避免用戶將焦點設置到指定的單元格?

3.2       DataGridViewCheckBoxColumn

DataGridViewCheckBoxColumn用於顯示Boolean或CheckState類型的值。Boolean 值顯示爲二元(two-state)或三元 (three-state) 的CheckBox,而這取決於該列的ThreeState 屬性的值。如果該類型的列綁定到CheckState類型的值,ThreeState屬性的默認值爲true。

一般情況下,CheckBox類型的單元格要麼用於存儲數據,就像其它類型的數據一樣,要麼用於進行一些重要操作。用戶點擊CheckBox單元格時,如果你希望對此立即做出反應,可以處理CellClick事件,但該事件發生在單元格的值更新之前。如果點擊之時就希望獲得新值,一種選擇是根據當前值計算點擊後的值;另一種方法是立即提交值的變化,然後在CellValueChanged事件處理函數中對此作出反應,而要在用戶點擊單元格時立即提交值的變化,你必須處理CurrentCellDirtyStateChanged事件,在這裏,調用CommitEnd方法提交新值。

3.3       DataGridViewImageColumn

DataGridViewImageColumn 類型的列用於顯示圖像。這種類型的列有三種方法生成:綁定到數據源時自動生成;爲非綁定列手動生成;在CellFormatting事件處理函數(該事件發生在單元格顯示前)中動態生成。

綁定到數據源時自動生成Image列的方法適用於大量的圖像格式,包括.NET中Image類支持的各種格式,還有Access數據庫及Northwind範例數據庫使用的OLE圖片格式。

如果你想提供DataGridViewButtonColumn列的功能,又希望顯示自定義的外觀,手動生成Image列會很有用。在顯示後,你可以處理CellClick事件以處理用戶對單元格的點擊(模擬按鈕列)。

如果你要爲計算值或非圖片的值提供圖片顯示,在CellFormatting事件處理函數中動態生成Image列的方法會很有用。比如,你有一個表示風險值的列,它的值可能是”high”、”middle”或”low”,可以爲它們顯示不同的圖標作爲警示;或者你有一個名爲”Image”的列,它的值時圖片文件的位置而不是真實的圖片內容,也可以用這種方法。

3.3.1       常見問題

1)     如何使Image列不顯示任何圖像(字段值爲null時)?

3.4       DataGridViewButtonColumn

使用DataGridViewButtonColumn 列,可以在單元格內顯示按鈕。如果你要爲用戶操作特定行提供一種簡單的方式,Button列會很有用,比如排序或在另一個窗體中顯示子表記錄。

在對DataGridView進行數據綁定時不會自動生成Button列,所以你必須手動創建它們,然後把它們添加到DataGridView控件的Columns集合中。

你可以處理CellClick事件以響應用戶的點擊動作。

3.5       DataGridViewComboBoxColumn

在DataGridViewComboBoxColumn類型的列中,你可以顯示包含下拉列表的單元格。這在僅允許用戶輸入一些特定值的時候顯得很有用,比如在SQL Server示例數據庫Northwind中Products表的Category列,它表示產品的種類,這個應只允許選擇現有的產品種類,此時就可以使用ComboBox列。

如果你瞭解如何爲ComboBox控件生成下拉列表,就可以用相同的方式爲ComboBox列中的所有單元格生成下拉列表。要麼通過列的Items集合手動添加,要麼通過DataSource,DisplayMember 和ValueMember屬性綁定到一個數據源。要了解其中的更多信息,可以參考WinForms中ComboBox空間的用法。

你可以將ComboBox列的單元格的實際值綁定到DataGridView控件本身的數據源(注意不是ComboBox列的數據源),這需要設置該列的DataPropertyName屬性(設置某個列的名稱)。

ComboBox列不會在數據綁定時自動生成,所以你必須手動創建它們,然後將其添加到Columns集合屬性中。另外,你也可以使用設計器,在設計時設置相應的屬性,這個過程類似於在設計器中ComboBox控件的使用。

3.5.1       DataError事件和ComboBox列

在使用DataGridViewComboBoxColumn 時,有時會修改單元格的值或啓動ComboBox控件的Items集合,這樣可能會引發DataError事件。這是ComboBox列的設計使然,ComboBox列的單元格會進行數據驗證。在ComboBox列的單元格嘗試繪製包含的內容時,它需要將包含的值進行格式化(見第二章第三節),在此轉換過程中,它會在ComboBox的Items集合中查找對應的值,如果查找失敗,就會引發DataError事件。忽略了DataError事件可能會使單元格不能進行正確的格式化。

3.5.2       常見問題

1)     如何能夠在ComboBox類型的單元格中輸入數據?

2)     How do I handle the SelectedIndexChanged event?

3)     How do I have a combo box column display a sub set of data based upon the value of a different combo box column?

3.6       DataGridViewLinkColumn

使用DataGridViewLinkColumn列,你可以顯示一列包含超鏈接的單元格。在顯示數據源中的URL值,或者替代按鈕列進行一些特殊行爲,如打開另一個子記錄窗體時會很有用。

Link列也不會在DataGridView數據綁定時自動生成。要使用它,你還得手動創建,然後將它添加到DataGridView控件的Columns集合中。

你可以處理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)

1)     如何在用戶編輯單元格的時候顯示錯誤圖標?

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事件的編輯操作:

  • 取消編輯(Canceling an edit)
  • 刷新一個編輯 (通過調用RefreshEdit方法)
  • 嘗試將單元格的值寫入數據源
  • 初始化編輯控件\單元格的值(通過設置單元格的FormattedValue屬性或調用單元格的InitializeEditingControl方法)
 
  • 結束編輯(Ending an edit)
 
  • 提交編輯(Committing an edit)
 
  • 刪除一行(Deleting a row)
 

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
usually also requires getting the cell's formatted value also.

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       常見問題

1)     如何同時顯示綁定數據和非綁定數據?

2)     How do I show data that comes from two tables?(TODO)

3)     如何顯示主從表?

4)     如何在同一DataGridView內顯示主從表?

5)     如何避免對一列的排序?

6)     如何針對多個列排序?

7)     How do I have a combo box column display a sub set of data based upon the value of a different combo box column?(TODO)

8)     如何在點擊工具欄按鈕的時候將數據提交到數據庫?

9)     如何在用戶刪除記錄時顯示確認對話框?

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.

  1. DataGridViewCell.Style
  2. DataGridViewRow.DefaultCellStyle
  3. AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers)
  4. RowsDefaultCellStyle
  5. DataGridViewColumn.DefaultCellStyle
  6. DefaultCellStyle

For row and column header cells, the InheritedStyle property is populated by values from the following list of source properties in the given order.

  1. DataGridViewCell.Style
  2. ColumnHeadersDefaultCellStyle or RowHeadersDefaultCellStyle
  3. DefaultCellStyle

The following diagram illustrates this process.

You can also access the styles inherited by specific rows and columns. The column InheritedStyle property inherits its values from the following properties.

  1. DataGridViewColumn.DefaultCellStyle
  2. DefaultCellStyle

The row InheritedStyle property inherits its values from the following properties.

  1. DataGridViewRow.DefaultCellStyle
  2. AlternatingRowsDefaultCellStyle (only for cells in rows with odd index numbers)
  3. RowsDefaultCellStyle
  4. DefaultCellStyle

For each property in a DataGridViewCellStyle object returned by an InheritedStyle property, the property value is obtained from the first cell style in the appropriate list that has the corresponding property set to a value other than the DataGridViewCellStyle class defaults.

The following table illustrates how the ForeColor property value for an example cell is inherited from its containing column.

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.

常見問題:

1)     如何調整最後一列的寬度使其佔據網格的剩餘客戶區?

5.3.1       Sizing Options in the Windows Forms DataGridView Control 

DataGridView rows, columns, and headers can change size as a result of many different occurrences. The following table shows these occurrences.

Occurrence

Description

User resize

Users can make size adjustments by dragging or double-clicking row, column, or header dividers.

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 the DataGridView 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 the DataSource 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 the DataSource property is set or the VirtualMode property is true.

Call the UpdateCellErrorText method or the UpdateRowErrorText method when you change the cell or row error text to ensure that the current value is displayed in the control.

Cell and row error glyphs are displayed when the ShowCellErrors and ShowRowErrors property values are true.

CellContextMenuStripNeeded

RowContextMenuStripNeeded

Used by the control to retrieve a cell or row ContextMenuStrip when the control DataSource property is set or the VirtualMode property is true.

RowHeightInfoNeeded

RowHeightInfoPushed

Used by the control to retrieve or store row height information in the data cache. Call the UpdateRowHeightInfo method when changing the cached row height information outside of a RowHeightInfoPushed event handler to ensure that the current value is used in the display of the control.

5.12.6    Best Practices in Virtual Mode

If you are implementing virtual mode in order to work efficiently with large amounts of data, you will also want to ensure that you are working efficiently with the DataGridView control itself. See below for more information on best practices

5.13    容量(Capacity)

In general, the DataGridView does not have any hard-coded capacity limits. The grid was designed so that more and more content can be added as machines become faster and have more memory. That said, the grid was not designed to deal with large number of columns. If you add more than 300 columns you will start to notice a degradation in performance as our performance tuning of the grid was not designed for this. If you need a grid with large amounts of columns then the DataGridView might not meet your needs. Regarding the number of rows supported, the DataGridView is bound by memory constraints. When using Virtual mode you can easily support over 2 million rows. Check out the best practices section below for information on things you can do (and not do) to improve memory usage and performance.

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

•AutoSizeRowsMo​​de

若要重寫控件的列大小的單個列模式,將其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(DataGridViewAutoSizeRowsMo​​de,布爾)的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的滾動條可以訪問,它通過保護Horizo​​ntalScrollBar和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單值單,SingleHorizo​​ntal和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控制自動調整大小。如果你使用自動大小調整,只調整的基礎上所顯示的行。在虛擬模式下只使用所顯示的行以及。

對行和列•,使用DataGridViewAutoSizeRowsMo​​de,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 in 3.5.1 section that a combo box cells value must be in the items collection or else a DataError event is raised. In this case, since all combo box cells in the column use the same datasource if you filter the datasource for one row then a combo box cell in another row might not have its value visible in the datasource, thus causing a DataError event.

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 article http://support.microsoft.com/default.aspx?scid=kb;en-us;325682. Using this class you can join two or more DataTables together. This JoinView can then be databound to the DataGridView.

22. 如何顯示主從表?

使用DataGridView時最常見的情況之一就是主從表單,這時要顯示具有主從關係的兩個數據表。在主表中選擇一行記錄,從表中也會隨之變化,顯示相應的記錄。

通過DataGridView控件和BindingSource 組件的交互作用來實現主從表單是非常簡單的。下面的示例演示的是SQL Server的範例數據庫Northwind 中的兩個表:Customers 和Orders。在主DataGridView中選擇一個顧客,那麼該顧客的所有訂單會顯示在從DataGridView 中。

using System;

using System.Data;

using System.Data.SqlClient;

using System.Windows.Forms;

public class Form1 : System.Windows.Forms.Form

{

    private DataGridView masterDataGridView = new DataGridView();

    private BindingSource masterBindingSource = new BindingSource();

    private DataGridView detailsDataGridView = new DataGridView();

    private BindingSource detailsBindingSource = new BindingSource();

    [STAThreadAttribute()]

    public static void Main()

    {

        Application.Run(new Form1());

    }

    // Initializes the form.

    public Form1()

    {

        masterDataGridView.Dock = DockStyle.Fill;

        detailsDataGridView.Dock = DockStyle.Fill;

        SplitContainer splitContainer1 = new SplitContainer();

        splitContainer1.Dock = DockStyle.Fill;

        splitContainer1.Orientation = Orientation.Horizontal;

        splitContainer1.Panel1.Controls.Add(masterDataGridView);

        splitContainer1.Panel2.Controls.Add(detailsDataGridView);

        this.Controls.Add(splitContainer1);

        this.Load += new System.EventHandler(Form1_Load);

        this.Text = "DataGridView master/detail demo";

    }

    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;

}

 

http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章