数式計算用のカスタマイズした関数

ReoGrid は Excel と同様の数式計算機能を提供しています。数式計算について詳しくは、「数式計算」をご覧ください。数式計算における関数は、現時点では 50 個以上の内蔵関数を利用できます。関数一覧は、「数式計算用関数一覧」をご覧ください。内蔵した関数以外に、カスタマイズした関数も作成できます。この文書ではカスタマイズした関数について説明します。

カスタマイズした関数を作成

カスタマイズした関数を作成するには、静的なクラス FormulaExtension を利用します。このクラスは以下の命名空間にあります。

unvell.ReoGrid.Formula

例として、関数の入力値をすべて大文字に変換する関数を作成します。

unvell.ReoGrid.Formula.FormulaExtension.CustomFunctions["myUpper"] =
  (cell, args) => {
    // パラメータ配列の数を確認
    if (args.Length == 0)
    {
      // 数が0の場合、nullを返す
      return null;
    }

    // 一つ目のパラメータを取得して大文字の文字列に変換
    return Convert.ToString(args[0]).ToUpper();
  };

上記で作成したカスタマイズした関数を利用します。セルB2に=myUpper("hello")計算式を設定します。

169

編集が終了した後、セルのデータが関数の入力値の「hello」から「HELLO」に変わりました。

170

カスタマイズした関数の定義

FormulaExtension の CustomFunctions プロパティは以下のように定義されています。

Dictionary<string name, Func<ReoGridCell cell, object[] args, object>> CustomFunctions

このプロパティは、コールバック関数の配列となります。コールバック関数のパラメータは以下の通りです。

  1. cell 計算式が格納されたセルのインスタンスと関数を呼び出す際のパラメータ配列
  2. args 計算式が呼び出される際のパラメータ配列
  3. object 関数の返却値

最も簡単な関数のサンプル(Lambda関数)

C#:

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) => {
  return null;
};

VB.NET:

FormulaExtension.CustomFunctions("myFunc") = Function(cell, args)
    Return Nothing
End Function

最も簡単な関数のサンプル(Delegate関数)

C#:

static object MyFunc(ReoGridCell cell, params object[] arguments)
{
  return null;
}

Delegate 関数を CustomFunctions に追加する方法は以下の通りです。

FormulaExtension.CustomFunctions["myFunc"] = MyFunc;

VB.NET:

Shared Function MyFunc(cell As ReoGridCell, ParamArray args As Object()) As Object
  Return Nothing
End Function

Delegate 関数を CustomFunctions に追加する方法は以下の通りです。

FormulaExtension.CustomFunctions("myFunc") = AddressOf MyFunc

コールバック関数のセルインスタンス

一つ目のパラメータであるセルのインスタンスは、カスタマイズした関数を利用する計算式が格納されたセルのインスタンスです。

worksheet["A1"] = "=myFunc()";

A1セルにmyFunc関数を利用する計算式があります。この計算式が実行される場合、コールバック関数のcellパラメータがA1セルのインスタンスになります。関数の中で取得するコードは以下の通りです。

static object MyFunc(ReoGridCell cell, params object[] arguments)
{
  MessageBox.Show(cell.Position.ToAddress());   // output: A1
  return null;
}

コールバック関数の中でワークシートのインスタンスを取得

セルのインスタンスからワークシートのインスタンスとワークブックのインスタンスを取得できます。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) => 
{
  // ワークシートを取得
  var worksheet = cell.Worksheet; 
  // ワークブックを取得
  var workbook = worksheet.Workbook;

  ...
  return null;
};

関数の利用範囲を制限

関数を特定のワークブックのみで利用できるように制限したい場合、ワークブックのインスタンスを取得して当該ワークブックであるかを確認します。利用不可なワークブックの場合、nullを返すことで関数の利用を制限できます。

var worksheet = cell.Worksheet; 
var workbook = worksheet.Workbook;

if (workbook != validWorkbook) 
{
  // 有効なワークブックではない
  return null;
}

コールバック関数のパラメータ配列

カスタマイズした関数が呼び出される際、計算式にある関数の呼び出し元のパラメータ配列が args になります。

worksheet[“B2”] = “=myFunc(A1, A2:B3, 100)”;

B2セルの計算式には、関数myFuncを呼び出すために三つのパラメータがあります。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) => 
{
  // argsの内容は
  //   args[0] = CellPosition(A1)
  //   args[1] = RangePosition(A2:B3)
  //   args[2] = 定数100

  // args配列のサイズを確認
  if (args.Length < 2)
  {
    // パラメータが二つ未満の場合、nullを返す
    return null;
  }

  return 1;
};

値のパラメータを処理

ReoGrid 内部では常に数値のデータを .NET の double 型として取り扱っています。int 型、long 型、float 型などをセルのデータに設定した場合、自動的に double 型に転換されます。数値のみを含む文字列をセルに設定した場合、文字列から数値を抽出してデータとして設定されます。以下は数式計算における利用する型の一覧です。

数値 double 型
ブーリアン型 bool 型
文字列 string 型
日付時間 DateTime 型
セル参照 CellPosition 構造体
範囲参照 RangePosition 構造体
任意の型 object 型

定数

計算式にある数値、文字列、ブーリアンは、定数として取り扱います。

worksheet["A1"] = "=myFunc(10)";

関数myFuncに渡される数値10は定数です。定数は double 型です。カスタマイズした関数でこのパラメータを取得する際に、double であるかを確認して利用します。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) => 
{
  if (args.Length < 1 || !(args[0] is double))
  {
    // 1一つ目のパラメータは数値型であることが必要
    return null;
  }
  ...
};

変数

セルや範囲への参照は変数として取り扱います。例えばmyFunc関数を以下のように定義します。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) =>
{
  double value = (double)args[0];
  return value * 2;
};

myFunc関数を利用します。A1セルの参照を渡します。

sheet["A1"] = (int)10;
sheet["B1"] = "=myFunc(A1)";

A1セルにint型の10が設定されていますが、myFunc関数でdouble型として直接利用しても問題ありません。また、A1セルへ参照しているため、A1セルの値が変化したらmyFuncに渡されるパラメータも変化します。

参照型のパラメータを処理

セルの参照

計算式にセル参照があった場合、ReoGrid は自動的にセルの値を取得してカスタマイズした関数に渡します。サンプルは、上述の「変数」の部分をご覧ください。

範囲の参照

範囲の参照がパラメータとしてカスタマイズした関数に渡される場合、RangePosition構造体がカスタマイズした関数に渡されます。

worksheet["G2:K3"] = new object[] { 1, 2, 5, 7, 8, 10, 12, 15, 16, 19};
worksheet["L2"] = "=CountEvenNumber(G2:K3)";

CountEvenNumber 関数は指定した範囲から偶数の数をカウントします。定義は以下の通りです。

FormulaExtension.CustomFunctions["CountEvenNumber"] = (cell, args) =>
{
  if (args.Length < 1 || !(args[0] is RangePosition))
  {
    // 一つ目のパラメータが範囲の参照ではない場合処理を終了
    return null; 
  }

  RangePosition range = (RangePosition)args[0];

  int count = 0;

  // 範囲にあるすべてのセルを反復
  cell.Worksheet.IterateCells(range, (r, c, inCell) => {
    double value;

    // 数値型の値をセルから取得
    if (ReoGridCellUtility.TryGetNumberData(inCell.Data, out value) {
      if ((value % 2) == 0) {
        // 数値が偶数であれば+1
        count++;
      }
    }
   
    // trueを渡して反復を続ける
    return true;
  });

  return count;
};

セルの反復関数 IterateCells について詳しくは、「セルの反復」をご覧ください。

名前範囲の参照

セルの名前

名前範囲の参照先がセルの場合、セルへの参照として処理されます。カスタマイズした関数に命名範囲の名前を渡した場合、参照先のセルのデータが渡されます。

// 名前範囲name1を定義
worksheet.DefineNamedRange("name1", "A1");

// name1にデータを設定
sheet["name1"] = 20;

// B1セルにname1を参照する計算式を設定
sheet["B1"] = "=myFunc(name1)";

計算式が実行される場合、myFunc関数に渡されるパラメータは double 型の20です。

範囲の名前

名前範囲の参照先が範囲の場合、範囲ポジション RangePosition 構造体が渡されます。

// 名前範囲を定義
worksheet.DefineNamedRange("evenRange", "G2:K3");
// 名前範囲のデータを設定
worksheet["evenRange"] = new object[] { 1, 2, 4, 6, 9, 11, 13, 14, 15, 17 };

// eventRange名前を参照する計算式を設定
worksheet["I2"] = "=CountEvenNumber(evenRange)";

// 計算結果を確認
AssertSame(worksheet["I2"], 4);

結果は4です。範囲の参照を渡した場合と同じ結果となります。

計算式におけるカスタマイズした名前解析

ReoGridでは、セルや範囲への参照に名前を付けて定義できます。定義した名前は計算式の中で参照として利用できます。プログラムで任意の値を名前として定義したい場合、カスタマイズした名前プロバイダを利用します。

カスタマイズした名前プロバイダは FormulaExtension クラスの NameReferenceProvider プロパティです。このプロパティに .NET の関数を設定すると、計算式に利用された名前参照の解析ができます。

unvell.ReoGrid.Formula.FormulaExtension.NameReferenceProvider = 
  (cell, name) =>
  {
    if (name == "myName1")
      return 10;
    else if (name == "myName2")
      return 20;
    else
      return null;
  };

この例では、計算式に myName1 を含んだ箇所を、数値10に差し替えます。myName2 を含んだ箇所を数値20に差し替えます。

NameReferenceProvider プロパティの定義は以下の通りです。

Func<ReoGridCell cell, string name, object ret> NameReferenceProvider { get; set; }

myName1名前を参照する計算式は以下の通りです。

171

「Enter」を押すと結果が10となります。

172

myName2を利用する計算式です。

173

結果が20となります。

174

カスタマイズした名前解析を利用して数値定数を増やす

様々な分野のアプリケーションには、それぞれ独自の計算用定数があります。カスタマイズした名前解析を利用すれば、簡単に定数を増やすことができます。以下の例では、カスタマイズした定数PI、EとMyConstをサポートしています。

C#:

private object MathConstantProvider(ReoGridCell cell, string name)
{
  switch (name)
  {
    case "PI": return Math.PI;
    case "E": return Math.E;
    case "MyConst": return 1.23456;
    default: return null;
  }
}

// カスタマイズした名前解析を適用
FormulaExtension.NameReferenceProvider = MathConstantProvider;

VB.NET:

Shared Function MathConstantProvider(cell As ReoGridCell, name As String) As Object
  Select Case (name)
    Case "PI"
      Return Math.PI

    Case "E"
      Return Math.E

    Case "MyConst"
      Return 1.23456
  End Select

  Return Nothing
End Function

// カスタマイズした名前解析を適用
FormulaExtension.NameReferenceProvider = AddressOf MathConstantProvider

MyConst名前を利用する計算式は以下の通りです。

worksheet("A1") = "=MyConst"

結果:

181

関数とカスタマイズした名前解析を合わせて活用

カスタマイズした関数とカスタマイズした名前解析を合わせて利用できます。

FormulaExtension.NameReferenceProvider = (cell, name) =>
{
  switch (name)
  {
    case "Pi": return Math.PI;
    case "E": return Math.E;
    default: return null;
  }
};

Piを利用する計算式は以下の通りです。

worksheet["D3"] = "=Div100(Pi)";
AssertSame(worksheet["D3"], Math.PI / 100);

ReoScript スクリプト言語実行の拡張関数

ReoGrid の拡張機能版はスクリプト言語実行機能をサポートしています。ReoGrid は ReoScript (第三者ライブラリ)を利用してスクリプト言語実行機能を実現しています。スクリプト言語の文法は ECMAScript/JavaScript に準拠しています。スクリプトの利用によってワークブック、ワークシート、セル、セルのスタイル、罫線、書式などの制御ができ、製品の二次開発などに便利です。スクリプト実行機能について詳しくは、「スクリプト言語実行(準備中)」をご覧ください。

ReoScript ライブラリは標準的なスクリプトランタイム関数を提供しています。 ReoGrid はスプレッドシート制御の関数を提供しています。ReoGrid を利用するアプリケーションでは、スクリプトのためのカスタマイズした関数を作成できます。この文書ではスクリプト言語におけるカスタマイズした関数について説明します。

178

スクリプト言語用のカスタマイズした関数を作成

ワークブックに Srm というオブジェクトがあります。このオブジェクトはスクリプト言語実行のランタイムマシンです。Srm オブジェクトに追加した関数はスクリプトのどこでも利用できます。以下の例では sqrt 関数を追加します。

workbook.Srm["sqrt"] = new NativeFunctionObject("sqrt", (ctx, owner, args) =>
{
    if (args.Length < 1)
        return NaNValue.Value;
    else
        return Math.Sqrt(ScriptRunningMachine.GetDoubleValue(args[0], 0));
});

sqrt 関数を利用する計算式を設定します。

var worksheet = reoGridControl.CurrentWorksheet;

worksheet["B3"] = "=sqrt(400)";

結果:

44

セルのデータを編集すると計算式が確認できます。

43

このカスタマイズした関数 sqrt は計算式だけではなく、スクリプト言語でも呼び出されます。

スクリプト言語を実行してカスタマイズした関数を作成

スクリプト言語ではグローバルオブジェクト script が利用できます。このオブジェクトのメソッドとして追加した関数は、グローバル関数となり、スクリプト言語や計算式の中で呼び出すことができます。

カスタマイズした関数のメソッドは、ECMAScript/JavaScript 文法の無名関数、ReoScript のラムダ式のいずれかを設定すると作成できます。

reoGridControl.RunScript("script.myfunc = data => '[' + data + ']';");

myfunc 関数は、入力値の左側と右側に括弧を付けます。