0

目前,我正在共享运行时加载项项目中从 CustomFunction.Invocation 更改为 StreamingInvocation,因为我将不得不使用 Web 套接字。我有一个用例需要使用自定义函数的地址属性作为数据对象中的键以供进一步使用,我发现它不能与 StreamingInvocation 一起使用。我尝试使用 onChanged 和 onCalculated 事件实现解决方法,但效果不佳。所以,我不确定是否有一些解决方法可以用来在其内部获取自定义函数的地址属性。

编辑:添加代码和更多细节

export class TestExcelService {
  private static instance: TestExcelService;
  private dataEventEmtter?: EventEmitter;

  // prevent direct construction call with new keyword.
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  private constructor() {
    this.registerOnCalculated();
  }

  public static getInstance(): TestExcelService {
    if (!TestExcelService.instance) {
        TestExcelService.instance = new TestExcelService();
    }
    return TestExcelService.instance;
  }

  public setDataEventEmtter(eventEmitter: EventEmitter): void {
    this.dataEventEmtter = eventEmitter;
  }

  public getDataEventEmtter(): EventEmitter | undefined {
    return this.dataEventEmtter;
  }

  private async registerOnCalculated(): Promise<void> {
    await Excel.run(async (context: Excel.RequestContext) => {
      context.workbook.worksheets.getActiveWorksheet().onCalculated.add(
        async (arg: Excel.WorksheetCalculatedEventArgs): Promise<void> => {
          const formulaAddresses: ICellAddress[] = await this.filterMyFormulaAddress(arg);
          if (this.dataEventEmtter && formulaAddresses.length > 0) {
            this.dataEventEmtter.emit('onExcelCalculated', formulaAddresses);
          }
        },
      );
    });
  }

  private async filterMyFormulaAddress(arg: Excel.WorksheetCalculatedEventArgs): Promise<ICellAddress[]> {
    // filter only some formulas
    return [];
  }
}


export class TestDataService {
  private eventEmitter: EventEmitter = new EventEmitter();
  private static instance: TestDataService;
  private dataQueue = [];

  // prevent direct construction call with new keyword.
  // eslint-disable-next-line @typescript-eslint/no-empty-function
  private constructor() {
    this.eventEmitter.addListener('onExcelCalculated', this.excelCalculatedHandler.bind(this));
  }

  public static getInstance(): TestDataService {
    if (!TestDataService.instance) {
        TestDataService.instance = new TestDataService();
    }
    return TestDataService.instance;
  }

  public addDataToQueue(data): void {
    const duplicatedIndex: number = this.dataQueue.findIndex(
      (queueData) => data.formulaId === queueData.formulaId,
    );
    if (duplicatedIndex < 0) {
      this.dataQueue.push(data);
    }
  }

  public getEventEmitter(): EventEmitter {
    return this.eventEmitter;
  }

  private async excelCalculatedHandler(formulaAddresses: ICellAddress[]): Promise<void> {
    if (Array.isArray(this.dataQueue) && this.dataQueue.length > 0) {
    // check that data queue is not empty and map formula addresses from onCalculated event to the data queue
    }
  }
}

我不能发布完整的代码,所以我将不得不删除和修改代码的某些部分。

一般的想法是我将必须使用它们各自的公式地址映射的数据对象添加到 DataService 中的数据队列中,并且在触发 onCalculated 事件后,我应该能够获得计算和过滤地址的列表。

根据我的测试,公式地址的顺序应该与数据队列的顺序相匹配。起初,当我一次执行一个公式或同时重新计算少量公式时,这种解决方法似乎工作正常,但是当公式数量开始增长并且我尝试重新计算所有公式时,公式的顺序地址和数据队列不会像我预期的那样匹配。

4

1 回答 1

0

流媒体功能很特别。如果任何一组流函数使用相同的表达式,则它们共享相同的根(即上下文)。工作簿中的范围会很好。在另一个工作簿中,上下文会有所不同。因此没有为流功能提供地址信息。如您所见,onCalculated 事件将记录所有计算的地址。但是,对于地址的特殊请求,它需要额外的逻辑,例如保持公式地址的顺序以匹配您案例中的数据队列。

于 2021-05-07T09:02:09.693 回答