使用 Excel VSTO 加载项插入多个公式错误地 placed/not 计算

Inserting multiple formulas using Excel VSTO Add-In wrongly placed/not calculated

我正在使用 C#Interop 开发一个 Excel add-in。我编写了将公式一个一个插入单元格的代码,效果很好:

private static void SetFormulasCellwise(Range dataRange,
    IEnumerable<string> columnFormulas,
    int columnIndex)
{
    var rowIndex = 0;
    foreach (var columnFormula in columnFormulas)
    {
        var dataColumnCell = dataRange.Cells[rowIndex + 1, columnIndex + 1];

        try
        {
            dataColumnCell.FormulaLocal = columnFormula;
        }
        catch (COMException)
        {
            MessageBox.Show("Failed to set column formula\n"
                            + columnFormula + "\n" +
                            "to row " + (rowIndex + 1) + " and column "
                            + (columnIndex + 1) + ".\n" +
                            "Please make sure the formula is valid\n" +
                            "and that the workbook is writable.",
                "Error");
            throw;
        }

        rowIndex++;
    }
}

但是,就性能而言,这并不理想。即使禁用公式计算,设置所有公式并重新启用公式计算,这也有点慢:

Excel.Application.Calculation = enable ? xlCalculationManual : xlCalculationAutomatic;

我写了另一个版本,按如下方式设置公式列:

private static void SetFormulasColumnwise(Range dataRange,
    int columnIndex,
    IReadOnlyCollection<string> columnFormulas)
{
    var columnNumber = columnIndex + 1;
    var dataColumnCell = dataRange.Range[dataRange.Cells[1, columnNumber],
        dataRange.Cells[dataRange.Rows.Count, columnNumber]];
    var columnFormulas2Dimensional = ToColumn2DimensionalArray(columnFormulas);

    try
    {
        dataColumnCell.Formula = columnFormulas2Dimensional;
    }
    catch (COMException)
    {
        MessageBox.Show("Failed to set column formulas "
                        + "to column " + columnNumber + ".\n" +
                        "Please make sure the formula is valid\n" +
                        "and that the workbook is writable.",
            "Error");
        throw;
    }
}

private static string[,] ToColumn2DimensionalArray(IReadOnlyCollection<string> columnFormulas)
{
    var columnFormulas2Dimensional = new string[columnFormulas.Count, 1];
    var columnFormulasIndex = 0;
    foreach (var columnFormula in columnFormulas)
    {
        columnFormulas2Dimensional[columnFormulasIndex, 0] = columnFormula;
        columnFormulasIndex++;
    }

    return columnFormulas2Dimensional;
}

对于列版本,公式被插入到范围内,但向下移动了一个偏移量,如 6,并且它们不会自动计算。如果我 select 公式栏并按 Enter 只计算一个公式,但我需要自动计算所有公式。为什么会这样?这两个代码片段应该或多或少是相同的,因为它们以相同的方式修改单元格。列版本只是结合操作来提高性能。我试图通过调用例如强制 Excel 重新计算范围dataRange.Calculate() 但它什么也没做。

专栏版本有什么问题?我怎样才能让它表现得像第一个版本但比第一个版本更有效?

 dataColumnCell.Formula = columnFormulas2Dimensional;

columnFormulas2Dimensional 是类型:string[,],但 dataColumnCell.Formula 正在等待类型:string

只是为了尝试,这应该显示一个值:

dataRange.FormulaLocal = columnFormulas2Dimensional[0,0];

(我在 Visual Studio 2017 上测试过,它似乎有效。我只是使用了一个基本公式。)

 ThisAddIn.SetFormulasColumnwise(Globals.ThisAddIn.GetTestCells(), 1, new List<string>() { "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)" });

这是我的完整代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
public partial class ThisAddIn
{
    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {

    }

    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
    }
    public void Calc(Range dataRange)
    {
        dataRange.Calculate();
    }
    public void SetSource()
    {
        Excel.Worksheet ws = Application.ActiveSheet as Excel.Worksheet;
        Excel.Range c1 = ws.Cells[1, 1];
        Excel.Range c2 = ws.Cells[10, 1];

        var oRangeSource = (Excel.Range)ws.get_Range(c1, c2);
        for (int i = 1; i <= 10; i++)
        {
            oRangeSource.Cells[i, 1].Value = i;
        }
    }
    public Excel.Range GetTestCells()
    {
        Excel.Worksheet ws = Application.ActiveSheet as Excel.Worksheet;
        Excel.Range c1 = ws.Cells[1, 2];
        Excel.Range c2 = ws.Cells[10, 2];

        //Get the range using number index
        var oRange = (Excel.Range)ws.get_Range(c1, c2);

        return oRange;
    }
    public static void SetFormulasCellwise(Range dataRange,IEnumerable<string> columnFormulas,int columnIndex)
    {
        var rowIndex = 0;
        foreach (var columnFormula in columnFormulas)
        {
            var dataColumnCell = dataRange.Cells[rowIndex+1 , columnIndex];

            try
            {
                dataColumnCell.FormulaLocal = columnFormula;
            }
            catch (COMException)
            {
                MessageBox.Show("Failed to set column formula\n"
                                + columnFormula + "\n" +
                                "to row " + (rowIndex + 1) + " and column "
                                + (columnIndex + 1) + ".\n" +
                                "Please make sure the formula is valid\n" +
                                "and that the workbook is writable.",
                    "Error");
                throw;
            }

            rowIndex++;
        }
    }
    public static void SetFormulasColumnwise(Range dataRange, int columnIndex, IReadOnlyCollection<string> columnFormulas)
    {
        var columnNumber = columnIndex;
        var dataColumnCell = dataRange;
        var columnFormulas2Dimensional = ToColumn2DimensionalArray(columnFormulas);

        try
        {
            dataRange.FormulaLocal = columnFormulas2Dimensional[0,0];


        }
        catch (COMException)
        {
            MessageBox.Show("Failed to set column formulas "
                            + "to column " + columnNumber + ".\n" +
                            "Please make sure the formula is valid\n" +
                            "and that the workbook is writable.",
                "Error");
            throw;
        }
    }

    public static string[,] ToColumn2DimensionalArray(IReadOnlyCollection<string> columnFormulas)
    {
        var columnFormulas2Dimensional = new string[columnFormulas.Count, 1];
        var columnFormulasIndex = 0;
        foreach (var columnFormula in columnFormulas)
        {
            columnFormulas2Dimensional[columnFormulasIndex, 0] = columnFormula;
            columnFormulasIndex++;
        }

        return columnFormulas2Dimensional;
    }

    #region Code généré par VSTO

    /// <summary>
    /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas
    /// le contenu de cette méthode avec l'éditeur de code.
    /// </summary>
    private void InternalStartup()
    {
        this.Startup += new System.EventHandler(ThisAddIn_Startup);
        this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
    }

    #endregion
}

}

然后在我的测试功能区按钮中单击:

private void testxav_Click(object sender, RibbonControlEventArgs e)
    {
        Globals.ThisAddIn.SetSource();
        ThisAddIn.SetFormulasColumnwise(Globals.ThisAddIn.GetTestCells(), 1, new List<string>() { "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)" });
        //ThisAddIn.SetFormulasCellwise(Globals.ThisAddIn.GetTestCells(), new List<string>() { "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)", "=SOMME(A1:A10)" },1);

        Globals.ThisAddIn.Calc(Globals.ThisAddIn.GetTestCells());
    }

根据您的意见重新考虑不同的解决方案:

这是你需要的吗? (我使用模数将 columnFormulas 集合中的每个公式插入到所需范围的每个单元格中) 因为每个单元格有 1 个不同的公式,所以我不知道如何在不迭代每个单元格的情况下做到这一点

 public static void SetFormulasColumnwise(Range dataRange, IReadOnlyCollection<string> columnFormulas)
    {


        try
        {
            foreach (Range c in dataRange.Cells)
               //c.Row.ToString() returns the current row index
                c.FormulaLocal = columnFormulas.ElementAt((int.Parse(c.Row.ToString())-1)  % (columnFormulas.Count()));

        }
        catch (COMException)
        {
            MessageBox.Show("Failed to set column formulas "
                            +
                            "Please make sure the formula is valid\n" +
                            "and that the workbook is writable.",
                "Error");
            throw;
        }
    }

在测试功能区中,我使用 5 个不同的公式调用了 SetFormulasColunWise 以确保它们被正确插入

ThisAddIn.SetFormulasColumnwise(Globals.ThisAddIn.GetTestCells(),  new List<string>() { "=SOMME(A1:A10)", "=SOMME(A1:A2)", "=SOMME(A3:A4)", "=SOMME(A5:A8)", "=SOMME(A3:A8)" });

而 GetTestCells 是

public Excel.Range GetTestCells()
    {
        Excel.Worksheet ws = Application.ActiveSheet as Excel.Worksheet;
        Excel.Range c1 = ws.Cells[1, 2];
        Excel.Range c2 = ws.Cells[20, 2];

        //Get the range using number index
        var oRange = (Excel.Range)ws.get_Range(c1, c2);

        return oRange;
    }

在下面的屏幕截图中,您可以看到 Excel 结果(手动添加 C 列和 D 列,以便您可以看到 B 中计算了哪个公式)