条件付き書式は、なくなって初めてありがたみがわかる Excel 機能の代表格です。マイナスの値を赤で表示する、ステータスが「期限超過」の行をハイライトする、進捗率の列に緑のデータバーを出す。これを .NET アプリに組み込むだけで、画面が「どこを見ればよいか」をユーザーに語りかけてくれるようになります。

本記事では、条件付き書式が裏側でどう動いているのか、スケールするパターンとそうでないパターンは何か、を順を追って解説します。


メンタルモデル

条件付き書式ルールは、3 つの要素から成り立っています。

  1. 適用範囲A1:A1000、シート全体、など。
  2. 条件 — セルごとに真偽を返す数式または比較。
  3. スタイル — 条件が真のときに適用するもの(文字色、塗りつぶし、罫線、データバー、アイコン)。

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 つ挙げます。

  1. ルールは、列全体ではなく実際のデータ範囲に適用するA:A をスコープにすると、Excel が持ちうる全 1,048,576 セルに対して評価が走ります。A1:A1000 のように絞り込めば、データのある場所だけを見るようになります。
  2. 条件自体に高コストな数式を使わないVLOOKUP(...) をセル単位 × 再描画ごとに評価するルールは、もはやルールではなくベンチマークです。
  3. 条件付き書式が掛かっている範囲への一括投入に注意。1 件挿入するたびに重なるルールが再評価される場合、5,000 セルを 1 つずつ入れるのは最悪ケースです。ReoGrid 4.4 ではこの経路を専用に最適化し、1 つの条件付き書式ルール下での一括ロードが 4.3.13 比で約 11,700 倍速くなりました。詳しくは v4.4 リリースノート の実測値をご覧ください。
  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 の関心事であり、ワークブックではなくコントロールの描画ロジックに置くべきです。


さらに読む