添加自定义公式函数

要添加自定义函数,请使用 FormulaExtension 类。以下示例添加了一个将给定字符串转换为大写的函数。

unvell.ReoGrid.Formula.FormulaExtension.CustomFunctions["myUpper"] =
  (cell, args) =>
  {
    if (args.Length == 0)
    {
      // 此函数需要至少一个参数
      return null;
    }

    return Convert.ToString(args[0]).ToUpper();
  };

输入公式 =myUpper("hello") 来测试此函数:169

按 Enter 键,单元格值将被转换为大写。170

关于 FormulaExtension

FormulaExtension 是用于在公式计算过程中添加扩展的接口。

在包含脚本执行功能的 ReoGrid 扩展版本中,公式扩展可以使用 ReoScript 功能实现。对于使用标准版 ReoGrid 的应用程序,FormulaExtension 接口是实现公式扩展功能的更简单方式。

177

CustomFunctions 属性定义如下:

Dictionary<string, Func<ReoGridCell, object[], object>> CustomFunctions

最简单的函数示例

C#:

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) => {
  // 函数体写在这里
  return null;
};

VB.NET:

FormulaExtension.CustomFunctions("myFunc") = Function(cell, args)
    ' 函数体写在这里
    Return Nothing
End Function

使用委托函数

也可以使用常规委托函数而非 Lambda 表达式。定义函数如下:

C#

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

将此静态方法作为自定义函数在公式中使用:

VB.NET

FormulaExtension.CustomFunctions["myFunc"] = MyFunc;

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

将此共享方法作为自定义函数在公式中使用:

FormulaExtension.CustomFunctions("myFunc") = AddressOf MyFunc

单元格实例

第一个参数是拥有公式的单元格实例。此函数从公式内部调用。例如:

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

单元格 A1 有一个调用自定义函数 “myFunc” 的公式。上面定义的 .NET 函数体将被调用,cell 参数将是 A1 单元格。

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

从函数中获取工作表实例

通过单元格实例的 Worksheet 属性获取工作表实例,通过工作表实例的 Workbook 属性获取工作簿。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) =>
{
  var worksheet = cell.Worksheet;
  var workbook = worksheet.Workbook;

  ...

  return null;
};

将函数作用域限制在特定的工作簿或工作表中:

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

  if (workbook != myWorkbook)
  {
    // 不在有效的工作簿作用域内
    return null;
  }
}

获取函数参数

第二个参数包含从公式调用方传入的参数。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) =>
{
  if (args.Length < 2)
  {
    // 此函数需要至少两个参数
    return null;
  }

  return null;
};

公式中的数据类型

以下数据类型在公式计算过程中使用:

  • Number(int、long、short、float、double)
  • String(string 和 StringBuffer)
  • Boolean
  • DateTime
  • Object

ReoGrid 在公式计算中始终使用 double 作为数值类型。在将值传入函数之前,ReoGrid 会执行以下转换:

  • int、long、short 和 float 转换为 double
  • string 和 StringBuffer 对象转换为 string

数值常量

公式中使用的数值常量被初始化为 double,例如:

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

数字 10 被解析并以 double 形式存储在内存中。参数数组中的第一个元素将是 double 类型。

FormulaExtension.CustomFunctions["myFunc"] = (cell, args) =>
{
  if (args.Length < 1 || !(args[0] is double))
  {
    // 此函数需要一个 double 值
    return null;
  }
  ...
};

数值变量

当数值来自单元格时,它会被转换为 double。创建以下函数:

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

然后在公式中使用:

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

不会出现数据类型转换错误。(但请注意参数长度。)

函数中的引用

单元格引用

ReoGrid 会自动从公式中使用的引用单元格获取值,如”数值变量”示例所示。

范围引用

如果公式中使用了范围字面量,ReoGrid 会将 RangePosition 结构体传入函数。例如:

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

公式中使用了范围引用 (G2:K3);必须在自定义函数体内手动处理。以下示例函数计算给定范围内的偶数数量:

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) {
        count++;
      }
    }

    // 继续遍历
    return true;
  });

  return count;
};

名称引用

名称指向单元格

如果名称引用指向一个单元格,则会自动获取该单元格的值并传入函数。

worksheet.DefineNamedRange("r1", "A1");
sheet["r1"] = (int)20;
sheet["B1"] = "=myFunc(r1)";

名称指向范围

如果公式中使用了指向范围的名称引用,ReoGrid 会先通过名称查找范围,然后传入 RangePosition 结构体,处理方式与范围引用相同。

worksheet.DefineNamedRange("evenRange", "G2:K3");
worksheet["evenRange"] = new object[] { 1, 2, 4, 6, 9, 11, 13, 14, 15, 17 };
worksheet["I2"] = "=CountEvenNumber(evenRange)";
AssertSame(worksheet["I2"], 4);

自定义名称引用提供程序

可以添加由 .NET 代码提供的在公式中使用的常量值。以下示例添加了两个返回不同常量值的名称:

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

NameReferenceProvider 定义如下:

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

在单元格中输入 =myName1 来测试名称 1:171

名称返回值 10:172

在单元格中输入 =myName2173

名称返回值 20:174

添加数学常量

通过使用自定义名称引用提供程序,还可以定义数学常量。以下代码定义了一个常量提供程序:

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

然后在公式中使用常量:

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 语言自定义函数

当启用 ReoScript 模块扩展时,可以使用 ReoScript 语言来处理单元格数据、样式、边框、数据格式等——类似于 Excel 中的 VBA。ReoScript 支持脚本语言的 .NET 扩展,包括从脚本调用 .NET 函数。

ReoGrid 支持调用 ReoScript 自定义扩展函数:

注意: ReoScript 语言扩展仅在完整发行包中可用。 178

在 C# 中添加 ReoScript 函数

添加自定义函数以扩展控件:

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));
});

测试:

var worksheet = workbook.CurrentWorksheet;

worksheet[2, 1] = "=sqrt(400)";

44

双击单元格查看其公式:43

自定义函数也可以在脚本中使用。

ReoScript 上下文中的自定义函数

workbook 对象是在公式和脚本中都可用的全局对象(类似于 JavaScript 中的 window)。Custom Function

运行上面的代码一次,然后在公式中使用。Using Custom Function

按 Enter 键:View Result of Custom Function

在 C# 中运行脚本添加函数

也可以在 C# 中运行脚本来创建 ReoScript 函数。以下使用 ReoScript Lambda 表达式创建新函数:

grid.RunScript("script.myfunc = data => '[' + data + ']';");
这篇文章对您有帮助吗?