none
DataGridViewでのID制約を実装 RRS feed

  • 質問

  • こんにちは。お世話になります。

    WindowsControlの質問であり、本来は言語に依存しませんが、現在の.Net Framework掲示板では浮いてしまうような

    質問のようでしたので、C#掲示板に投稿させていただきました。C#の文法で質問します。よろしくお願いいたします。

     

    IDのあるテーブルを自由に更新させようと思っています。

    しかし当然ながら、

    1. すでに登録されているレコードのIDは編集不可能に
    2. ID列に重複した値やnullはだめ

    という制約を課すべきなので、

    1の実装として、

    • 普段は、ID列をReadOnlyにして、バインドしているbindingSourceのAddingNewイベントでReadOnlyを解除
    • dataGridViewのRowValidated処理で、ReadOnlyを再びtrueに (あと、ErrorTextをnullにしている)

    しました。

    2の実装として、

    • dataGridViewのRowValidatingイベントで、ReadOnly==true、つまり新規行の編集でないときは、return。
    • それ以外のときは、空白なら、エラーに
    • dataGridViewのデータの大元のdataTableのSelectメソッドを実行し、行が存在したとき、エラーに

    としました。

    おおむねは、これで動作するのですが、[ESC]によって編集をキャンセルしたときに、ReadOnlyが解除されたままで、

    既存列に移動した後で、編集できてしまうようになります。また、そこから編集をせずに移動させても、

    検証が始まってしまい、DataTableのIDの数は当然1であり、エラーから抜け出せなくなります。

     

    編集キャンセルしたときのプロパティを取得するかイベントなどで、ReadOnlyをtrueにできないかと思います。

    VirtualModeはfalseなので、CancelRowEditイベントは使えません。

    あるいは、実装の仕方そのものがおかしいのでしょうか。どうか、お教えください。

    2007年8月22日 8:27

回答

  • 1については、CellBeginEdit イベントが使えると思います。
    2については、全レコードが読み込み済みなのであれば、制約を使用する手もあります。


    少し長いですが、サンプルを書いてみました。

    画面に DataGridView を配置し、イベントを2つ結びつけてください。

    Code Snippet

    public Form1()
    {
        InitializeComponent();

     

        // テストデータ
        DataTable table = new DataTable();
        table.Columns.Add("ID", typeof(string));
        table.Columns.Add("データ", typeof(string));
        table.Rows.Add("001", "データ1");
        table.Rows.Add("002", "データ2");
        // (読み込んだ後の状態と同じにしておく)
        table.AcceptChanges();

     

        // 主キー制約をテーブルに追加
        UniqueConstraint pk = new UniqueConstraint(
            table.Columns["ID"], true);
        table.Constraints.Add(pk);

     

        dataGridView1.DataSource = table;
    }

     

    private void dataGridView1_CellBeginEdit(
        object sender, DataGridViewCellCancelEventArgs e)
    {
        DataGridView grid = (DataGridView)sender;

     

        // 新規追加用の行では処理しない(編集可能)
        if (e.RowIndex == grid.NewRowIndex)
            return;

     

        // ID列であれば編集可否を制御する
        string tableColumnName = grid.Columns[e.ColumnIndex].DataPropertyName;
        if (tableColumnName == "ID")
        {
            DataRow row = ((DataRowView)grid.Rows[e.RowIndex].DataBoundItem).Row;
            switch (row.RowState)
            {
                case DataRowState.Modified:
                case DataRowState.Unchanged:
                    // 既存していたレコードでは編集不可
                    // (データベースへ反映していない追加行は編集可能)
                    e.Cancel = true;
                    return;
            }
        }
    }

     

    private void dataGridView1_DataError(
        object sender, DataGridViewDataErrorEventArgs e)
    {
        MessageBox.Show(
            e.Exception.Message, "エラー",
            MessageBoxButtons.OK, MessageBoxIcon.Error);
        e.Cancel = true;
    }

     

     

     Ogacha さんからの引用
    WindowsControlの質問であり、本来は言語に依存しませんが、現在の.Net Framework掲示板では浮いてしまうような質問のようでしたので、C#掲示板に投稿させていただきました。

    これ、迷いますよね

    2007年8月22日 10:08

すべての返信

  • 1については、CellBeginEdit イベントが使えると思います。
    2については、全レコードが読み込み済みなのであれば、制約を使用する手もあります。


    少し長いですが、サンプルを書いてみました。

    画面に DataGridView を配置し、イベントを2つ結びつけてください。

    Code Snippet

    public Form1()
    {
        InitializeComponent();

     

        // テストデータ
        DataTable table = new DataTable();
        table.Columns.Add("ID", typeof(string));
        table.Columns.Add("データ", typeof(string));
        table.Rows.Add("001", "データ1");
        table.Rows.Add("002", "データ2");
        // (読み込んだ後の状態と同じにしておく)
        table.AcceptChanges();

     

        // 主キー制約をテーブルに追加
        UniqueConstraint pk = new UniqueConstraint(
            table.Columns["ID"], true);
        table.Constraints.Add(pk);

     

        dataGridView1.DataSource = table;
    }

     

    private void dataGridView1_CellBeginEdit(
        object sender, DataGridViewCellCancelEventArgs e)
    {
        DataGridView grid = (DataGridView)sender;

     

        // 新規追加用の行では処理しない(編集可能)
        if (e.RowIndex == grid.NewRowIndex)
            return;

     

        // ID列であれば編集可否を制御する
        string tableColumnName = grid.Columns[e.ColumnIndex].DataPropertyName;
        if (tableColumnName == "ID")
        {
            DataRow row = ((DataRowView)grid.Rows[e.RowIndex].DataBoundItem).Row;
            switch (row.RowState)
            {
                case DataRowState.Modified:
                case DataRowState.Unchanged:
                    // 既存していたレコードでは編集不可
                    // (データベースへ反映していない追加行は編集可能)
                    e.Cancel = true;
                    return;
            }
        }
    }

     

    private void dataGridView1_DataError(
        object sender, DataGridViewDataErrorEventArgs e)
    {
        MessageBox.Show(
            e.Exception.Message, "エラー",
            MessageBoxButtons.OK, MessageBoxIcon.Error);
        e.Cancel = true;
    }

     

     

     Ogacha さんからの引用
    WindowsControlの質問であり、本来は言語に依存しませんが、現在の.Net Framework掲示板では浮いてしまうような質問のようでしたので、C#掲示板に投稿させていただきました。

    これ、迷いますよね

    2007年8月22日 10:08
  • TH01様、以前もお世話になりましたが、今回もありがとうございました。

    このコードでバッチリです! やはり、実装方法が全然違っていました。

     

    DataGridView.NewRowIndexとEventArgs系.RowIndexとを比べる技、

    DataGridView.DataErrorを使い、DataRow.RowStateで検証する技は、なるほどです。参考になりました。

     

    掲示板のジャンルに、WPFとかの3.0系とASP.NETに加えて、WindowsForm関連とか、

    ADO.NET関連(となると、DataGridViewは曖昧か)とかいうのがあれば、

    他の言語のユーザーとのリソース共有ができると思うんですけどね。

    でも、ソースを貼る時、言語に依存してしまうため、困るのかもしれませんね。

    2007年8月22日 13:38