組込のセル型に日付ピッカーはありません。作り方を通して、 独自のセル型がホストの UI とどう連携するかを示します。
作るもの — 日付を表示し、右端のカレンダーボタンを押すと ホスト側の日付選択ダイアログが開くセル型です。
方針は 2 段構えです。
- セル型(コア側) — 日付の描画とボタンの当たり判定。UI に依存しない
- ホスト側 —
CellButtonClickedを受けてダイアログを出し、値を書き戻す
コアは UI を持たないため、カレンダーそのものはコア側では出せません。 セル型は「押された」ことだけを伝え、実際のダイアログはホストが出します。
セル型を実装する
using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.CellTypes;
using unvell.ReoGrid.Core.Style;
/// <summary>日付を表示し、右端のカレンダーボタンでホストに日付選択を依頼するセル型。</summary>
public sealed class DatePickerCellType : CellTypeDescriptor
{
private const double ButtonWidth = 18;
private const uint Accent = 0xFF2D8CF0; // 組込セル型と同じ水色
private const uint HoverTint = 0xFFEAF4FE;
private readonly string _format;
public DatePickerCellType(string format = "yyyy/MM/dd") => _format = format;
public override string TypeName => "datepicker";
// 日付の文字列は自分で描く
public override bool ReplacesText => true;
// キーボードからの直接入力も許す(Excel の日付セルと同じ感覚)
public override bool AllowsTextEditing => true;
public override bool Paint(in CellTypePaintContext ctx)
{
// 値は Excel と同じシリアル値。0 は「未入力」として空欄にする
double serial = ctx.Value.AsNumber;
string text = serial > 0
? DateTime.FromOADate(serial).ToString(_format)
: "";
var style = ctx.MakeTextStyle(HAlign.Left, VAlign.Middle);
ctx.Graphics.DrawText(text, ctx.X + 3, ctx.Y, ctx.Width - ButtonWidth - 6, ctx.Height,
ctx.TextColor, style);
// 右端にカレンダーのボタンを描く(ホバー中は背景を付ける)
double bx = ctx.X + ctx.Width - ButtonWidth;
if (ctx.HoverRegion == 1)
ctx.Graphics.FillRectangle(bx, ctx.Y + 1, ButtonWidth, ctx.Height - 2, HoverTint);
// 枠だけの四角 = 外側を塗って内側を地色で抜く
double gy = ctx.Y + ctx.Height / 2 - 5;
ctx.Graphics.FillRectangle(bx + 4, gy, 10, 10, Accent);
ctx.Graphics.FillRectangle(bx + 5, gy + 3, 8, 6, 0xFFFFFFFF);
return true;
}
// ボタンの上だけをホバー領域 1 にする(それ以外は 0)
public override int HitRegion(double localX, double localY, double width, double height)
=> localX >= width - ButtonWidth ? 1 : 0;
public override CellTypeClickResult OnClick(in CellTypeClickContext ctx)
=> ctx.LocalX >= ctx.Width - ButtonWidth
? CellTypeClickResult.RaiseButton() // ホストの CellButtonClicked へ
: CellTypeClickResult.Unhandled;
public override void WriteConfig(IDictionary<string, object?> config)
=> config["format"] = _format;
}
要点は 4 つです。
| メンバー | 役割 |
|---|---|
Paint | セルの中身を描く。true を返すと既定の描画は行われない |
HitRegion | ホバー中の領域番号を返す。Paint に ctx.HoverRegion として渡る |
OnClick | クリックの結果を返す。RaiseButton() で CellButtonClicked が上がる |
WriteConfig | reogrid-json に保存する設定値 |
AllowsTextEditing を true にしているので、ボタンを使わずキーボードから
直接日付を打ち込むこともできます。ReplacesText が true なので、
セルの文字は既定の描画ではなく Paint が描いたものになります。
描画に使えるのは FillRectangle / DrawLine / DrawText / FillEllipse などの
基本的な図形だけです。プラットフォーム非依存にするための制約で、
組込のセル型も同じ範囲で描かれています(独自の描画層)。
割り当てる
ws.SetCellType(RangePosition.Parse("C2:C100"), new DatePickerCellType());
// 値は日付のシリアル値。表示形式はセル型が持つのでスタイル側は不要
ws.SetNumber(1, 2, new DateTime(2026, 8, 19).ToOADate());
値は Excel と同じシリアル値(1899-12-30 からの日数)です。
DateTime.ToOADate() / DateTime.FromOADate() で変換します。
シリアル値で持つため、DATEDIF や TODAY()-C2 のような数式がそのまま使えます。
文字列で持つとこれができません。
ホスト側でダイアログを出す
grid.CellButtonClicked += (s, e) =>
{
var ws = grid.ActiveWorksheet;
if (ws.GetCellType(e.Cell.Row, e.Cell.Col) is not DatePickerCellType) return;
using var dlg = new DatePickerForm(); // 自前のダイアログ
if (dlg.ShowDialog(this) != DialogResult.OK) return;
ws.RecordCell("日付の選択", e.Cell.Row, e.Cell.Col,
() => ws.SetNumber(e.Cell.Row, e.Cell.Col, dlg.Value.ToOADate()));
grid.RefreshExternal();
};
CellButtonClicked はボタンセル型と共通のイベントです。複数の種類のボタンを
置いている場合は、上のように GetCellType で判別してください。
WinForms なら MonthCalendar、WPF なら DatePicker を載せた小さなウィンドウで十分です。
履歴に載せる
// ホスト側: CellButtonClicked を受けて日付選択ダイアログを出し、結果を書き戻す
ws.RecordCell("日付の選択", row, col, () => ws.SetNumber(row, col, picked.ToOADate()));
RecordCell で包むと Ctrl+Z で戻せるようになります。包まずに SetNumber を
呼んだ場合、値は変わりますが履歴には載りません
(元に戻す・やり直す)。
保存できるようにする
reogrid-json から読み戻せるようにするには、型名を登録します。
// reogrid-json から復元できるようにする
CellTypeRegistry.Register("datepicker",
config => new DatePickerCellType(config.GetString("format") ?? "yyyy/MM/dd"));
WriteConfig で書き出した値が config として渡ってきます。
登録はアプリの起動時に一度行ってください。登録していないと、
読み込んだシートのそのセルはセル型なしになります。
別のやり方 — ドロップダウンで済ませる
選べる日付が限られている場合(例: 締め日の候補)は、セル型を作らずに
DropdownCellType で十分です。
ws.SetCellType(1, 2, new DropdownCellType(["2026-08-31", "2026-09-30", "2026-10-31"]));
OnActivate から CellTypeClickResult.OpenDropdown(options) を返せば、
独自のセル型からドロップダウンを開くこともできます。ポップアップの表示は
コントロールが行うため、ホスト側のコードは要りません。