➡️

Windows Forms C#定石 - DataGridView - EditMode, DropDown

に公開

はじめに

C# ソフト開発時に、決まり事として実施していた内容を記載します。

DataGridView については下記記事もあります

テスト環境

ここに記載した情報/ソースコードは、Visual Studio Community 2022 を利用した下記プロジェクトで生成したモジュールを Windows 11 24H2 で動作確認しています。

  • Windows Forms - .NET Framework 4.8
  • Windows Forms - .NET 8

EditMode

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview.editmode

DataGridView 利用時、DataGridViewEditMode.EditOnEnter がお勧めです。

既定状態

DataGridView.EditMode の既定値 DataGridViewEditMode.EditOnKeystrokeOrF2 の場合、セルに対するマウスクリックの挙動は以下の通りです。

  • DataGridViewTextBoxCell
    • 1回目のクリックで選択、2回目クリックで編集モードに遷移
  • DataGridViewCheckBoxCell
    • チェックエリアをクリックで即時選択/非選択となる
      • 上記は見た目の変化だけで、実際に DataGridViewCheckBoxCell の値が更新されるのは、セル移動時となります。詳細は末尾「おまけ - DataGridViewCheckBoxCell」参照
  • DataGridViewComboBoxCell
    • 1回目のクリックで編集モードに遷移。編集モードになった後、DropDown 表示ボタンが有効となる
    • 対象セルが選択されていない状態で、DropDown 表示ボタンをクリックしても、DropDown 表示されない(編集モードに遷移するだけ)

編集モードというワンクッションで、操作に手間がかかるので、後述 EditOnEnter がお勧めです。

EditOnEnter

DataGridViewEditMode.EditOnEnter は、セルが選択されると同時に編集モードに入る動作となります。

// デザイナで DataGridView dataGridView1 を配置
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;

タブ移動で問題なく編集モードとなり、マウスクリックで下記動作となります。

  • DataGridViewTextBoxCell
    • 1回目のクリックで編集モードに遷移
  • DataGridViewCheckBoxCell
    • チェック部分をクリックで即時選択/非選択可能
  • DataGridViewComboBoxCell
    • 対象セルが選択されていない状態でも、DropDown 表示ボタンをクリックで、DropDown 表示される
    • 対象セルが選択された状態で、セルをクリックするとDropDown 表示される

DataGridViewComboBoxCell 選択時、自動的に DropDown 表示を行うこともできますが、基本的に、そのような設定はしませんでした。(理由は後述)

自動表示

DataGridViewComboBoxCell 選択時に、DropDown 自動表示は、下記で対処可能です。

// デザイナで DataGridView dataGridView1 を配置
dataGridView1.CellEnter += DataGridView_CellEnter;
// .NET Framework 時 object? の ? 不要
private void DataGridView_CellEnter(object? sender, DataGridViewCellEventArgs e)
{
  if (e.ColumnIndex < 0 || e.RowIndex < 0)
  {
    return;
  }
  if (sender is DataGridView dgv)
  {
    dgv.BeginEdit(true);           // 編集モードに移行
    if (dgv.EditingControl is ComboBox comboBox)
    {
      comboBox.DroppedDown = true; // DropDownを表示
    }
  }
}

キー操作でセル移動する場合には、特に気になることはありません。

しかし、マウス操作の場合、DropDown 表示が表示された状態で、任意のセルを選択しても、DropDown 表示のキャンセルとして扱われるので、1回のアクションで任意のセルに移動できなくなってしまいます。

DropDown 自動表示をしない場合、DropDown 表示をするためには、セルクリック、もしくは、キー入力(F4 もしくは ALT + )する手間があります。

どちらがベターかといえば、、、

キー操作で移動を行うの方より、マウス操作を行う方のほうが習熟度は低いと思うので、マウス操作に着目して判断することとします。
マウスで DataGridViewComboBoxCell を選択ということは、編集を意図しているので、DropDown 自動表示で良いのかなぁ。
いや、マウスの場合、DataGridViewComboBoxCell の DropDown 表示ボタンを直接クリックすれば、自動表示としなくても DropDown 表示できるので不便と感じることはないかな。

そうすると、DropDown 自動表示とした場合、1回のアクションで任意のセルに移動できない不便が勝ると判断したので、自動表示を行うことはありませんでした。

おまけ

DataGridViewCheckBoxCell

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridviewcheckboxcell

通常、チェック ボックスのセル値は、他のデータと同様にストレージ用、または一括操作を実行するためのものです。 ユーザーがチェック ボックスのセルをクリックしたときにすぐに応答する必要がある場合は、DataGridView.CellClick イベントを処理できますが、このイベントはセルの値が更新される前に発生します。 クリック時の新しい値が必要な場合は、現在の値に基づいて期待される値を計算する方法があります。 もう 1 つの方法は、変更をすぐにコミットし、それに応答する DataGridView.CellValueChanged イベントを処理することです。 セルがクリックされたときに変更をコミットするには、DataGridView.CurrentCellDirtyStateChanged イベントを処理する必要があります。 ハンドラーで、現在のセルがチェック ボックスセルの場合は、DataGridView.CommitEdit メソッドを呼び出し、Commit 値を渡します。

前述 EditMode で、DataGridViewCheckBoxCell が選択されていない状態でも、クリックでチェック表示が選択/非選択となると記載しましたが、この時点では、セルの値は確定していないので、CellValueChanged イベントは発生しません。

EditMode で提示した形態の DataGridView で、CellValueChanged イベントハンドラを用意してみます。

// デザイナで DataGridView dataGridView1 を配置
// 列定義
dataGridView1.Columns.AddRange(new DataGridViewColumn[]
{
  new DataGridViewTextBoxColumn { Name = "ID", Width = 20, ReadOnly = true },
  new DataGridViewTextBoxColumn { Name = "Name", Width = 200 },
  new DataGridViewComboBoxColumn { Name = "Type", Width = 200,
        DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox },
  new DataGridViewCheckBoxColumn { Name = "Select 1", Width = 80 },
  new DataGridViewCheckBoxColumn { Name = "Select 2", Width = 80 },
  new DataGridViewButtonColumn { Name = "Click", Width = 80 }
});
if (dataGridView1.Columns["Type"] is DataGridViewComboBoxColumn col)
{
  col.Items.AddRange(new string[] { "Type1", "Type2", "Type3", "Type4" });
}
// テストデータ
for (int no = 0; no < 10; no++)
{
  DataGridViewRow row = new DataGridViewRow();
  row.CreateCells(dataGridView1);
  row.Cells[0].Value = no; 
  row.Cells[1].Value = $"情報{no}";
  // TODO - 以降のセルにも値を設定

  dataGridView1.Rows.Add(row);
}
// イベントハンドラ
dataGridView1.CellValueChanged += DataGridView_CellValueChanged;
// .NET Framework 時 object? の ? 不要
private void DataGridView_CellValueChanged(object? sender, DataGridViewCellEventArgs e)
{
  if (e.ColumnIndex < 0 || e.RowIndex < 0)
  {
    return;
  }
  if (sender is DataGridView dgv)
  {
    var cell = dgv.Rows[e.RowIndex].Cells[e.ColumnIndex];
    if (cell != null)
    {
      // TODO - 値変化時の処理
      MessageBox.Show($"col={e.ColumnIndex} row={e.RowIndex} value={cell.Value}");
    }
  }
}

任意の DataGridViewCheckBoxCell をクリックして、チェック表示を選択/非選択と変更しても、CellValueChanged の MessageBox.Show がされることはありません。
チェック表示を初期値から変更した状態でセルを移動すると、MesageBox.Show されます。

DataGridViewCheckBoxCell でチェック表示を選択/非選択と変更直後に CellValueChanged を発生させたい場合は、CurrentCellDirtyStateChanged イベントで CommitEdit が必要となります。

// デザイナで DataGridView dataGridView1 を配置
dataGridView1.CurrentCellDirtyStateChanged += DataGridView_CurrentCellDirtyStateChanged;
// .NET Framework 時 object? の ? 不要
private void DataGridView_CurrentCellDirtyStateChanged(object? sender, EventArgs e)
{
  if (sender is DataGridView dgv)
  {
    if (dgv.CurrentCell is DataGridViewCheckBoxCell)
    {
      dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }
  }
}

dataGridView1.AllowUserToAddRows = true として、新しい行の DataGridViewCheckBoxCell のチェック表示部分以外をクリックでは行追加されませんが、チェック表示部分をクリックすると行追加されます。

チェック表示部分をクリックした場合、CurrentCellDirtyStateChanged 以前で行追加がされるので、下記のようなソースとしても意味がありません。

// .NET Framework 時 object? の ? 不要
private void DataGridView_CurrentCellDirtyStateChanged(object? sender, EventArgs e)
{
  if (sender is DataGridView dgv)
  {
    if (dgv.CurrentCell is DataGridViewCheckBoxCell)
    {
      // 行追加用の「新しい行」以外 - 既に行追加が動作してるので意味がない
      if (dgv.CurrentRow != null && !dgv.CurrentRow.IsNewRow)
      {
        dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
      }
    }
  }
}

出典

本記事は、2025/04/07 Qiita 投稿記事の転載です。

Windows Forms C#定石 - DataGridView - EditMode, DropDown

Discussion