aardio 文档

dotNet.ReoGrid 扩展库 - 自定义函数

添加自定义公式函数

要添加自定义函数,可以使用 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")
169

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

关于 FormulaExtension

ReoGrid.Formula.FormulaExtension 是一个用于在公式计算期间添加扩展的接口。

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

177

ReoGrid.Formula.FormulaExtension.CustomFunctions 属性在 C# 源码中定义为:

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

最简单的函数示例

aardio:

ReoGrid.Formula.FormulaExtension.CustomFunctions["myFunc"] = function(cell, args){
    // 函数体
    return null;
};

使用 lambda 表达式

也可以使用 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
171

名称返回值10:
172

输入 =myName2
173

名称返回值20:
174

添加数学常量

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

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"

结果:
181

组合自定义函数和自定义名称提供程序

在自定义函数中使用自定义名称提供程序也受支持。名称将被转换为值并传递给函数。

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 模块扩展时,可以使用 ReoScript 脚本语言处理单元格的数据、样式、边框、数据格式等,类似于 Excel 中的 VBA。ReoScript 支持为脚本语言制作 .NET 扩展,包括在脚本中调用 .NET 函数。

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

ReoScript 语言扩展仅在完整发布包中可用。

178

在 aardio 中添加 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)";

44

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

完整示例代码:

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

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

ReoScript 上下文中的自定义函数

对象 'workbook' 是在公式和脚本中都可用的全局对象。(类似于 JavaScript 中的 'window')
自定义函数

运行上面的代码一次并在公式中使用它。
使用自定义函数

按 'enter':
查看自定义函数结果

在 aardio 中运行脚本添加函数

也可以在 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();

Markdown 格式