DataGridView

文章來源:microsoft 文章作者:Matthew MacDonald 發佈時間:2006-04-17   

 

新的 DataGridView 是 .NET 2.0 中的一個新控件,是針對 .NET 1.x 中功能較差的標準 DataGrid 控件而設計的。Matthew MacDonald 在本文中論述了許多改進,包括:DataGridView 支持大量自定義和細緻的格式設置、靈活的大小調整和選擇、更好的性能以及更豐富的事件模型。

.NET Framework 的前兩個版本(.NET 1.0 和 .NET 1.1)在數據綁定方面留下了明顯的空白。儘管開發人員擁有一個靈活的可配置模型,可以將幾乎所有的控件鏈接到幾乎所有的數據源,但卻沒有一種有效的方法來顯示完整的信息表。可以實現此目的的唯一工具就是 DataGrid 控件,這種控件非常適合於處理簡單的演示,但是不太適合實際代碼。

填補這項空白是 .NET 2.0 的主要目標之一,並且 Microsoft 已經用全新的網格控件 DataGridView 來實現此目標。DataGridView 有兩個指導原則。首先,其目標是支持常見任務(如主控/詳細列表、驗證和數據格式設置),而不需要您編寫許多代碼。更重要的是,設計過程中始終考慮了擴展性,因此您可以集成所需的專用功能,而不必採用低級別的複雜編程。

本頁內容

基本數據綁定
美化 DataGridView
使用 DataGridView 選擇單元格
DataGridView 對象
DataGridView 樣式
自定義單元格格式
按鈕列
圖像列
編輯 DataGridView
處理錯誤
驗證輸入
使用列表列約束選擇
 
圖 1 比較了自動調整 DataGridView 大小的各種方法。

另一個合理的更改是清理每一列中顯示的標題文本。例如,標題“Order Date”比字段名稱“OrderDate”看上去更爲專業。這項更改很容易進行。您只需從 DataGridView.Columns 集合中檢索相應的 DataGridViewColumn,並修改其 HeaderText 屬性:

DataGridView.Columns("OrderID").HeaderText = "Order ID"

使用 DataGridView 選擇單元格

默認情況下,DataGridView 允許自由選擇。用戶可以突出顯示單元格或單元格組,可以一次突出顯示所有單元格(通過單擊網格右上角的方塊),還可以突出顯示一行或多行(通過在行標題列中單擊)。根據選擇模式,用戶甚至能夠通過選擇列標題來選擇一列或多列。通過使用 DataGridViewSelectionMode 枚舉中的某個值來設置 DataGridView.SelectionMode 屬性,可以控制此行爲,如下所述:

CellSelect:用戶可以選擇單元格,但不能選擇整個行或標題。如果 DataGridView.MultiSelect 爲 True,則用戶可以選擇多個單元格。

FullColumnSelect:單擊列標題只能選擇整個列。如果 DataGridView.MultiSelect 爲 True,則用戶可以選擇多個列。使用此模式時,單擊列標題不會對網格進行排序。

FullRowSelect:單擊行標題只能選擇整個行。如果 DataGridView.MultiSelect 爲 True,則用戶可以選擇多個行。

ColumnHeaderSelect:用戶可以使用 CellSelect 或 FullColumnSelect 選擇模式。使用此模式時,單擊列標題不會對網格進行排序。

RowHeaderSelect:用戶可以使用 CellSelect 或 FullRowSelect 選擇模式。這是默認的選擇模式。

通過 DataGridView,可以使用以下三個屬性方便地檢索選定的單元格:SelectedCells、SelectedRows 和 SelectedColumns。無論使用的是哪種選擇模式,SelectedCells 都始終返回 DataGridViewCell 對象的集合。另一方面,如果使用行標題選擇了整個行,則 SelectedRows 只返回信息,而如果使用列標題選擇了整個列,則 SelectedColumns 也只返回信息。

例如,以下代碼片段將檢查選定的整個行。只要找到一行,它就會在消息框中顯示 CustomerID 列中的相應值:

For Each SelectedRow As DataGridViewRow In _DataGridView1.SelectedRowsMessageBox.Show( _SelectedRow.Cells("CustomerID").Value)Next

使用 CurrentCell 或 CurrentCellAddress 屬性檢索對當前單元格的引用也同樣簡單。使用 DataGridView 時,您會注意到當前單元格被一個矩形圍住,看起來像是一個用黑色虛線繪製的方框。這就是用戶當前所在的位置。

CurrentCellAddress 屬性是隻讀的,但是您可以使用 CurrentCell 以編程方式更改當前位置。完成此操作後,DataGridView 將被滾動以使當前位置可見。

' 移至第十一行的第四個單元格。DataGridView.CurrentCell = _DataGridView.Rows(10).Cells(3)

DataGridView 對象

到目前爲止,您已經瞭解瞭如何與當前選定的一組行、單元格和列進行交互。DataGridView 提供了兩個關鍵集合,用於處理整個數據集。這兩個集合分別是 Columns(DataGridViewColumn 對象的集合)和 Rows(DataGridViewRow 對象的集合,每個對象都引用一組 DataGridViewCell 對象的集合)。圖 2顯示了對象模型。

一般而言,您將使用 DataGridViewColumn 對象來配置列顯示屬性、格式設置及標題文本,而使用 DataGridViewRow 和 DataGridViewCell 對象從綁定的記錄中檢索實際數據。在 DataGridViewCell 中修改數據時,處理方式與用戶編輯的方式相同:引發相應的 DataGridView 更改事件,並且修改底層的 DataTable。

現在您已經瞭解了 DataGridView 對象模型,因此可以輕鬆地創建遍歷該表的代碼。要選擇行、列或單元格,只需找到對應的 DataGridViewRow、DataGridViewColumn 或 DataGridViewCell 對象,並將 IsSelected 屬性設置爲 True。

以下示例以編程方式選擇 OrderID 字段的值小於 100 的所有行:

For Each Row As DataGridViewRow In DataGridView1.RowsIf Row.Cells("OrderID").Value < 100 ThenRow.Selected = TrueEnd IfNext

值得一提的是,有幾個從 DataGridViewColumn 派生的不同的類。這些類可以控制在單元格中顯示和編輯值的方式。.NET 包括五個預先創建的 DataGridView 列類:DataGridViewButtonColumn、DataGridViewCheckBoxColumn、DataGridViewComboBoxColumn、DataGridViewImageColumn 和 DataGridViewTextBoxColumn。

DataGridView 樣式

設計 DataGridView 時面臨的挑戰之一就是創建一個格式設置系統,該系統要能夠足夠靈活地應用不同級別的格式設置,而對於非常大的表又要保持高效。從靈活性角度來看,最好的方法是允許開發人員分別配置每個單元格。但是從效率角度來看,這種方法可能是有害的。包含數千行的表中具有好幾萬個單元格,維護每個單元格的不同格式肯定會浪費很多內存。

爲解決此問題,DataGridView 通過 DataGridViewCellStyle 對象來實現多層模型。DataGridViewCellStyle 對象表示單元格的樣式,並且包括如顏色、字體、對齊、換行和數據格式等詳細信息。您可以創建一個 DataGridViewCellStyle 來指定整個表的默認格式。此外,還可以指定列、行和各個單元格的默認格式。格式設置的越細緻、創建的 DataGridViewCellStyle 對象越多,該解決方案的可伸縮性也就越小。但是,如果您主要使用基於列和基於行的格式設置,並且只是偶爾設置各個單元格的格式,則與 DataGrid 相比,DataGridView 不需要太多內存。

DataGridView 應用格式設置時,將遵循以下優先順序(從最高到最低):

1. DataGridViewCell.Style 2. DataGridViewRow.DefaultCellStyle 3. DataGridView.AlternatingRowsDefaultCellStyle 4. DataGridView.RowsDefaultCellStyle 5. DataGridViewColumn.DefaultCellStyle6. DataGridView.DefaultCellStyle

重要的是要清楚:樣式對象不是以“全有/全無”的方式應用的,DataGridView 會檢查每個屬性。例如,假設您的單元格使用 DataGridViewCell.Style 屬性來應用自定義字體,但沒有設置自定義前景色。結果,該字體設置將覆蓋任何其他樣式對象中的字體信息,但如果層次結構中下一個樣式對象的前景色不爲空,則將從該對象繼承前景色(在這種情況下爲 DataGridViewRow.DefaultCellStyle)。

DataGridViewCellStyle 定義了兩種格式設置:數據和外觀。數據格式設置描述顯示數據綁定值之前如何對其進行修改。這種格式設置通常包括使用格式設置字符串將數字或日期值轉換爲文本。要使用數據格式設置,只需使用 DataGridViewCellStyle.Format 屬性設置格式定義或自定義格式字符串即可。

例如,以下代碼片段對 UnitCost 字段中的所有數字進行格式設置,以將它們顯示爲貨幣值,保留兩位小數並加上在區域設置中定義的相應貨幣符號:

DataGridView1.Columns("UnitCost"). _DefaultCellStyle.Format = "C"

外觀格式設置包括顏色和格式等表面細節。例如,以下代碼右對齊 UnitCost 字段、應用粗體並將單元格的背景更改爲黃色:

DataGridView1.Columns("UnitCost"). _DefaultCellStyle.Font = _New Font(DataGridView.Font, FontStyle.Bold)DataGridView1.Columns("UnitCost"). _DefaultCellStyle.Alignment = _DataGridViewContentAlignment.MiddleRightDataGridView1.Columns("UnitCost"). _DefaultCellStyle.BackColor = Color.LightYellow

其他與格式設置相關的屬性包括 ForeColor、SelectionForeColor、SelectionBackColor、WrapMode(控制文本在空間允許時是跨越多行還是直接截斷)及 NullValue(將替代 Null 值的值)。

DataGridView 還包括一個設計器,用於在設計時配置列樣式。只需從“Properties”(屬性)窗口中選擇“DataGridView Properties”(DataGridView 屬性)鏈接,或者從各種預先創建的樣式設置中選擇“AutoFormat”(自動套用格式)。

自定義單元格格式

單元格格式設置的第一種方法是設置更高級別的 DataGridView、DataGridViewColumn 和 DataGridViewRow 屬性。但是,有時您需要爲特定單元格單獨設置樣式。例如,您可能需要在列中的數據大於或小於某個值時標記該列中的數據。例如,突出顯示項目計劃列表中已過去的到期日期,或者在銷售分析中突出顯示負收益率。在這兩種情況下,您需要對單獨的單元格進行格式設置。

瞭解 DataGridView 對象模型後,您可能想要遍歷特定列中的單元格集合,以尋找要突出顯示的值。這種方法是可行的,但不是最好的方法。關鍵問題是如果用戶編輯了數據,或者如果代碼更改了綁定的數據源,不會對單元格的突出顯示情況進行相應的更新。

幸運的是,DataGridView 針對此目的提供了 CellFormatting 事件。CellFormatting 只在顯示單元格值之前引發。通過該事件,可以基於單元格的內容來更新單元格樣式。以下示例檢查特定的客戶並相應地標記單元格:

Private Sub DataGridView1_CellFormatting( _ByVal sender As System.Object, _ByVal e As System.Windows.Forms. _DataGridViewCellFormattingEventArgs) _Handles DataGridView1.CellFormatting' 檢查該列是否正確。If DataGridView1.Columns(e.ColumnIndex).Name = _"CustomerID" Then' 檢查該值是否正確。If e.Value = "ALFKI" Thene.CellStyle.ForeColor = Color.Rede.CellStyle.BackColor = Color.YellowEnd IfEnd IfEnd Sub

樣式不是影響網格外觀的唯一細節。您還可以隱藏列、在不同位置之間移動列,並“凍結”列,使這些列在用戶滾動到右端時仍然可見。這些功能都是通過 DataGridViewColumn 類的屬性提供的,如下所述:

DisplayIndex:設置列在 DataGridView 中顯示的位置。例如,DisplayIndex 值爲 0 的列將自動顯示在最左邊的列中。如果多個列具有相同的 DisplayIndex,則先顯示最先出現在該集合中的列。因此,如果您使用 DisplayIndex 將一列向左移動,則可能還需要設置最左邊的列的 DisplayIndex,以將其向右移動。最初,DisplayIndex 與 DataGridView.Columns 集合中 DataGridViewColumn 對象的索引相匹配。

Frozen:如果爲 True,則該列將始終可見並且固定在表的左側,即使用戶爲查看其他列而滾動到右側亦如此。

HeaderText:設置將在列標題中顯示的文本。

Resizable 和 MinimumWidth:將 Resizable 設置爲 False,以防止用戶調整列寬;或者將 MinimumWidth 設置爲允許的最小像素數目。

Visible:要隱藏列,請將此屬性設置爲 False。

按鈕列

DataGridView 提供的一種列是 DataGridViewButtonColumn,這種列在每一項旁邊顯示一個按鈕。您可以響應此按鈕的單擊事件,並使用它啓動其他操作或顯示新的表單。

以下示例使用按鈕文字“Details...”創建簡單的按鈕列:

' 創建按鈕列。Dim Details As New DataGridViewButtonColumn()Details.Name = "Details"' 關閉數據綁定並顯示靜態文本。'(但是,您可以通過設置 DataPropertyName ' 屬性來使用該表中的屬性。)Details.DisplayTextAsFormattedValue = FalseDetails.Text = "Details..."' 清除標題。Details.HeaderText = ""' 添加該列。DataGridView1.Columns.Insert( _DataGridView1.Columns.Count, Details)

圖 3 顯示了包含新列的網格。以下代碼會對任何行中的按鈕單擊事件做出反應,並顯示相應的記錄信息:

Private Sub DataGridView1_CellClick( _ByVal sender As System.Object, _ByVal e As System.Windows.Forms. _DataGridViewCellEventArgs) _Handles DataGridView1.CellClickIf DataGridView1.Columns(e.ColumnIndex).Name = _"Details" ThenMessageBox.Show("You picked " & _DataGridView1.Rows(e.RowIndex). _Cells("CustomerID").Value)End IfEnd Sub

比較現實的方案是,在此時創建並顯示一個新窗口,並將有關選定記錄的信息傳遞到這個新窗口,以便查詢並顯示完整的信息。

圖像列

DataGridView 提供的另一種列是 DataGridViewImageColumn,這種列將在單元格邊框內顯示一個圖片。您可以設置 DataGridViewImageColumn.Layout 屬性以便配置圖片在單元格中的顯示方式:是將單元格擴展到適當的大小,還是將直接剪裁太大的圖像。

使用 DataGridViewImageColumn 的方法有兩種。首先,您可以採用與 DataGridViewButtonColumn 相同的方式手動創建並添加該列。如果您需要顯示 DataSet 本身不提供的相關圖像信息,則此列非常有用。例如,您可能需要獲取文件名(如 ProductPic002.gif),從網絡驅動器讀取相應的文件,然後在網格中顯示該文件。爲完成此操作,您需要對 DataGridView 事件(如 CellFormatting)做出反應,以便讀取相應行的圖片、獲取圖像數據並使用該列中的 Value 屬性將其插入。

如果 DataSet 包含不需要任何手動操作的二進制圖片數據,那麼事情會變得更加簡單。例如 SQL Server Pubs 數據庫中的 pub_info 表,該表包含公司徽標。綁定至此表時,您不需要執行任何額外步驟,DataGridView 將自動識別出您正在使用圖像,並會創建所需的 DataGridViewImageColumn。(有關這項技術的示例,請參閱本文的下載內容。)

編輯 DataGridView

衆所周知,DataGrid 的用戶輸入功能很不靈活,您幾乎沒有辦法自定義單元格驗證方式及錯誤報告方式。而另一方面,DataGridView 允許您通過對在編輯過程的所有階段中引發的大量不同事件做出反應來控制其行爲。

默認情況下,當用戶用鼠標雙擊單元格或按 F2 鍵時,DataGridView 單元格將進入編輯模式。您還可以通過將 DataGridView.EditCellOnEnter 屬性設置爲 True,對 DataGridView 進行配置,以便當用戶移到該單元格後,該單元格立即切換到編輯模式。您還可以使用 DataGridView 的 BeginEdit()、CancelEdit()、CommitEdit() 和 EndEdit() 方法通過編程方式開始和停止單元格編輯。用戶編輯單元格時,行標題將顯示一個鉛筆狀的編輯圖標。

用戶可以通過按 Esc 鍵來取消編輯。如果將 EditCellOnEnter 屬性設置爲 True,則單元格仍將處於編輯模式,但是所有更改都將被放棄。要提交更改,用戶只需移到新的單元格,或將焦點切換到其他控件。如果您的代碼可以移動當前單元格的位置,則此代碼也會提交更改。

爲防止單元格被編輯,可以設置 DataGridViewCell、DataGridViewColumn、DataGridViewRow 或 DataGridView 的 ReadOnly 屬性(取決於您是要防止對該單元格進行更改、對該列中的所有單元格進行更改、對該行中的所有單元格進行更改,還是要防止對該表中的所有單元格進行更改)。DataGridView 還提供了 CellBeginEdit 事件,用於取消嘗試的編輯。

處理錯誤

默認情況下,DataGridViewTextBoxColumn 允許用戶輸入任何字符,包括當前單元格中可能不允許使用的那些字符。例如,用戶可以在數字字段中鍵入非數字字符,也可以指定與 DataSet 中定義的 ForeignKeyConstraint 或 UniqueConstraint 衝突的值。DataGridView 採用不同的方式來處理這些問題:

如果可以將編輯的值轉換爲所需的數據類型(例如,用戶在數字列中鍵入了文本),則用戶將不能提交更改或導航到其他行。而必須取消更改或編輯值。

如果編輯的值與 DataSet 中的約束衝突,則用戶通過導航到其他行或按 Enter 鍵提交更改後,更改將被立即取消。

這些處理方式適用於大多數情況。但是,如果需要,您也可以通過響應 DataGridView.DataError 事件來參與錯誤處理,該事件是在 DataGridView 偵聽到來自數據源的錯誤時引發的。

驗證輸入

驗證是一項與錯誤處理稍有不同的任務。通過錯誤處理,您可以處理由 DataSet 報告的問題。而通過驗證,您可以捕獲您自己定義的錯誤情況,例如 DataSet 中允許的數據在應用程序中卻沒有意義。

當用戶通過導航到新的單元格提交更改時,DataGridView 控件將引發 CellValidating 和 CellValidated 事件。這些事件之後是 RowValidating 和 RowValidated 事件。您可以響應這些事件,檢查用戶輸入的值是否正確,並執行所需的任何後期處理。如果值無效,通常您會通過兩種方式來做出響應,即取消更改和單元格導航(通過將 EventArgs 對象的 Cancel 屬性設置爲 True),或者設置某種錯誤文本來提醒用戶。可以將錯誤文本置於其他控件中,也可以使用相應的 DataGridViewRow 和 DataGridViewCell 的 ErrorText 屬性在 DataGrid 中顯示錯誤文本:

設置 DataGridViewCell.ErrorText 時,單元格中將顯示感嘆號圖標。將鼠標懸停在此圖標上將顯示錯誤消息。

設置 DataGridViewRow.ErrorText 時,行左側的行標題中將顯示感嘆號圖標。將鼠標懸停在此圖標上將顯示錯誤消息。

通常,您會結合使用這兩種屬性,並設置行和單元格中的錯誤消息。以下示例檢查 CompanyName 字段中太長的文本輸入。如果發現有問題的值,則會將錯誤符號(紅色的感嘆號)添加到單元格中,並顯示描述該問題的工具提示文本。

Private Sub DataGridView1_CellValidating( _ByVal sender As System.Object, _ByVal e As System.Windows.Forms. _DataGridViewCellValidatingEventArgs) _Handles DataGridView1.CellValidatingIf DataGridView1.Columns(e.ColumnIndex).Name = _"CompanyName" ThenIf CType(e.FormattedValue, String).Length > _50 ThenDataGridView1.Rows( _e.RowIndex).Cells(e.ColumnIndex). _ErrorText = _"The company name is too long."End IfEnd IfEnd Sub

使用列表列約束選擇

使用驗證可以捕獲任何錯誤情況。但是,這種方法不一定是最好的,因爲它允許用戶輸入無效的內容,然後在事實出現後嘗試改正無效輸入。更好的解決方案是使用戶在一開始就無法輸入任何無效的內容。

一個常見例子就是您需要將列限制在預定義值列表的範圍內。在此示例中,對於用戶而言,最簡單的辦法是從列表中選擇正確的值,而不要手動鍵入值。最大的優勢在於,您可以使用 DataGridViewComboBoxColumn 非常方便地實現此設計。

可以使用 Items 集合手動爲 DataGridViewComboBoxColumn 添加項列表,就像使用 ListBox 一樣。此外,還可以將 DataGridViewComboBoxColumn 綁定到其他數據源。在這種情況下,您可以使用 DataSource 屬性來指定數據源,並使用 DisplayMember 屬性指示列中應顯示的值,以及使用 ValueMember 屬性指定底層列值應使用的值。

爲了演示這種情況,來看看下一個有關 Products 表的示例。此表中的每一條記錄都通過其 CategoryID 字段鏈接至 Categories 表中的記錄。要更改產品類別,用戶需要記住正確的 ID,並將其輸入到 CategoryID 字段中。更好的解決方案是使用與 Categories 表綁定的 DataGridViewComboBoxColumn。此列將使用 CategoryName 作爲顯示值,但是會將 CategoryID 作爲真正的底層值。此列仍將通過 DataPropertyName 屬性與 Products 表綁定在一起,這意味着當用戶從列表中選擇新的 Category 時,產品記錄的 CategoryID 字段將自動更改。

以下是配置此表所需的代碼:

' 刪除自動生成的 CategoryID 列。DataGridView1.Columns.Remove("CategoryID")' 爲 CategoryID 創建列表列。Dim List As New DataGridViewComboBoxColumn()List.DisplayIndex = 0List.HeaderText = "Category"' 此列綁定至' Products.CategoryID 字段。List.DataPropertyName = "CategoryID"' 該列表將從 Categories 表獲得數據。List.DataSource = ds.Tables("Categories")List.DisplayMember = "CategoryName"List.ValueMember = "CategoryID"' 添加該列。DataGridView1.Columns.Add(List)

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