条件付き書式は、なくなって初めてありがたみがわかる Excel 機能の代表格です。マイナスの値を赤で表示する、ステータスが「期限超過」の行をハイライトする、進捗率の列に緑のデータバーを出す。これを .NET アプリに組み込むだけで、画面が「どこを見ればよいか」をユーザーに語りかけてくれるようになります。
本記事では、条件付き書式が裏側でどう動いているのか、スケールするパターンとそうでないパターンは何か、を順を追って解説します。
メンタルモデル
条件付き書式ルールは、3 つの要素から成り立っています。
- 適用範囲 —
A1:A1000、シート全体、など。 - 条件 — セルごとに真偽を返す数式または比較。
- スタイル — 条件が真のときに適用するもの(文字色、塗りつぶし、罫線、データバー、アイコン)。
Excel および .xlsx でラウンドトリップ可能なライブラリでは、ルールはシートに 1 度だけ保存される のがポイントです。セルごとにコピーされるわけではありません。レンダラはセルを描画するたびにオンザフライで評価します。これがパフォーマンスに大きく影響します(後述)。
最初のルール
定番のケース: しきい値を超えた値をハイライトします。
using unvell.ReoGrid.ConditionalStyle; // Rule
sheet.ConditionalStyles.Add(new Rule(
"THIS > 100", // 条件 (THIS = セル値)
"A1:A1000", // 範囲
new WorksheetRangeStyle
{
Flag = PlainStyleFlag.TextColor | PlainStyleFlag.BackColor,
TextColor = SolidColor.White,
BackColor = SolidColor.Red,
}));
これで A1:A1000 の範囲にあるセルのうち、値が 100 を超えるものは白文字 + 赤背景で表示されるようになります。A37 = 250 を代入すれば自動で再描画され、50 に戻せばハイライトが消えます。イベント配線は不要です。
ルールから何が見えるか — 行全体のハイライトができない理由
ルールの数式は、適用範囲の各セルについて 1 回ずつ評価され、THIS がそのセルの値になります。V4 のセルごとの文脈はこれだけです。つまり、数式に書いたセル番地は、評価対象のセルに合わせてズレません。
これが効いてくるのが、いちばんよく求められる「F 列が "期限超過" の行を丸ごと色付けしたい」というケースです。Excel なら列を絶対参照・行を相対参照にして書きます。
// Excel の定石。ただし ReoGrid V4 では 1 行も色が付かない。
sheet.ConditionalStyles.Add(new Rule(
"$F1 = \"期限超過\"",
"A1:Z1000",
new WorksheetRangeStyle { Flag = PlainStyleFlag.BackColor, BackColor = SolidColor.LightCoral }));
$F1 は範囲内のどのセルを評価するときも $F1 のままなので、ルールの成否は F1 という 1 つのセルだけで決まり、範囲全体が色付くか、まったく色付かないかのどちらかになります(reogrid-v4#15)。V4 では、ルールは「そのセル自身についての条件」に限り、行の色付けは普通にスタイルを設定して行います。
// V4 での行ハイライト: ステータスが変わったときに行にスタイルを設定する
var overdue = new WorksheetRangeStyle
{
Flag = PlainStyleFlag.BackColor,
BackColor = SolidColor.LightCoral,
};
for (int r = 1; r <= lastRow; r++)
{
if ((sheet[r, 5] as string) == "期限超過")
sheet.SetRangeStyles(new RangePosition(r, 0, 1, 26), overdue);
}
ReoGrid V5 は OOXML のルールセット(expression ルールを含む)を実装しており、そちらでは参照がセルごとにズレるため、上の Excel の定石がそのまま動きます。
データバーとアイコンセット
ReoGrid V4 のルールは 1 種類だけです — 数式ひとつと WorksheetRangeStyle ひとつ。DataBarRule もカラースケールもアイコンセットもありません。これらは ReoGrid V5 で OOXML のルールセットとして実装され、.xlsx とのラウンドトリップにも対応しました。
V4 では、データバーはルールではなく描画の仕事になります。カスタムセルボディならセルの文字の背面にバーを描けます。十数行で書けて、色も形も思いどおりです。
public class ProgressBarCell : CellBody
{
public override void OnPaint(CellDrawingContext dc)
{
double value = Math.Clamp(Cell.Worksheet.GetCellData<double>(Cell.Position), 0, 1);
var bounds = GetBodyBounds();
int barWidth = (int)(value * bounds.Width);
if (barWidth > 0)
{
var rect = new Rectangle(bounds.Left, bounds.Top + 1, barWidth, bounds.Height - 2);
dc.Graphics.FillRectangleLinear(SolidColor.SteelBlue, SolidColor.SkyBlue, 90f, rect);
}
dc.DrawCellText(); // 書式適用後の値をバーの上に描く
}
}
.xlsx を使う場合の違いは 1 点です。ルールは Excel にラウンドトリップしますが、CellBody はしません。描いているのはあなたのコードなので、保存されたファイルにあるのはただのセルです。
パフォーマンス: ハマりやすい落とし穴
「すべてのルールを、すべてのセルに対して、すべての描画タイミングで評価する」という素朴な実装は、小さな範囲なら十分速いですが、大きな範囲では破綻します。経験則を 4 つ挙げます。
- ルールは、列全体ではなく実際のデータ範囲に適用する。
A:Aをスコープにすると、Excel が持ちうる全 1,048,576 セルに対して評価が走ります。A1:A1000のように絞り込めば、データのある場所だけを見るようになります。 - 条件自体に高コストな数式を使わない。
VLOOKUP(...)をセル単位 × 再描画ごとに評価するルールは、もはやルールではなくベンチマークです。 - 条件付き書式が掛かっている範囲への一括投入に注意。1 件挿入するたびに重なるルールが再評価される場合、5,000 セルを 1 つずつ入れるのは最悪ケースです。ReoGrid 4.4 ではこの経路を専用に最適化し、1 つの条件付き書式ルール下での一括ロードが 4.3.13 比で約 11,700 倍速くなりました。詳しくは v4.4 リリースノート の実測値をご覧ください。
- ルール変更はバッチ化する。10 個のルールをループで追加するなら、再描画を停止し、追加し終えてから再開する。1 ルールごとに再描画してはいけません。
スケールするパターンは、「範囲を絞り、条件を軽くし、既にルールが適用されている範囲には一括 API(SetRangeData)を使う」というものです。
.xlsx へのラウンドトリップ
ユーザーが Excel に書き出す場合、書式は 無事に往復してほしい ところです。互換性で押さえておくべき点が 2 つあります。
- 標準ルールタイプを使う。「セル値が X と比較」「数式が真」「上位/下位 N」「重複」「データバー」「カラースケール」「アイコンセット」は OpenXML 仕様に含まれており、綺麗にラウンドトリップします。
- 個別の独自スタイル(特定ストップを持つグラデーション、プログラム指定したフォント)はラウンドトリップしますが、ルールのセマンティクスが OpenXML で表現可能 であることが前提です。.NET のユーザー定義関数を呼ぶ条件は Excel に対応するものが無いため、書き出しでは生き残りません。
安全な目安: 「Excel の『新しいルール』ダイアログだけで(VBA を書かずに)記述できるか?」です。これに該当するルールはラウンドトリップします。
ルールの削除
実装に必要となる操作は 2 つあります。
// シート上のすべてのルールを削除
sheet.ConditionalStyles.Clear();
// あるセルが現在いずれかのルールで書式設定されているかを確認
bool isFormatted = sheet.HasConditionalStyle(new CellPosition("A37"));
HasConditionalStyle は、「このセルは本当に赤いのか、それともルールのせいで赤く見えているのか?」を判別するときに便利です。コピー&ペーストの挙動を実装するときや、ユーザー向けにスタイルインスペクタを作るときによく使います。
どんなときに使うべきか
条件付き書式が向いているのは、概ね次のようなケースです。
- ユーザーがデータを スキャン して把握したい(分析ではなく一目で確認したい)
- 視覚的なヒントが データそのもの に依存する(UI 状態ではない)
- Excel と同じ振る舞い を自社アプリでも維持したい
逆に、ハイライトが 選択・ホバー・アプリのレベルの状態 に依存する場合は、条件付き書式の出番ではありません。それは UI の関心事であり、ワークブックではなくコントロールの描画ロジックに置くべきです。