DataGridView:日付編集時にカレンダー(DateTimePicker)を利用する方法(サンプルあり)

📋 目次(クリックで展開)

DataGridView で日付を扱う際、「キーボードで手入力させるよりも、マウス操作でカレンダーからパッと日付を選ばせたい」という要件はよく出てくると思います。日付の手入力はフォーマットの打ち間違いなどのミスが起きやすいため、カレンダー UI の導入は効果的です。

しかし、DataGridView の標準機能には DataGridViewCalendarColumn のようなカレンダー専用の列タイプが用意されていません。多くの場合、有償コンポーネントを購入してプロジェクトに組み込むことになると思います。

また、複雑なカスタム列クラスを自作する方法もありますが、標準の DateTimePicker を用意しておき、対象のセルが編集モードになった瞬間にセルの真上に重ね合わせる手法を使うことで、Form1.cs 1枚のシンプルなコードだけで実現できます。

今回は、編集時にカレンダーコントロールをスムーズに表示・非表示を切り替える実装手順と、スクリプトのポイントを解説します。

編集時にカレンダーを重ね合わせる仕組み

この機能は、DataGridView の編集開始・終了などのイベントを利用して、カレンダーコントロール(DateTimePicker)の「表示・非表示・位置合わせ」を切り替えることで実現します。

  • 非編集時(通常状態):カレンダーコントロールは Visible = false(非表示)の状態で、画面の裏側に隠れています。
  • 編集開始時(CellBeginEdit:ユーザーが日付のセルをダブルクリックするなどして編集が始まった瞬間、そのセルの画面上の正確な位置とサイズを割り出し、カレンダーをその場所に重ねて表示(Visible = true)します。
  • 確定・編集終了時(CellEndEdit:カレンダーで日付が選ばれてドロップダウンが閉じたら、その値をセルのデータに書き戻し、用が済んだカレンダーを再び非表示にして隠します。

実装コード

画面に DataGridView(dataGridView1)が配置されているものとします。現在の日付(DateTime.Today)をベースにしたデモデータをバインドし、日付列でのみカレンダーを起動させる実装例です。

Form1.cs

Form1.cs
using System.Data;
namespace DataGridView_CalendarCell;
public partial class Form1 : Form
{
private DataTable _employeeTable = new DataTable();
// セルの上にピッタリ重ね合わせるためのカレンダーコントロール
private DateTimePicker _dateTimePicker = new DateTimePicker();
public Form1()
{
InitializeComponent();
InitializeData();
InitializeDataGridView();
InitializeDateTimePicker(); // カレンダーの初期設定
}
private void InitializeData()
{
// 添付画像のイメージに合わせたデータ構造
_employeeTable.Columns.Add("ID", typeof(int));
_employeeTable.Columns.Add("FirstName", typeof(string));
_employeeTable.Columns.Add("LastName", typeof(string));
_employeeTable.Columns.Add("Date", typeof(string)); // 日付列
_employeeTable.Rows.Add(1, "John", "Smith", DateTime.Today.AddDays(-5).ToShortDateString());
_employeeTable.Rows.Add(2, "John", "Johnson", DateTime.Today.AddDays(-3).ToShortDateString());
_employeeTable.Rows.Add(3, "Mike", "Brown", DateTime.Today.ToShortDateString());
}
private void InitializeDataGridView()
{
dataGridView1.DataSource = _employeeTable;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
// 1. 編集開始・終了、およびスクロール時のイベントを購読
dataGridView1.CellBeginEdit += DataGridView1_CellBeginEdit;
dataGridView1.CellEndEdit += DataGridView1_CellEndEdit;
dataGridView1.Scroll += DataGridView1_Scroll; // スクロールズレ対策
}
private void InitializeDateTimePicker()
{
// 2. カレンダーコントロールを初期状態では非表示にして DataGridView の子要素として追加
_dateTimePicker.Visible = false;
_dateTimePicker.Format = DateTimePickerFormat.Short; // 「YYYY/MM/DD」形式で表示
// カレンダーのドロップダウンが閉じられた時のイベントを購読
_dateTimePicker.CloseUp += DateTimePicker_CloseUp;
dataGridView1.Controls.Add(_dateTimePicker);
}
/// <summary>
/// セルの編集が始まったときに呼び出されるイベント
/// </summary>
private void DataGridView1_CellBeginEdit(object? sender, DataGridViewCellCancelEventArgs e)
{
// 「Date」列以外は何もしない
if (dataGridView1.Columns[e.ColumnIndex].Name != "Date") return;
// 3. 現在編集しようとしているセルの「画面上の位置とサイズ」を取得
Rectangle rect = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
// カレンダーをセルの大きさと位置に合わせる
_dateTimePicker.Size = rect.Size;
_dateTimePicker.Location = rect.Location;
// 4. 現在セルに入っている日付文字列を解析し、カレンダーの初期値としてセット
if (DateTime.TryParse(dataGridView1[e.ColumnIndex, e.RowIndex].Value?.ToString(), out DateTime dt))
{
_dateTimePicker.Value = dt;
}
else
{
_dateTimePicker.Value = DateTime.Today; // 変換できなければ今日の日付
}
// カレンダーを表示させてフォーカスを移す
_dateTimePicker.Visible = true;
_dateTimePicker.Focus();
}
/// <summary>
/// セルの編集が終了するときに呼び出されるイベント
/// </summary>
private void DataGridView1_CellEndEdit(object? sender, DataGridViewCellEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].Name != "Date") return;
// 5. カレンダーで選択された日付を、セルの値へ書き戻す
if (_dateTimePicker.Visible)
{
dataGridView1[e.ColumnIndex, e.RowIndex].Value = _dateTimePicker.Value.ToShortDateString();
_dateTimePicker.Visible = false; // カレンダーを使用後は非表示にする
}
}
/// <summary>
/// カレンダーのドロップダウン(カレンダー画面)が閉じられたときのイベント
/// </summary>
private void DateTimePicker_CloseUp(object? sender, EventArgs e)
{
// 6. 日付が選択されてカレンダーが閉じたら、明示的にセルの編集モードを終了(確定)させる
// これを呼ぶことで、自動的に上記の「CellEndEdit」イベントが誘発されます
dataGridView1.EndEdit();
}
/// <summary>
/// グリッドがスクロールされたときのイベント
/// </summary>
private void DataGridView1_Scroll(object? sender, ScrollEventArgs e)
{
// カレンダーが表示されたままスクロールされると位置がズレるため、強制的に編集を終了して隠す
if (_dateTimePicker.Visible)
{
_dateTimePicker.Visible = false;
dataGridView1.EndEdit();
}
}
}

ソースコード

サンプルアプリケーション

「Date」セルの編集を開始し、カレンダーアイコンとドロップダウンの組み合わせのボタンをクリックします。すると、カレンダーコントロールが表示されることが分かります。カレンダーコントロール上で日付を選択すると、選択した日付が DataGridView の値として反映されます。

DataGridView_CalendarCell.zip
※ サンプルアプリケーションは、.NET 10 をターゲットに作成しています。

実装のポイント

1. GetCellDisplayRectangle メソッドによる位置合わせ

セルの上に別のコントロールを重ねるための一取得に、dataGridView1.GetCellDisplayRectangle(列, 行, true) を利用します。 このメソッドを使うことで、対象のセルが今画面の中の「横位置、縦位置、横幅、縦幅」のどこにレンダリングされているかという領域情報(Rectangle)を取得できます。これを DateTimePicker のサイズと位置(Location)にそのまま代入するだけで、セル位置に連動してカレンダーを表示できます。

2. CloseUp イベント内でカレンダーを閉じる

標準の DateTimePicker は、カレンダーから日付を選んで画面が閉じても、別のセルをクリックするまで値が確定しない性質があります。 そこで、カレンダー画面が閉じられたときに発生する CloseUp イベント をキャッチし、その瞬間に dataGridView1.EndEdit() をコードから呼び出します。これにより、日付選択と同時に値がデータへと反映されるため、ユーザーにとって自然な操作感となります。

3. スクロール時の「カレンダー置き去り」を防ぐズレ対策

DataGridView にコントロールを直接貼り付ける際、実務で必ず発生するのが「カレンダーを出したままマウスホイールなどでグリッドをスクロールすると、重ね合わせたカレンダーだけが元の座標に取り残されて画面上で浮いてしまう」という事象です。 これを防ぐため、Scroll イベント が発生した際にはカレンダーの Visiblefalse にし、EndEdit() で強制的に編集モードを終了して非表示にします。

まとめ

今回は、独自にカスタム列クラスを実装することなく、イベント処理とコントロールの重ね合わせで DataGridView の日付入力欄にカレンダーを表示する手順について解説しました。

コントロールのライフサイクル(編集開始で表示、確定・スクロールで非表示)をコントロールすることで、標準機能の組み合わせだけでもインライン編集画面を構築することができます。

  • CellBeginEdit 時に GetCellDisplayRectangle を使ってセルの位置にカレンダーを表示する
  • カレンダーの CloseUp イベントで EndEdit() を呼び、日付選択と同時に入力を確定する
  • CellEndEdit 時に選択された日付を .ToShortDateString() でセルの値へ書き戻す
  • スクロール時はカレンダーの座標のズレを防ぐため、Scroll イベント内でカレンダーを隠す

日付入力を伴う業務画面のカスタマイズの参考になりましたら幸いです。


💬 コメント