none
重複する値を入力する際にチェックできるようにしたい RRS feed

  • 質問

  • Visual Studio 2010 Pro C#とPostgreSQL9.0で開発しています。

    DataGridViewの"id"カラムで既にレコードに入力済みの値と
    重複する値を入力する際にチェックできるようにしたいのです。
    構成上、PostgreSQL側で"id"カラムに一意制約をつけられないのです。

    label3.Textには、チェックにするセルの値が入ってきているのですが
    重複チェックできません。(エラーにもならないのですが...)

    どなたか、ご指導よろしくお願いいたします。

        private void dataGridView_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
          switch (dataGridViewRecords.Columns[e.ColumnIndex].Name)
          {
            case "id":
              CheckColumn(e.RowIndex);
              break;
          }
        }
    
        private void CheckColumn(int row)
        {
          Control cntl = dataGridViewRecords;
          int col = dataGridViewRecords.Columns["id"].Index;
          DataGridViewCell cell = dataGridViewRecords[col, row];
          DataGridViewTextBoxColumn column = (DataGridViewTextBoxColumn)(dataGridViewRecords.Columns["id"]);
    
          BackColorGridCell(cntl, col, row, false);
    
          string CellStr = "";
    
          if (cell.Value != null)
          {
            CellStr = cell.Value.ToString();
          }
    
          if (CellStr != "")
          {
            cell.Value = CellStr.ToString();
            label3.Text = CellStr.ToString();
    
            // 重複チェック
            ArrayList list = new ArrayList();
    
            for (int i = 0; i < dataGridViewRecords.RowCount; i++)
            {
              string str = dataGridViewRecords[col, i].ToString();
              str = str.ToString();
              list.Add(str);
            }
    
            if (list.Contains(CellStr))
            {
              ShowError(cntl, col, row, "id", "が重複しています。");
            }
          }
        }
    
        private void BackColorGridCell(object sender, int col, int row, bool flg)
        {
          DataGridViewCell cell = ((DataGridView)sender)[col, row];
    
          if (flg)
          {
            cell.Style.BackColor = Color.FromArgb(255, 150, 150);
          }
          else
          {
            cell.Style.BackColor = Color.FromArgb(255, 255, 255);
          }
        }
    
        private void ShowError(Control cntl, int col, int row, string param1, string param2)
        {
          BackColorGridCell(cntl, col, row, true);
          MessageBox.Show(param1 + param2, "確認", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
    

    2010年11月23日 9:25

回答

  • CellValidating を使った重複チェックは、たとえば次のように記述できます。

    private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
      if (dataGridView1.Columns[e.ColumnIndex].Name == "id")
      {
        foreach (DataGridViewRow row in dataGridView1.Rows)
        {
          if (row.Index != e.RowIndex)
          {
            // 編集中の行どうしでないとき
            if (row.Cells[e.ColumnIndex].Value != null)
            {
              // 列に値が格納されているとき
              if (e.FormattedValue.ToString() == row.Cells[e.ColumnIndex].Value.ToString())
              {
                // 値が重複するならエラー文字列をセットして
                // ループから抜ける
                dataGridView1.Rows[e.RowIndex].ErrorText = "データが重複しています。";
                break;
              }
            }
          }
        }
      }
    }
    
    

    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク oira3ryu 2010年11月26日 0:59
    2010年11月24日 0:35

すべての返信

  • CellValidating イベントや RowValidating イベントを使って、ユーザーが入力した値を検証すればいいと思います。

     

    ・RowValidating

    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.datagridview.rowvalidating(v=VS.80).aspx

     

    ・CellValidating

    http://msdn.microsoft.com/ja-jp/library/system.windows.forms.datagridview.cellvalidating(v=VS.80).aspx


    なかむら(http://d.hatena.ne.jp/griefworker)
    2010年11月23日 10:02
  • なかむらさま、いつもありがとうございます。

    CellValidatingイベントで単一の値と比較して重複チェックするのであれば

        private void dataGridViewRecords_CellValidating(object sender,
    
          DataGridViewCellValidatingEventArgs e)
    
        {
    
          DataGridView dgv = (DataGridView)sender;
    
          if (dgv.Columns[e.ColumnIndex].Name == "id" &&
    
            e.FormattedValue.ToString() == "1") 
    
          {
    
              dgv.Rows[e.RowIndex].ErrorText = "値が重複しています。";
    
              dgv.CancelEdit();
    
             e.Cancel = true;
    
          }
    
        }
    

    できるのですが、「今入力されたセルの値と、既に入力済みの複数のセルの値を重複チェック」させる場合に

    どのようにすればいいでしょうか?

     

     

    2010年11月23日 11:12
  • 最初のコードに戻れば、

       for (int i = 0; i < dataGridViewRecords.RowCount; i++)
            {
              string str = dataGridViewRecords[col, i].ToString();
              str = str.ToString();
              list.Add(str);
            }

    で、
              string str = dataGridViewRecords[col, i].Value.ToString();

    とする必要があります。
    また、listにAddする時に自分自身を除かないと必ず重複チェックに引っかかると思います。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年11月23日 15:55
    モデレータ
  • CellValidating を使った重複チェックは、たとえば次のように記述できます。

    private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
      if (dataGridView1.Columns[e.ColumnIndex].Name == "id")
      {
        foreach (DataGridViewRow row in dataGridView1.Rows)
        {
          if (row.Index != e.RowIndex)
          {
            // 編集中の行どうしでないとき
            if (row.Cells[e.ColumnIndex].Value != null)
            {
              // 列に値が格納されているとき
              if (e.FormattedValue.ToString() == row.Cells[e.ColumnIndex].Value.ToString())
              {
                // 値が重複するならエラー文字列をセットして
                // ループから抜ける
                dataGridView1.Rows[e.RowIndex].ErrorText = "データが重複しています。";
                break;
              }
            }
          }
        }
      }
    }
    
    

    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク oira3ryu 2010年11月26日 0:59
    2010年11月24日 0:35
  • なかむらさま、ありがとうございます。

    例示していただいたコードを実行してみようとしたのですが、なぜか重複チェックされないのです。

    実行しているのは、ほとんどなかむらさまに掲示していただいたコードそのままで

    データグリッドビューの名前を変更しただけです。

    例示されたコードで申し訳ありませんがお願いいたします。

    // フォームロードイベント
    private void FormRecords_Load(object sender, EventArgs e)
    {
    	// データグリッドビューのイベントハンドラ設定
    	DataGridViewEvent(); 
    }	
    
    // データグリッドビューのイベントハンドラ
    private void DataGridViewEvent()
    {
    	// DataGridViewのCellValidatingイベント設定
    	dataGridViewRecords.CellValidating += new DataGridViewCellValidatingEventHandler(dataGridViewRecords_CellValidating);
    }
    
    private void dataGridViewRecords_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
    {
     if (dataGridViewRecords.Columns[e.ColumnIndex].Name == "id")
     {
      foreach (DataGridViewRow row in dataGridViewRecords.Rows)
      {
       if (row.Index != e.RowIndex)
       {
        // 編集中の行どうしでないとき
        if (row.Cells[e.ColumnIndex].Value != null)
        {
         // 列に値が格納されているとき
         if (e.FormattedValue.ToString() == row.Cells[e.ColumnIndex].Value.ToString())
         {
          // 値が重複するならエラー文字列をセットして
          // ループから抜ける
          dataGridViewRecords.Rows[e.RowIndex].ErrorText = "データが重複しています。";
          break;
         }
        }
       }
      }
     }
    }
    
    

    2010年11月24日 10:25
  • すいません、間違っていました。

        private void dataGridViewRecords_CellValidated(object sender, DataGridViewCellEventArgs e)
        {
          DataGridView dgv = (DataGridView)sender;
          //エラーテキストを消す
          dgv.Rows[e.RowIndex].ErrorText = null;
        }
    

    エラーを消していたから、「重複チェックできていない」ように見えただけでした。

    しかし、dgv.Rows[e.RowIndex].ErrorText = null;しないとエラーがでたままになってしまうのですが

    こういう場合は、どのように処理すればよいのでしょうか?

    2010年11月24日 11:40
  • trapemiyaさま、いつもありがとうございます。

    string str = dataGridViewRecords[col, i].Value.ToString();
    

    ですが、「オブジェクト参照がオブジェクト インスタンスに設定されていません」というエラーになってしまいました。

    値がNullといエラーなのでしょうか?

    2010年11月24日 11:46
  • ですが、「オブジェクト参照がオブジェクト インスタンスに設定されていません」というエラーになってしまいました。

    値がNullといエラーなのでしょうか?

    その通りだと思います。一番下の新規行がNullではないでしょうか?どのみちNull値は比較しないので、Nullの場合はその行を実行しないようにしても良いと思います。

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年11月24日 11:54
    モデレータ
  • 何度もすいません。わかりました。
        private void dataGridViewRecords_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
          if (dataGridViewRecords.Columns[e.ColumnIndex].Name == "id")
          {
            foreach (DataGridViewRow row in dataGridViewRecords.Rows)
            {
              if (row.Index != e.RowIndex)
              {
                // 編集中の行どうしでないとき
                if (row.Cells[e.ColumnIndex].Value != null)
                {
                  label2.Text = e.FormattedValue.ToString();              
                  label3.Text = dataGridViewRecords.Rows[e.RowIndex].ToString();
                  // 列に値が格納されているとき
                  if (e.FormattedValue.ToString() == row.Cells[e.ColumnIndex].Value.ToString())
                  {
                    // 値が重複するならエラー文字列をセットして
                    // ループから抜ける
                    dataGridViewRecords.Rows[e.RowIndex].ErrorText = "データが重複しています。";
                    //入力した値をキャンセルして元に戻すには、次のようにする
                    dataGridViewRecords.CancelEdit();
                    //キャンセルする
                    e.Cancel = true;
                    //break;
                  }
                }
              }
            }
          }
        }
    
        private void dataGridViewRecords_CellValidated(object sender, DataGridViewCellEventArgs e)
        {
          DataGridView dgv = (DataGridView)sender;
          //エラーテキストを消す
          dgv.Rows[e.RowIndex].ErrorText = null;
        }
    
    というふうにすることで、重複値が入力された場合はエラーアイコンを出して、値を元に戻し
    セル選択が解除されたらエラーテキストを消すという処理ができました。
    これで、よいものなのでしょうか?アドバイスありましたらお願いいたします。
    2010年11月24日 12:00
  • それで良さそうですね。
    (もしかするとユーザーさんにとっては CancelEdit されない方がいい場合もあるかも?)

    この返信では、スレッドの本題とは関係ない些細なことを書かせてもらいます。
    もし本題がまだ続く場合には邪魔しちゃうことになるのですみません。

    CellValidating イベントは、編集の有無とは無関係に、セルを移動させるだけでも常に発生しますので、イベントハンドラの先頭に次のコードがあった方がいいと思います。

    if (!dataGridViewRecords.IsCurrentCellDirty) return;

    それと、本当にどうでもいいことですし、大きなお世話なんですけど、、、

    > DataGridView dgv = (DataGridView)sender;

    CellValidating の方は直接 dataGridViewRecords を使用され、CellValidated では sender を利用(インスタンスの変数名に依存しない書き方)されている点に、チグハグな印象を受けました。

    > // 編集中の行どうしでないとき

    私でしたらこのコメントは if (row.Index != e.RowIndex) の上に書きます。

    > // 列に値が格納されているとき

    「別の行と値が同じとき」の方がいいと思います。

    2010年11月25日 2:05
  • TH01さま、ありがとうございます。

    それと、本当にどうでもいいことですし、大きなお世話なんですけど、、、

    大変参考になりますので、どしどしご指導お願いいたします。

     

    if (!dataGridViewRecords.IsCurrentCellDirty) return;
    

    で、編集中のセルだけに絞って処理させるという使い方なのですね。

    参考になり早速採用させていただきました。

     

    DataGridView dgv = (DataGridView)sender;
    

    の部分も参考にしたサイトがその書き方をしておりましたので

    そのまま使っていたのが原因です(汗;

    編集コメントもそのとおりですね。

     

    みなさんのご指導により、重複チェックの部分はこれでできたようなので

    一旦、このスレッドを閉めさせていただきます。

    ご指導ありがとうございました。

    2010年11月26日 1:24