把一個DataTable或者某個object集合的數據源綁定到DataGridView中,直接通過DataGridView數據錄入需要進行額外的數據驗證。數據驗證總共分成兩個大類:
I)類型驗證:
所謂“類型驗證”,就是輸入的數據是不是可以(有效)轉化成特定的類型(比如字段是int,但是輸入確實abc等),這明顯不符合要求。默認情況下,直接在綁定的DataGridView輸入不符合類型的數據,當切換到下一條信息或者調用EndEdit方法將拋出異常。這很難看,我們可以通過處理DataError來實現強制性輸入正確類型,代碼非常簡單:
[C#]
namespace WinFormCSharp { public partial class Form1 : Form { DataTable dt = new DataTable(); DataGridView dv = new DataGridView(); public Form1() { InitializeComponent(); dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Name", typeof(string)); } private void Form1_Load(object sender, EventArgs e) { for (int i = 1; i < 11; i++) { dt.Rows.Add(i, "Name" + i); } dt.AcceptChanges(); dv.Parent = this; dv.Dock = DockStyle.Fill; dv.DataSource = dt; //自定義出錯事件處理,到DataError這邊處理 dv.DataError += new DataGridViewDataErrorEventHandler(dv_DataError); } void dv_DataError(object sender, DataGridViewDataErrorEventArgs e) { e.ThrowException = false; } } }
[VB.NET]
Namespace WinFormCSharp Public Partial Class Form1 Inherits Form Private dt As New DataTable() Private dv As New DataGridView() Public Sub New() InitializeComponent() dt.Columns.Add("Id", GetType(Integer)) dt.Columns.Add("Name", GetType(String)) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) For i As Integer = 1 To 10 dt.Rows.Add(i, "Name" & i) Next dt.AcceptChanges() dv.Parent = Me dv.Dock = DockStyle.Fill dv.DataSource = dt '自定義出錯事件處理,到DataError這邊處理 dv.DataError += New DataGridViewDataErrorEventHandler(AddressOf dv_DataError) End Sub Private Sub dv_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) e.ThrowException = False End Sub End Class End Namespace
注意:DataError事件通常在調用EndEdit(DataGridView編輯完一行,開始下一行默認調用此事件)之后被觸發,此時如果輸入數據無法轉換到列需要的那個類型就會觸發DataError事件。因此你處理這個事件(通過把ThrowException=false)之后,異常框不會彈出,而是光標永久定格在那個單元格中(其余操作都無法做),直到你改變了數據符合規定的類型為止。
II)自定義驗證:
所謂“自定義驗證”就是說錄入數據在完全符合類型轉換的條件下檢查一些數據類型的范圍。我們仍舊拿上面的舉例子——假設規定Id范圍只能在1~100之間,如何處理呢?
你可以處理CellValidating事件。該事件可以通過一個e.FormattedValue獲取當前單元格內容,以便你對其進行操作。現在我們先假定排除第I種錯誤,純粹是第二種,那么你應該在原來的基礎上添加如下粗體部分的代碼(Button是模擬保存數據到內存數據表中,順便用於測試范圍驗證是否成功):
[C#]
namespace WinFormCSharp { public partial class Form1 : Form { DataTable dt = new DataTable(); DataGridView dv = new DataGridView(); Button btn = new Button(); public Form1() { InitializeComponent(); dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Name", typeof(string)); } private void Form1_Load(object sender, EventArgs e) { for (int i = 1; i < 11; i++) { dt.Rows.Add(i, "Name" + i); } dt.AcceptChanges(); dv.Parent = this; dv.Dock = DockStyle.Fill; dv.DataSource = dt; //添加一個保存到DataTable中的按鈕 btn.Parent = this; btn.Dock = DockStyle.Bottom; btn.Text = "Save To DataTable"; //當不滿足條件,該字段為錯誤 dv.ShowCellErrors = true; //處理單元格驗證事件 dv.CellValidating += dv_CellValidating; //處理按鈕事件 btn.Click += new EventHandler(btn_Click); } void btn_Click(object sender, EventArgs e) { bool flag=true; //默認沒有錯誤 //循環遍歷檢測是否有錯誤 dv.Rows.Cast<DataGridViewRow>().ToList().ForEach(row=>{ if(row.Cells[0].ErrorText!="") { flag=false; //有錯誤 } } ); if (flag) { dt.AcceptChanges(); } else { MessageBox.Show("注意,你的單元格中仍舊有錯誤!"); } } void dv_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { //如果是第一個字段 if (e.ColumnIndex == 0) { //先轉化成int類型 int n = int.Parse(e.FormattedValue.ToString()); //范圍判斷 if (n < 1 || n > 100) { dv.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "必須在1~100之內!"; } else { dv.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = ""; } } } void dv_DataError(object sender, DataGridViewDataErrorEventArgs e) { e.ThrowException = false; } } }
[VB.NET]
Namespace WinFormCSharp Public Partial Class Form1 Inherits Form Private dt As New DataTable() Private dv As New DataGridView() Private btn As New Button() Public Sub New() InitializeComponent() dt.Columns.Add("Id", GetType(Integer)) dt.Columns.Add("Name", GetType(String)) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) For i As Integer = 1 To 10 dt.Rows.Add(i, "Name" & i) Next dt.AcceptChanges() dv.Parent = Me dv.Dock = DockStyle.Fill dv.DataSource = dt '添加一個保存到DataTable中的按鈕 btn.Parent = Me btn.Dock = DockStyle.Bottom btn.Text = "Save To DataTable" '當不滿足條件,該字段為錯誤 dv.ShowCellErrors = True '處理單元格驗證事件 AddHandler dv.CellValidating, AddressOf dv_CellValidating '處理按鈕事件 AddHandler btn.Click, AddressOf btn_Click End Sub Private Sub btn_Click(sender As Object, e As EventArgs) Dim flag As Boolean = True '默認沒有錯誤 '循環遍歷檢測是否有錯誤 dv.Rows.Cast(Of DataGridViewRow)().ToList().ForEach(Function(row) If row.Cells(0).ErrorText <> "" Then '有錯誤 flag = False End If End Function) If flag Then dt.AcceptChanges() Else MessageBox.Show("注意,你的單元格中仍舊有錯誤!") End If End Sub Private Sub dv_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) '如果是第一個字段 If e.ColumnIndex = 0 Then '先轉化成int類型 Dim n As Integer = Integer.Parse(e.FormattedValue.ToString()) '范圍判斷 If n < 1 OrElse n > 100 Then dv.Rows(e.RowIndex).Cells(e.ColumnIndex).ErrorText = "必須在1~100之內!" Else dv.Rows(e.RowIndex).Cells(e.ColumnIndex).ErrorText = "" End If End If End Sub Private Sub dv_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) e.ThrowException = False End Sub End Class End Namespace
由於DataGridView似乎沒有公共的方法或屬性直接判斷有沒有錯誤的單元格,所以按鈕事件用擴展方法循環遍歷每個行的第一列字段。
這樣,類型和有效性驗證都做到了!
不過我們可以精簡代碼(只用CellValidating事件一次性完成,因為該事件優先於DataError發生),通過設置其Cancel屬性可以控制該事件是否被取消(“取消”會讓光標定格在本單元格內,無法處理其它事情)。
[C#]
namespace WinFormCSharp { public partial class Form1 : Form { DataTable dt = new DataTable(); DataGridView dv = new DataGridView(); Button btn = new Button(); public Form1() { InitializeComponent(); dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("Name", typeof(string)); } private void Form1_Load(object sender, EventArgs e) { for (int i = 1; i < 11; i++) { dt.Rows.Add(i, "Name" + i); } dt.AcceptChanges(); dv.Parent = this; dv.Dock = DockStyle.Fill; dv.DataSource = dt; //添加一個保存到DataTable中的按鈕 btn.Parent = this; btn.Dock = DockStyle.Bottom; btn.Text = "Save To DataTable"; //當不滿足條件,該字段為錯誤 dv.ShowCellErrors = true; //處理單元格驗證事件 dv.CellValidating += dv_CellValidating; } void btn_Click(object sender, EventArgs e) { bool flag=true; //默認沒有錯誤 //循環遍歷檢測是否有錯誤 dv.Rows.Cast<DataGridViewRow>().ToList().ForEach(row=>{ if(row.Cells[0].ErrorText!="") { flag=false; //有錯誤 } } ); if (flag) { dt.AcceptChanges(); } else { MessageBox.Show("注意,你的單元格中仍舊有錯誤!"); } } void dv_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) { //如果是第一個字段 if (e.ColumnIndex == 0) { int n = 0; //先轉化成int類型,嘗試轉化 if (int.TryParse(e.FormattedValue.ToString(), out n)) { //可以的話,再進一步范圍判斷 if (n < 1 || n > 100) { dv.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "必須在1~100之內!"; } else { dv.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = ""; } } else { //轉換失敗,類型都有問題 e.Cancel = true; } } } } }
[VB.NET]
Namespace WinFormCSharp Public Partial Class Form1 Inherits Form Private dt As New DataTable() Private dv As New DataGridView() Private btn As New Button() Public Sub New() InitializeComponent() dt.Columns.Add("Id", GetType(Integer)) dt.Columns.Add("Name", GetType(String)) End Sub Private Sub Form1_Load(sender As Object, e As EventArgs) For i As Integer = 1 To 10 dt.Rows.Add(i, "Name" & i) Next dt.AcceptChanges() dv.Parent = Me dv.Dock = DockStyle.Fill dv.DataSource = dt '添加一個保存到DataTable中的按鈕 btn.Parent = Me btn.Dock = DockStyle.Bottom btn.Text = "Save To DataTable" '當不滿足條件,該字段為錯誤 dv.ShowCellErrors = True '處理單元格驗證事件 AddHandler dv.CellValidating, AddressOf dv_CellValidating End Sub Private Sub btn_Click(sender As Object, e As EventArgs) Dim flag As Boolean = True '默認沒有錯誤 '循環遍歷檢測是否有錯誤 dv.Rows.Cast(Of DataGridViewRow)().ToList().ForEach(Function(row) If row.Cells(0).ErrorText <> "" Then '有錯誤 flag = False End If End Function) If flag Then dt.AcceptChanges() Else MessageBox.Show("注意,你的單元格中仍舊有錯誤!") End If End Sub Private Sub dv_CellValidating(sender As Object, e As DataGridViewCellValidatingEventArgs) '如果是第一個字段 If e.ColumnIndex = 0 Then Dim n As Integer = 0 '先轉化成int類型,嘗試轉化 If Integer.TryParse(e.FormattedValue.ToString(), n) Then '可以的話,再進一步范圍判斷 If n < 1 OrElse n > 100 Then dv.Rows(e.RowIndex).Cells(e.ColumnIndex).ErrorText = "必須在1~100之內!" Else dv.Rows(e.RowIndex).Cells(e.ColumnIndex).ErrorText = "" End If Else '轉換失敗,類型都有問題 e.Cancel = True End If End If End Sub End Class End Namespace
