検索は FindEngine(unvell.ReoGrid.Core.Search)が担います。UI 非依存なので、
コントロールを持たないヘッドレスのコードからもそのまま呼べます。
探す
using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Search;
var options = new FindOptions { Text = "未着手" };
// A1 の直後から順に探す。見つからなければ null(末尾で折り返す)
FindMatch? hit = FindEngine.FindNext(ws, new CellPosition(0, 0), options);
if (hit is FindMatch m)
{
_ = $"{m.Position.ToAddress()}: {m.Text}";
// 続けて次を探すときは、直前の位置を渡す
FindMatch? next = FindEngine.FindNext(ws, m.Position, options);
_ = next;
}
// 逆方向
FindMatch? back = FindEngine.FindPrevious(ws, new CellPosition(100, 0), options);
_ = back;
FindNext は渡した位置の次から探し、末尾まで行ったら折り返します。
WholeWorkbook を立てているとシートも跨いで折り返します。見つからなければ null です。
FindMatch は Sheet / Row / Col / Text を持つ読み取り専用の record struct で、
Position から CellPosition が取れます。Text は照合に使われた文字列であり、
LookIn の指定によって中身が変わります。
全件を列挙する
var options = new FindOptions { Text = "未着手" };
// 全件を列挙する(走査は実在するセルのみ)
foreach (FindMatch m in FindEngine.Enumerate(ws, options))
{
_ = $"{m.Sheet.Name}!{m.Position.ToAddress()}";
}
// 件数だけ知りたいとき
int count = FindEngine.Count(ws, options);
_ = count;
探し方を変える
// セル全体が一致するものだけ、大文字小文字を区別して
var exact = new FindOptions
{
Text = "TODO",
MatchCase = true,
WholeCell = true,
};
// 正規表現。パターンが壊れていれば IsValid が false(例外は投げない)
var regex = new FindOptions { Text = @"^\d{4}-\d{2}$", UseRegex = true };
if (!regex.IsValid)
{
// 入力欄にエラーを出す、など
}
// 探す対象を選ぶ。既定は Values(数値書式が作った表示テキスト)
var inFormulas = new FindOptions { Text = "SUM", LookIn = SearchIn.Formulas };
var inNotes = new FindOptions { Text = "要確認", LookIn = SearchIn.Comments };
// 範囲を限定する/列方向に走査する/ブック全体に広げる
var scoped = new FindOptions
{
Text = "未着手",
Within = RangePosition.Parse("B2:D500"),
Order = SearchOrder.ByColumns,
WholeWorkbook = true,
};
_ = (exact, inFormulas, inNotes, scoped);
FindOptions は record(不変)です。ダイアログが 1 つ持って各呼び出しに渡せば、
検索・再検索・置換で条件がずれません。
| メンバー | 既定 | 内容 |
|---|---|---|
Text | "" | 探す文字列。UseRegex なら正規表現 |
MatchCase | false | 大文字小文字を区別する |
WholeCell | false | セル全体が一致するものだけ(Excel の「セル内容が完全に同一であるものを検索する」) |
UseRegex | false | Text を .NET の正規表現として扱う |
LookIn | Values | Values / Formulas / Comments |
Order | ByRows | 行方向・列方向 |
Within | null | 範囲を限定する。null はシート全体 |
WholeWorkbook | false | 他のシートまで広げる |
IncludeHidden | false | 非表示の行・列も対象にする |
IsValid は、UseRegex を立てたときにパターンがコンパイルできるかを返します。
壊れたパターンで例外は投げません — 走査の途中で落ちるより、入力欄に印を付けられるほうが実用的だからです。
LookIn の 3 つ
Values(既定) — セルの表示テキスト。数値書式が作った文字列を見るので、#,##0が付いた1234は1,234として一致します。Formulas— セルの入力文字列。数式なら先頭の=を含み、それ以外は生の値です。Comments— セルのメモの本文。
置換する
var options = new FindOptions { Text = "未着手", LookIn = SearchIn.Formulas };
// 1 セルだけ置換する。対象セルが一致しなければ false
bool replaced = FindEngine.ReplaceAt(ws, 1, 2, options, "着手済み");
_ = replaced;
// シート全体。Undo は 1 ステップ、再計算も 1 回にまとまる
int n = FindEngine.ReplaceAll(ws, options, "着手済み");
_ = n;
// ブック全体(シートごとに 1 ステップの Undo)
int total = FindEngine.ReplaceAll(wb, options, "着手済み");
_ = total;
置換はセルの入力文字列に対して行われます。 これはセルへ書き戻せる唯一のテキストで、 Excel が置換を数式にしか適用しないのと同じ理由です。
結果として、表示テキストにしか無い一致は検索では見つかるが置換はできません。
#,##0 が付いた 1234 を Values で探すと 1,234 の , に当たりますが、その , は
セルのどこにも書かれていないため、ReplaceAt は false を返して何もしません。
桁区切りを消したいなら数値書式を変えてください(数値書式)。
ReplaceAll はシート単位で 1 ステップの Undo にまとまり、再計算も 1 回です。
ブック版はシートごとに 1 ステップになります(Undo 履歴がシート単位のため)。
走査のコスト
検索は実在するセルだけを、ストアの 256 行チャンク単位で読みます。 104 万行のシートでも、コストは中身の量にしか比例しません。
数式セルは事前にソートされたキー列から合流します(結果が空の数式は値エントリを残さないため、
値側の走査だけでは拾えません)。非表示の行・列は IncludeHidden を立てない限り飛ばします。
コントロール側の振る舞い
- WinForms / WPF にはモードレスの検索ダイアログがあります。Ctrl+F が検索、 Ctrl+H が置換、F3 / Shift+F3 で次・前を再検索します。
control.FindNext(options)/control.ReplaceAll(options, replacement)を直接呼べば、 ダイアログを出さずに同じ操作ができます。ヒットしたセルには選択が移動します。control.ShowFindDialog(replace: true)で置換タブを開いた状態にできます。
Studio で試す
Edit ▸ Find…(Ctrl+F)/Edit ▸ Replace…(Ctrl+H)/Edit ▸ Find Next(F3)。