Home » excel » c# – Excel formula lost after reassigning the cell value

c# – Excel formula lost after reassigning the cell value

Posted by: admin May 14, 2020 Leave a comment

Questions:

Here is the sample code

XL.Worksheet worksheet = Globals.ThisAddIn.Application.ActiveSheet;
XL.Range range = worksheet.UsedRange;
arrValue = (object[,])range.get_Value(XL.XlRangeValueDataType.xlRangeValueDefault);

for (int iRow = 1; iRow <= arrValue.GetLength(0); iRow++)
{
    for (int iCol = 1; iCol <= arrValue.GetLength(1); iCol++)
    {
         if (range.Cells[iRow, iCol].Formula != null)
              range.Cells[iRow, iCol].Formula = "=" + kvp.Value.ToString();  // assign underlying formula (kvp is keyValuePair of dictionary) to the cell

         range.Cells[iRow, iCol].Value = evalResults[count].ToString(); // assign value to the cell 
         count++;
    }

} 

I wonder if Cells.Value and Cells.Formula would overwrite each other. If I want to assign both of them at the same time, what is the best way to do it?

Thanks.

How to&Answers:

It is not possible to assign bot the value and the formula to an excel cell (you can’t do that even using excel in the normal interactive way), since the result would be undefined, i.e. if the result of the formula is different from the value what would you show in the cell?

Since you want to preserve your formulas for reuse in the future you could have a Recalculate method which applies the formulas, saves them to a dictionary (and you already have something like that, don’t you?), and then set the value of the cells with the current value (calculated from the formula).

Maybe this is what you are already doing here, but it’s not so clear, since I don’t understand what evalResults is. Anyway, I mean something like this:

private Dictionary<int,Dictionary<int,string>> formulas = new Dictionary<int,Dictionary<int,string>>(); // this has to be initialized according to your formulas
public void Recalculate()
{
    XL.Worksheet worksheet = Globals.ThisAddIn.Application.ActiveSheet;
    XL.Range range = worksheet.UsedRange;

    for (int iRow = 1; iRow <= arrValue.GetLength(0); iRow++)
    {
        for (int iCol = 1; iCol <= arrValue.GetLength(1); iCol++)
        {
            range.Cells[iRow, iCol].Formula = "=" + formulas[iRow][iCol];  
            range.Cells[iRow, iCol].Value = range.Cells[iRow, iCol].Value; 
        }

    }
}

If you want to keep the formulas in the excel workbook and not in memory (in the dictionary), a solution could be to have a hidden worksheet where you store the formulas as strings (i.e. without the ‘=’) in order to avoid their evaluation and the performance issues. So you would have a SetFormulas and a Recalculate method very similar to your code:

public void SetFormulas()
{
    XL.Worksheet worksheet = Globals.ThisAddIn.Application.ActiveSheet;
    XL.Worksheet formulasWorksheet = Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets["formulas"];
    XL.Range range = worksheet.UsedRange;
    arrValue = (object[,])range.get_Value(XL.XlRangeValueDataType.xlRangeValueDefault);

    for (int iRow = 1; iRow <= arrValue.GetLength(0); iRow++)
    {
        for (int iCol = 1; iCol <= arrValue.GetLength(1); iCol++)
        {
             if (range.Cells[iRow, iCol].Formula != null)
             {
                  range.Cells[iRow, iCol].Formula = "=" + kvp.Value.ToString();  // assign underlying formula (kvp is keyValuePair of dictionary) to the cell
                  formulasWorksheet.Cells[iRow, iCol].Value = kvp.Value.ToString(); // storing the formula here
             }
             range.Cells[iRow, iCol].Value = evalResults[count].ToString(); // assign value to the cell 
             count++;
        }

    }
}



public void Recalculate()
{
    XL.Worksheet worksheet = Globals.ThisAddIn.Application.ActiveSheet;
    XL.Worksheet formulasWorksheet = Globals.ThisAddIn.Application.ActiveWorkbook.Worksheets["formulas"];
    XL.Range range = worksheet.UsedRange;
    arrValue = (object[,])range.get_Value(XL.XlRangeValueDataType.xlRangeValueDefault);

    for (int iRow = 1; iRow <= arrValue.GetLength(0); iRow++)
    {
        for (int iCol = 1; iCol <= arrValue.GetLength(1); iCol++)
        {
             if (!string.IsNullOrEmpty(formulasWorksheet.Cells[iRow, iCol].Text))
             {
                  range.Cells[iRow, iCol].Formula = "=" + formulasWorksheet.Cells[iRow, iCol].Text; // restoring the formula
                  range.Cells[iRow, iCol].Value = range.Cells[iRow, iCol].Value // replacing the formula with the value
             }
        }

    }
}

Answer:

In your comment, you state your real concern, which is that performance will suffer if cell formulas are recalculated automatically. The solution is then to prevent automatic recalculation, i.e. make it manual:

Application.Calculation = xlCalculationManual

where xlCalculationManual is a built-in constant equal to -4135. You can set it back to xlCalculationAutomatic or -4105 later.

Just leave you formulas there, don’t edit the values, let Excel figure out the result.

While in manual mode, the user can force a recalculation using F9, or you can do it programmatically with Application.Calculate.

All this is also available in the Excel user interface: Tools > Options > Calculation.