条件付き書式は、なくなって初めてありがたみがわかる Excel 機能の代表格です。マイナスの値を赤で表示する、ステータスが「期限超過」の行をハイライトする、進捗率の列に緑のデータバーを出す。これを .NET アプリに組み込むだけで、画面が「どこを見ればよいか」をユーザーに語りかけてくれるようになります。
本記事では、条件付き書式が裏側でどう動いているのか、スケールするパターンとそうでないパターンは何か、を順を追って解説します。
メンタルモデル
条件付き書式ルールは、3 つの要素から成り立っています。
- 適用範囲 —
A1:A1000、シート全体、など。 - 条件 — セルごとに真偽を返す数式または比較。
- スタイル — 条件が真のときに適用するもの(文字色、塗りつぶし、罫線、データバー、アイコン)。
Excel および .xlsx でラウンドトリップ可能なライブラリでは、ルールはシートに 1 度だけ保存される のがポイントです。セルごとにコピーされるわけではありません。レンダラはセルを描画するたびにオンザフライで評価します。これがパフォーマンスに大きく影響します(後述)。
最初のルール
定番のケース: しきい値を超えた値をハイライトします。
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 に戻せばハイライトが消えます。イベント配線は不要です。
行全体のハイライト
よくある要望: F 列が "期限超過" のときに、F セルだけでなく行全体 を色付けしたい、というものです。
ポイントは、Excel と同じように 列を絶対参照、行を相対参照 にして条件式を書くことです。
sheet.ConditionalStyles.Add(new Rule(
"$F1 = \"期限超過\"", // $F = 列固定、1 = 行は変動
"A1:Z1000",
new WorksheetRangeStyle
{
Flag = PlainStyleFlag.BackColor,
BackColor = SolidColor.LightSalmon,
}));
ルールは 行全体の範囲 A1:Z1000 に対して適用します。各セルについて、エンジンは数式中の行番号を当該セルの行に置き換えて $F<row> = "期限超過" を評価します。1 つのルールですべての行をカバー できます。
データバーとアイコンセット
データバーや 3 色スケールも仕組みは同じルールで、スタイルのペイロードが異なるだけです。
sheet.ConditionalStyles.Add(new DataBarRule(
"B2:B500",
new DataBarStyle
{
FillColor = SolidColor.SteelBlue,
MinType = DataBarValueType.Min,
MaxType = DataBarValueType.Max,
}));
アイコンセットも同様に、アイコンセットを選び、しきい値を設定し、範囲を限定するだけです。
パフォーマンス: ハマりやすい落とし穴
「すべてのルールを、すべてのセルに対して、すべての描画タイミングで評価する」という素朴な実装は、小さな範囲なら十分速いですが、大きな範囲では破綻します。経験則を 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.ClearConditionalStyles();
// あるセルが現在いずれかのルールで書式設定されているかを確認
bool isFormatted = sheet.HasConditionalStyle(new CellPosition("A37"));
HasConditionalStyle は、「このセルは本当に赤いのか、それともルールのせいで赤く見えているのか?」を判別するときに便利です。コピー&ペーストの挙動を実装するときや、ユーザー向けにスタイルインスペクタを作るときによく使います。
どんなときに使うべきか
条件付き書式が向いているのは、概ね次のようなケースです。
- ユーザーがデータを スキャン して把握したい(分析ではなく一目で確認したい)
- 視覚的なヒントが データそのもの に依存する(UI 状態ではない)
- Excel と同じ振る舞い を自社アプリでも維持したい
逆に、ハイライトが 選択・ホバー・アプリのレベルの状態 に依存する場合は、条件付き書式の出番ではありません。それは UI の関心事であり、ワークブックではなくコントロールの描画ロジックに置くべきです。
