使用 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 中计算了哪个公式)
我正在使用 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 中计算了哪个公式)