1. 程式人生 > >DataGridView 選中行、列、單元格

DataGridView 選中行、列、單元格

基本資料繫結

用 DataSource 屬性來繫結 DataTable 物件(或從 DataTable 派生的物件)。

Dim ds As DataSet = GetDataSet()
DataGridView1.DataSource = ds.Tables("Customers")

與 DataGrid 不同的是,DataGridView 一次只能顯示一個表。如果繫結整個 DataSet,則不會顯示任何資料,除非您使用要顯示的表名設定了 DataMember屬性。

DataGridView1.DataSource = ds
DataGridView1.DataMember = "Customers"

基本的 DataGridView 顯示遵循以下幾項簡單的規則:

為資料來源中的每個欄位建立一列。

使用欄位名稱建立列標題。列標題是固定的,這意味著使用者在列表中向下移動時列標題不會滾動出檢視。

支援 Windows XP 視覺樣式。您會注意到列標題具有新式的平面外觀,並且當用戶將滑鼠移到其上時會突出顯示。

DataGridView 還包括幾個您可能不會立即注意到的預設行為:

允許就地編輯。使用者可以在單元格中雙擊或按 F2 來修改當前值。唯一的例外是將 DataColumn.ReadOnly 設定為 True 的欄位(如當前示例中的 orderID 欄位)。

支援自動排序。使用者可以在列標題中單擊一次或兩次,基於該欄位中的值按升序或降序對值進行排序。預設情況下,排序時會考慮資料型別並按字母或數字順序進行排序。字母順序區分大小寫。

允許不同型別的選擇。使用者可以通過單擊並拖動來突出顯示一個單元格、多個單元格或多個行。單擊 DataGridView 左上角的方塊可以選擇整個表。

支援自動調整大小功能。使用者可以在標題之間的列分隔符上雙擊,使左邊的列自動按照單元格的內容展開或收縮。

美化 DataGridView

當列無法自動展開以適合其包含的資料。您可以使用DataGridView.AutoSizeColumns() 方法以及DataGridViewAutoSizeColumnCriteria 列舉中的某個值來解決此問題。您可以選擇根據標題文字、當前顯示的行或表中的所有行的的寬度來調整列寬。

' 根據標題或此列的某一行中
' 最長一段文字的寬度調整
' 列寬。
DataGridView1.AutoSizeColumns( _
DataGridViewAutoSizeColumnCriteria.HeaderAndRows)

請記住,此方法必須在繫結資料後呼叫,否則不會產生任何效果。你可能還需要在使用者編輯資料後使用它(可能在響應DataGridView.CellValueChanged 等事件時)。

如果不增加列寬,則可以更改行高。預設情況下,列中的文字會跨越多行。如果您使用DataGridView.AutoSizeRows() 方法,則行會根據其中的內容調整高度。使用此方法前,您可能希望增加列寬,尤其是在欄位包含大量文字時。例如,以下程式碼片段使“說明”列的列寬增加為原列寬的四倍,然後調整行高以容納其內容。

DataGridView.Columns("Description").Width *= 4
DataGridView.AutoSizeRows( _
DataGridViewAutoSizeRowsMode.HeaderAndColumnsAllRows)

另一個合理的更改是清理每一列中顯示的標題文字。例如,標題“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.SelectedRows
MessageBox.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 物件的集合)。

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

要選擇行、列或單元格,只需找到對應的 DataGridViewRow、DataGridViewColumn 或 DataGridViewCell 物件,並將 IsSelected 屬性設定為 True。

以下示例以程式設計方式選擇 orderID 欄位的值小於 100 的所有行:

For Each Row As DataGridViewRow In DataGridView1.Rows
If Row.Cells("OrderID").Value < 100 Then
Row.Selected = True
End If
Next

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

DataGridView 樣式

DataGridView 通過DataGridViewCellStyle 物件來實現多層模型。DataGridViewCellStyle 物件表示單元格的樣式,並且包括如顏色、字型、對齊、換行和資料格式等詳細資訊。您可以建立一個 DataGridViewCellStyle 來指定整個表的預設格式。此外,還可以指定列、行和各個單元格的預設格式。格式設定的越細緻、建立的 DataGridViewCellStyle 物件越多,該解決方案的可伸縮性也就越小。但是,如果您主要使用基於列和基於行的格式設定,並且只是偶爾設定各個單元格的格式,則與 DataGrid 相比,DataGridView 不需要太多記憶體。

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

1. DataGridViewCell.Style
2. DataGridViewRow.DefaultCellStyle
3. DataGridView.AlternatingRowsDefaultCellStyle
4. DataGridView.RowsDefaultCellStyle
5. DataGridViewColumn.DefaultCellStyle
6. 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.MiddleRight
DataGridView1.Columns("UnitCost"). _
DefaultCellStyle.BackColor = Color.LightYellow

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

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

自定義單元格格式

單元格格式設定的第一種方法是設定更高級別的 DataGridView、DataGridViewColumn和 DataGridViewRow 屬性。但是,有時您需要為特定單元格單獨設定樣式。例如,您可能需要在列中的資料大於或小於某個值時標記該列中的資料。在這兩種情況下,您需要對單獨的單元格進行格式設定。

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" Then
e.CellStyle.ForeColor = Color.Red
e.CellStyle.BackColor = Color.Yellow
End If
End If
End Sub

隱藏列、在不同位置之間移動列,並“凍結”列,使這些列在使用者滾動到右端時仍然可見。這些功能都是通過 DataGridViewColumn 類的屬性提供的,如下所述:

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

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

HeaderText:設定將在列標題中顯示的文字。

ResizableMinimumWidth:將 Resizable 設定為 False,以防止使用者調整列寬;或者將 MinimumWidth 設定為允許的最小畫素數目。

Visible:要隱藏列,請將此屬性設定為 False。

按鈕列

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

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

' 建立按鈕列。
Dim Details As New DataGridViewButtonColumn()
Details.Name = "Details"
' 關閉資料繫結並顯示靜態文字。
'(但是,您可以通過設定 DataPropertyName
' 屬性來使用該表中的屬性。)
Details.DisplayTextAsFormattedValue = False
Details.Text = "Details..."
' 清除標題。
Details.HeaderText = ""
' 新增該列。
DataGridView1.Columns.Insert( _
DataGridView1.Columns.Count, Details)

以下程式碼會對任何行中的按鈕單擊事件做出反應,並顯示相應的記錄資訊:

Private Sub DataGridView1_CellClick( _
ByVal sender As System.Object, _
ByVal e As System.Windows.Forms. _
DataGridViewCellEventArgs) _
Handles DataGridView1.CellClick
If DataGridView1.Columns(e.ColumnIndex).Name = _
"Details" Then
MessageBox.Show("You picked " & _
DataGridView1.Rows(e.RowIndex). _
Cells("CustomerID").Value)
End If
End Sub

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

影象列

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

使用 DataGridViewImageColumn 的方法有兩種。首先,您可以採用與 DataGridViewButtonColumn 相同的方式手動建立並新增該列。如果您需要顯示 DataSet 本身不提供的相關影象資訊,則此列非常有用。例如,您可能需要獲取檔名(如 ProductPic002.gif),從網路驅動器讀取相應的檔案,然後在網格中顯示該檔案。為完成此操作,您需要對 DataGridView 事件(如 CellFormatting)做出反應,以便讀取相應行的圖片、獲取影象資料並使用該列中的 Value 屬性將其插入。

如果 DataSet 包含不需要任何手動操作的二進位制圖片資料,那麼事情會變得更加簡單。例如 SQL Server Pubs 資料庫中的 pub_info 表,該表包含公司徽標。繫結至此表時,您不需要執行任何額外步驟,DataGridView 將自動識別出您正在使用影象,並會建立所需的DataGridViewImageColumn。

編輯 DataGridView

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.CellValidating
If DataGridView1.Columns(e.ColumnIndex).Name = _
"CompanyName" Then
If CType(e.FormattedValue, String).Length > _
50 Then
DataGridView1.Rows( _
e.RowIndex).Cells(e.ColumnIndex). _
ErrorText = _
"The company name is too long."
End If
End If
End Sub

使用列表列約束選擇

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

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

此表中的每一條記錄都通過其 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 = 0
List.HeaderText = "Category"
' 此列繫結至
' Products.CategoryID 欄位。
List.DataPropertyName = "CategoryID"
' 該列表將從 Categories 表獲得資料。
List.DataSource = ds.Tables("Categories")
List.DisplayMember = "CategoryName"
List.ValueMember = "CategoryID"
' 新增該列。
DataGridView1.Columns.Add(List)