「税込価格の計算」「自社ルールの端数処理」「商品コードからの単価引き当て」——こうした業務ロジックを、画面のあちこちにコピペして書いていないでしょうか。ロジックが増えるたびにコードが散らばり、仕様変更のたびに修正漏れが発生する、よくある悩みです。
ReoGrid は WinForms / WPF 向けの .NET スプレッドシートコンポーネントで、SUM や VLOOKUP といった 200 以上の Excel 互換関数を標準搭載しています。さらに、自社の業務ロジックをカスタム関数として登録しておけば、利用者がセルに =TAXIN(B2) と書くだけでそのロジックを呼び出せます。
この記事では、C# でカスタム関数を登録する方法を、軽減税率対応の税込計算を例に具体的に解説します。
カスタム関数の登録は実質1行
カスタム関数は unvell.ReoGrid.Formula.FormulaExtension.CustomFunctions という辞書に、関数名をキーにしてデリゲートを登録するだけです。
using unvell.ReoGrid;
using unvell.ReoGrid.Formula;
// 文字列を大文字に変換する独自関数 UPPER2 を登録
FormulaExtension.CustomFunctions["UPPER2"] = (cell, args) =>
{
if (args.Length == 0) return null;
return Convert.ToString(args[0]).ToUpper();
};
これだけで、どのワークシートでも次のように使えます。
worksheet["A1"] = "hello";
worksheet["B1"] = "=UPPER2(A1)"; // → "HELLO"
登録するデリゲートの型は Func<Cell, object[], object> です。引数の意味は次のとおりです。
| 引数 | 型 | 内容 |
|---|---|---|
cell |
Cell |
数式が入力されているセル(位置や所属シートを参照できる) |
args |
object[] |
評価済みの引数の値。=UPPER2(A1) なら args[0] に A1 の値が入る |
| 戻り値 | object |
セルに表示される計算結果 |
ポイントは、args に渡ってくるのが「セル参照」ではなく評価済みの値そのものだということです。A1 の中身が文字列なら string、数値なら double が入ってきます。セル参照の解決はエンジン側が済ませてくれるので、自社ロジックの実装に集中できます。
引数の受け取りと型・未入力の扱い
args の中身は object なので、業務ロジックでは型変換と未入力チェックを最初に行うのが定石です。
FormulaExtension.CustomFunctions["TAXIN"] = (cell, args) =>
{
// 引数なし・空セルは 0 とみなす
if (args.Length == 0 || args[0] == null) return 0d;
// 文字列・数値どちらで来ても double に寄せる
if (!double.TryParse(Convert.ToString(args[0]), out double price))
{
return "#VALUE!"; // 数値化できなければエラー表示
}
return Math.Floor(price * 1.10); // 10% 税込・端数切り捨て
};
-
args[0] == null… 参照先が空セルのとき -
double.TryParse… セルに全角数字や文字列が入っていても落ちないように -
"#VALUE!"を返す … Excel と同じ感覚でエラー文字列をそのまま表示
戻り値に double を返せば数値セルとして、string を返せば文字列セルとして扱われます。
実践:軽減税率に対応した税込関数
実務では消費税率が一律ではありません。第2引数で税率(区分)を切り替えられるようにしてみます。=TAXIN(B2) のように省略したときは標準税率 10%、=TAXIN(B2, 8) のように指定したときはその税率で計算します。
FormulaExtension.CustomFunctions["TAXIN"] = (cell, args) =>
{
if (args.Length == 0 || args[0] == null) return 0d;
if (!double.TryParse(Convert.ToString(args[0]), out double price))
return "#VALUE!";
// 第2引数(税率%)省略時は 10%
double rate = 10d;
if (args.Length >= 2 && args[1] != null)
{
double.TryParse(Convert.ToString(args[1]), out rate);
}
return Math.Floor(price * (1 + rate / 100d));
};
シート側はこう書けます。
worksheet["A1"] = "商品";
worksheet["B1"] = "本体価格";
worksheet["C1"] = "税込";
worksheet["A2"] = "お弁当(軽減税率)";
worksheet["B2"] = 980;
worksheet["C2"] = "=TAXIN(B2, 8)"; // → 1058
worksheet["A3"] = "文房具";
worksheet["B3"] = 1200;
worksheet["C3"] = "=TAXIN(B3)"; // → 1320(標準10%)
税率や端数処理のルールが変わっても、修正するのは登録した関数1か所だけです。シートに散らばった数式を直して回る必要はありません。
第1引数の cell を使えば、計算元セルの位置に応じて挙動を変えることもできます。
FormulaExtension.CustomFunctions["WHEREAMI"] = (cell, args) =>
{
// 例: "Sheet1!C3" のように自分の位置を返す
return $"{cell.Worksheet.Name}!{cell.Address}";
};
もう一歩:複数の値をまとめて扱う
引数は可変長なので、複数の値を受け取って独自に集計する関数も作れます。標準の SUM では表現しづらい「自社ルールの加重平均」などに便利です。
// 重み付き平均: =WAVG(値1, 重み1, 値2, 重み2, ...)
FormulaExtension.CustomFunctions["WAVG"] = (cell, args) =>
{
double sum = 0, weight = 0;
for (int i = 0; i + 1 < args.Length; i += 2)
{
double v = Convert.ToDouble(args[i]);
double w = Convert.ToDouble(args[i + 1]);
sum += v * w;
weight += w;
}
return weight == 0 ? 0d : sum / weight;
};
worksheet["A1"] = "=WAVG(80, 3, 60, 1)"; // → 75((80*3 + 60*1) / 4)
標準関数との共存と注意点
カスタム関数を実務に組み込むときに押さえておきたいポイントです。
-
標準関数を上書きしない名前にする
CustomFunctionsの参照は、標準の 200+ 関数を評価したあとに行われます。SUMやIFといった既存名と衝突しない、自社プレフィックス付きの名前(ACME_TAXINなど)にしておくと安全です。 -
関数名は大文字で登録・大文字で使う
Excel の慣習に合わせ、関数名はTAXINのように大文字で登録し、数式でも大文字で記述するのが確実です。登録したキーと数式中の名前が一致したときに呼び出されます。 -
登録はアプリ起動時に一度だけ
CustomFunctionsは静的(アプリ全体で共有)です。フォームの初期化処理やProgram.Mainで一度登録すれば、すべてのワークブック・ワークシートで使えます。 -
再計算のタイミング
参照先セルの値が変われば、依存する数式は自動で再計算されます。外部状態(DBの単価マスタなど)に依存するロジックを書く場合は、値が変わったときにworksheet.Recalculate()を呼ぶと明示的に再評価できます。
まとめ
ReoGrid のカスタム関数は、FormulaExtension.CustomFunctions["関数名"] = (cell, args) => ... を1行書くだけで登録できます。
-
argsには評価済みの値が渡るので、セル参照の解決を気にせずロジックに集中できる - 税込計算・端数処理・自社マスタ引き当てなどを「Excel数式」として利用者に開放できる
- ロジックの修正は登録した1か所で完結し、シートに散らばらない
業務アプリの「計算ルール」をスプレッドシート側に寄せることで、コードと利用者の両方がシンプルになります。WinForms / WPF どちらでも同じ API で動きます。
ReoGrid は無料で評価でき、商用利用にはライセンスをご用意しています。導入のご相談はお問い合わせフォームからどうぞ。
- 公式サイト: https://reogrid.net/jp
- 価格・購入: https://reogrid.net/jp/purchase
- お問い合わせ: https://reogrid.net/jp/contact
