none
DataGridViewのRowValiating後のフォーカス移動等でNoNullAllowedExceptionが発生 RRS feed

  • 質問

  • DataGridViewのRowValiating後のフォーカス移動等でNoNullAllowedExceptionが発生します。対処方法をご教授ください。

    再現方法を示します。

    Accessのmdb(確認用の簡単なもの)からDataSetを作っています。

    そして、そのDataSetの一つの列にAllowDBNullをFalse(NULL不可)にします。そのDataSetにBindしたDataGridViewを作り、空の状態で(NULL不可)の列が空の状態で他の列に値を入力します。RowValiating後にDelで行削除し、フォーカス移動するとNoNullAllowedExceptionが発生します。

    箇条書きにすると次の流れです。

    (1)空のテーブルを用意

    (2)エラーとなる値(行)入力

    (3)RowValidating→Cancel

    (4)Delキーで行削除

    (5)別コントロールへフォーカス移動

    (6)DataGridViewへフォーカスを戻す

    (7)NoNullAllowedException発生

    コードを以下に示します。

            private void tableABindingNavigatorSaveItem_Click(object sender, EventArgs e)
            {
                this.Validate();
                this.tableABindingSource.EndEdit();
                this.tableAdapterManager.UpdateAll(this.testDataSet);
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                this.tableATableAdapter.Fill(this.testDataSet.TableA);
            }
    
            private void tableADataGridView_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
            {
                DataGridView dgv = (DataGridView)sender;
    
                //変更行だけ検証する
                if (!(dgv.IsCurrentRowDirty))
                {
                    return;
                }
    
                //行のセルが空かを調べる
                foreach (DataColumn col in testDataSet.TableA.Columns)
                {
                    if (col.AllowDBNull)
                    {
                        continue;
                    }
                    foreach (DataGridViewCell cell in dgv.Rows[e.RowIndex].Cells)
                    {
                        if (cell.OwningColumn.DataPropertyName == col.ColumnName)
                        {
                            if (cell.Value == null || cell.Value.ToString() == "")
                            {
                                //行にエラーテキストを設定
                                dgv.Rows[e.RowIndex].ErrorText =
                                    "'" + cell.OwningColumn.HeaderText + "'に値を入力してください。";
                                e.Cancel = true;
                                return;
                            }
                        }
                    }
                }
            }

    別のコントロールにフォーカスを移してからDataGridViewに戻すと、未ハンドルのNoNullAllowedException、

    BindingNavigatorの保存をクリックすると、BindingSourceのEndEditでNoNullAllowedExceptionが発生します。

    DataGridView側の問題なのでしょうか?それともコーディングが悪いのでしょうか?

    コーディングでしたら、ここら辺の正しいコーディング方法が書かれたページ等があればご教授ください。

    なお、似たような質問もありましたが、ESCキーを使わないので若干違うようです。

    以上、よろしくお願いいたします。


    • 編集済み akisas 2015年11月12日 6:25
    2015年11月12日 6:22

回答

  • とりあえずAllowDBNullのDataColumnにDefaultValueを設定しておけば、DataGrid.DataErrorイベントで捕捉できるようになるかな。

    using System.Data;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            BindingSource bnd = new BindingSource();
            DataTable dt;
            public Form1()
            {
                InitializeComponent();
    
    
                DataGridView dgv = new DataGridView();
                dgv.Dock = DockStyle.Fill;
                dgv.RowValidating += dgv_RowValidating;
                dgv.DataError += dgv_DataError;
                this.Controls.Add(dgv);
    
                TextBox txb = new TextBox();
    
                Panel p = new Panel();
                p.Height = txb.Height;
                p.Controls.Add(txb);
                p.Dock = DockStyle.Top;
                this.Controls.Add(p);
    
    
                dt = new DataTable();
                dt.Columns.Add(new DataColumn("ID", typeof(int)) { AllowDBNull = false, DefaultValue= -1 });
                dt.Columns.Add(new DataColumn("Name1", typeof(string)));
                dt.Columns.Add(new DataColumn("Name2", typeof(string)));
    
    
                bnd.DataSource = dt;
                dgv.DataSource = bnd;
            }
    
            void dgv_DataError(object sender, DataGridViewDataErrorEventArgs e)
            {
                if (e.Exception is System.Data.NoNullAllowedException)
                {
                    var dgv = (DataGridView)sender;
                    var row = dgv.Rows[e.RowIndex];
                    row.ErrorText = "値を入力してください";
                    e.ThrowException = false;
                }
            }
    
            void dgv_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
            {
                DataGridView dgv = (DataGridView)sender;
    
                //変更行だけ検証する
                if (!(dgv.IsCurrentRowDirty))
                {
                    return;
                }
    
                //行のセルが空かを調べる
                foreach (DataColumn col in dt.Columns)
                {
                    if (col.AllowDBNull)
                    {
                        continue;
                    }
                    foreach (DataGridViewCell cell in dgv.Rows[e.RowIndex].Cells)
                    {
                        if (cell.OwningColumn.DataPropertyName == col.ColumnName)
                        {
                            if (cell.Value == null || cell.Value.ToString() == "")
                            {
                                //行にエラーテキストを設定
                                dgv.Rows[e.RowIndex].ErrorText =
                                    "'" + cell.OwningColumn.HeaderText + "'に値を入力してください。";
                                e.Cancel = true;
                                return;
                            }
                        }
                    }
    
                }
            }
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク akisas 2015年11月13日 1:54
    2015年11月12日 12:36
  • こんにちは。

    編集状態の確定を行わずに行削除されてしまって内部的におかしくなっているような印象を受けました。
    行削除時に編集状態を終わらせてみてはどうでしょうか。

    private void dataGridView1_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
    {
        DataGridView dgv = (DataGridView)sender;
        if (dgv.IsCurrentRowDirty)
        {
            dgv.CancelEdit();
        }
    }
    

    • 回答としてマーク akisas 2015年11月13日 1:58
    2015年11月12日 15:30
    モデレータ

すべての返信

  • とりあえずAllowDBNullのDataColumnにDefaultValueを設定しておけば、DataGrid.DataErrorイベントで捕捉できるようになるかな。

    using System.Data;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            BindingSource bnd = new BindingSource();
            DataTable dt;
            public Form1()
            {
                InitializeComponent();
    
    
                DataGridView dgv = new DataGridView();
                dgv.Dock = DockStyle.Fill;
                dgv.RowValidating += dgv_RowValidating;
                dgv.DataError += dgv_DataError;
                this.Controls.Add(dgv);
    
                TextBox txb = new TextBox();
    
                Panel p = new Panel();
                p.Height = txb.Height;
                p.Controls.Add(txb);
                p.Dock = DockStyle.Top;
                this.Controls.Add(p);
    
    
                dt = new DataTable();
                dt.Columns.Add(new DataColumn("ID", typeof(int)) { AllowDBNull = false, DefaultValue= -1 });
                dt.Columns.Add(new DataColumn("Name1", typeof(string)));
                dt.Columns.Add(new DataColumn("Name2", typeof(string)));
    
    
                bnd.DataSource = dt;
                dgv.DataSource = bnd;
            }
    
            void dgv_DataError(object sender, DataGridViewDataErrorEventArgs e)
            {
                if (e.Exception is System.Data.NoNullAllowedException)
                {
                    var dgv = (DataGridView)sender;
                    var row = dgv.Rows[e.RowIndex];
                    row.ErrorText = "値を入力してください";
                    e.ThrowException = false;
                }
            }
    
            void dgv_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
            {
                DataGridView dgv = (DataGridView)sender;
    
                //変更行だけ検証する
                if (!(dgv.IsCurrentRowDirty))
                {
                    return;
                }
    
                //行のセルが空かを調べる
                foreach (DataColumn col in dt.Columns)
                {
                    if (col.AllowDBNull)
                    {
                        continue;
                    }
                    foreach (DataGridViewCell cell in dgv.Rows[e.RowIndex].Cells)
                    {
                        if (cell.OwningColumn.DataPropertyName == col.ColumnName)
                        {
                            if (cell.Value == null || cell.Value.ToString() == "")
                            {
                                //行にエラーテキストを設定
                                dgv.Rows[e.RowIndex].ErrorText =
                                    "'" + cell.OwningColumn.HeaderText + "'に値を入力してください。";
                                e.Cancel = true;
                                return;
                            }
                        }
                    }
    
                }
            }
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク akisas 2015年11月13日 1:54
    2015年11月12日 12:36
  • こんにちは。

    編集状態の確定を行わずに行削除されてしまって内部的におかしくなっているような印象を受けました。
    行削除時に編集状態を終わらせてみてはどうでしょうか。

    private void dataGridView1_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
    {
        DataGridView dgv = (DataGridView)sender;
        if (dgv.IsCurrentRowDirty)
        {
            dgv.CancelEdit();
        }
    }
    

    • 回答としてマーク akisas 2015年11月13日 1:58
    2015年11月12日 15:30
    モデレータ