要添加自定义函数,可以使用 ReoGrid.Formula.FormulaExtension 类。以下示例添加一个将给定字符串转换为大写的函数。
// 添加自定义函数
ReoGrid.Formula.FormulaExtension.CustomFunctions["myUpper"] = function(cell, args){
/*
注意这是一个 .NET 中的 object 数组,
这不是 aardio 数组不能用 # 操作符取数组长度。
应当用 args.Length 取数组长度
*/
if(args.Length == 0) {
// 此函数至少需要一个参数
return null;
}
//args[1] 是字符串,不用再转换
return string.upper( args[1] );
};
测试此函数,输入公式 =myUpper("hello"):

按回车后,单元格值将变为大写:

ReoGrid.Formula.FormulaExtension 是一个用于在公式计算期间添加扩展的接口。
在具有脚本执行功能的 ReoGrid 扩展版本中,公式扩展可以通过一些 ReoScript 功能实现。对于使用 ReoGrid 标准版本的应用程序,ReoGrid.Formula.FormulaExtension 接口是实现公式扩展功能的更简单方式。

ReoGrid.Formula.FormulaExtension.CustomFunctions 属性在 C# 源码中定义为:
Dictionary<string, Func<ReoGridCell, object[], object>> CustomFunctions
aardio:
ReoGrid.Formula.FormulaExtension.CustomFunctions["myFunc"] = function(cell, args){
// 函数体
return null;
};
也可以使用 lambda 表达式:
// 使此函数在公式中可用
ReoGrid.Formula.FormulaExtension.CustomFunction["myFunc"] = lambda(cell, ...) null;
第一个参数是拥有公式的单元格实例,例如:
sheet1["A1"] = "=myFunc()";
A1 单元格有一个调用自定义函数 "myFunc" 的公式,上面定义的函数体将被调用,cell 参数就是 "A1" 单元格。
function MyFunc(cell, ...){
win.msgbox(cell.Position.ToAddress()); // 输出: A1
return null;
}
通过获取单元格实例的 Worksheet 属性可以获取工作表实例,以及从工作表实例的 Workbook 属性获取工作簿实例。
ReoGrid.Formula.FormulaExtension.CustomFunction["myFunc"] = function(cell, args){
var sheet1 = cell.Worksheet;
var workbook = sheet1.Workbook;
// ...
return null;
};
限制函数仅在指定工作簿或工作表中可用:
{
var sheet1 = cell.Worksheet;
var workbook = sheet1.Workbook;
if (workbook != myWorkbook) {
// 不在有效的工作簿范围内
return null;
}
}
第二个参数是从公式调用者传递的参数。
ReoGrid.Formula.FormulaExtension.CustomFunction["myFunc"] = function(cell, args){
//.NET 对象数组要用 Length 属性取数组长度,不能用 `#` 操作符
if(args.Length < 2) {
// 此函数至少需要两个参数
return null;
}
return null;
};
公式计算期间使用多种数据类型:
ReoGrid 在公式计算中始终使用 double 作为数字类型。ReoGrid 在将值传递给函数之前执行以下转换:
公式中使用的数值常量将被初始化为 double 类型,例如:
sheet1["A1"] = "=myFunc(10)";
数字 10 将被解析并作为 double 类型存储在内存中。参数数组中的第一个参数将是 double 类型。
ReoGrid.Formula.FormulaExtension.CustomFunction["myFunc"] = function(cell, args){
if(args.Length < 1 || type(args[1]) != type.number) {
// 此函数需要一个 double 值
return null;
}
// ...
};
当数值来自单元格时,它将被转换为 double 类型。创建以下函数:
ReoGrid.Formula.FormulaExtension.CustomFunction["myFunc"] = function(cell, args){
var value = args[1];
return value * 2;
};
然后在公式中使用:
sheet1["A1"] = 10;
sheet1["B1"] = "=myFunc(A1)";
不会发生数据类型转换错误。(但要注意参数长度)
ReoGrid 会自动从公式中使用的引用单元格获取值,如"数值变量"示例所示。
如果在公式中使用范围字面量,ReoGrid 会将 ReoGrid.RangePosition 结构传递给函数,例如:
sheet1["G2:K3"] = {1, 2, 5, 7, 8, 10, 12, 15, 16, 19};
sheet1["L2"] = "=CountEvenNumber(G2:K3)";
有一个范围引用 (G2:K3) 在公式中使用,需要在自定义函数体内手动处理。以下示例函数计算给定范围内的偶数数量:
ReoGrid.Formula.FormulaExtension.CustomFunction["CountEvenNumber"] = function(cell, args){
if(args.Length < 1 || type(args[1]) != type.table) {
return null; // 我们需要一个范围引用
}
var range = args[1];
var count = 0;
// 遍历范围内的单元格
cell.Worksheet.IterateCells(range, function(r, c, inCell){
var value;
// 尝试从单元格数据获取数字
if(ReoGridCellUtility.TryGetNumberData(inCell.Data, value)) {
if((value % 2) == 0) {
count++;
}
}
// 继续迭代
return true;
});
return count;
};
如果名称引用单元格,单元格的值将被自动获取并传递给函数。
sheet1.DefineNamedRange("r1", "A1");
sheet1["r1"] = 20;
sheet1["B1"] = "=myFunc(r1)";
如果公式中使用了引用范围的名称,ReoGrid 会首先按名称查找范围,然后传递 ReoGrid.RangePosition 结构对象,可以像范围引用一样处理。
sheet1.DefineNamedRange("evenRange", "G2:K3");
sheet1["evenRange"] = {1, 2, 4, 6, 9, 11, 13, 14, 15, 17};
sheet1["I2"] = "=CountEvenNumber(evenRange)";
assert(sheet1["I2"] == 4);
可以添加在公式中使用但由 .NET 程序提供的常量值。以下示例添加两个返回不同常量值的名称:
ReoGrid.Formula.FormulaExtension.NameReferenceProvider = function(cell, name){
if(name == "myName1") {
return 10;
} else if(name == "myName2") {
return 20;
} else {
return null;
}
};
NameReferenceProvider 定义为:
Func<ReoGridCell, string, object> NameReferenceProvider { get; set; }
测试名称1,在单元格中输入 =myName1:

名称返回值10:

输入 =myName2:

名称返回值20:

通过使用自定义名称引用提供程序,还可以添加一些常量,如数学数字,以下代码定义了一个常量提供程序:
aardio:
function MathConstantProvider(cell, name){
select(name) {
case "PI"
return math.pi;
case "E"
return math.exp(1);
case "MyConst"
return 1.23456;
}
return null;
}
// 应用此名称引用提供程序
ReoGrid.Formula.FormulaExtension.NameReferenceProvider = MathConstantProvider;
在公式中使用常量:
sheet1["A1"] = "=MyConst"
结果:

在自定义函数中使用自定义名称提供程序也受支持。名称将被转换为值并传递给函数。
ReoGrid.Formula.FormulaExtension.NameReferenceProvider = function(cell, name){
select(name) {
case "Pi"
return math.pi;
case "E"
return math.exp(1);
}
return null;
};
在自定义函数中使用名称 Pi:
sheet1["D3"] = "=Div100(Pi)";
assert(sheet1["D3"] == math.pi / 100);
当启用 ReoScript 模块扩展时,可以使用 ReoScript 脚本语言处理单元格的数据、样式、边框、数据格式等,类似于 Excel 中的 VBA。ReoScript 支持为脚本语言制作 .NET 扩展,包括在脚本中调用 .NET 函数。
ReoGrid 支持调用 ReoScript 自定义扩展函数:
ReoScript 语言扩展仅在完整发布包中可用。

添加自定义函数扩展控件:
//注册自定义函数
grid.Srm["sqrt"] = ReoScript.NativeFunctionObject(
"sqrt", //指定函数名
function(ctx,world,args){
if(args.Length < 1) return 0/0/*NaN*/;
//获取第一个参数,其实在 aardio 里这句是多余的
//arg1 = ReoScript.ScriptRunningMachine.GetDoubleValue(args[1],0);
return math.sqrt( args[1] );
}
)
测试:
var grid = grid.CurrentWorksheet;
sheet1[2, 1] = "=sqrt(400)";

双击单元格查看其公式:

完整示例代码:
import win.ui;
/*DSG{{*/
var winform = win.form(text="ReoGrid 示例")
/*}}*/
import dotNet.ReoGrid;
var grid = ReoGrid.ReoGridControl(winform);
var sheet1 = grid.CurrentWorksheet;
//注册自定义函数
grid.Srm["sqrt"] = ReoScript.NativeFunctionObject(
"sqrt", //指定函数名
function(ctx,world,args){
if(args.Length < 1) return 0/0/*NaN*/;
//获取第一个参数,其实在 aardio 里这句是多余的
//arg1 = ReoScript.ScriptRunningMachine.GetDoubleValue(args[1],0);
return math.sqrt( args[1] );
}
)
//调用自定义函数
sheet1[2, 1] = "=sqrt(400)";
winform.show();
win.loopMessage();
自定义函数也可以在脚本中使用。
对象 'workbook' 是在公式和脚本中都可用的全局对象。(类似于 JavaScript 中的 'window')

运行上面的代码一次并在公式中使用它。

按 'enter':

也可以在 aardio 中运行 ReoScript 脚本来创建 ReoScript 函数,以下是用于创建新函数的 ReoScript lambda 表达式。
grid.RunScript("script.myfunc = data => '[' + data + ']';");
完整示例:
import win.ui;
/*DSG{{*/
var winform = win.form(text="ReoGrid 示例")
/*}}*/
import dotNet.ReoGrid;
var grid = ReoGrid.ReoGridControl(winform);
var sheet1 = grid.CurrentWorksheet;
//执行 ReoScript 脚本添加自定义函数
grid.RunScript("script.myfunc = data => '[' + data + ']';");
//调用自定义函数
sheet1[2, 1] = "=myfunc(400)";
winform.show();
win.loopMessage();