DataGridView中数据输入验证

把一个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
posted @ 2012-04-10 16:36  Serviceboy  阅读(4034)  评论(0编辑  收藏  举报