Skip to content

Commit cd1fe30

Browse files
authored
#2311 Implemented ISFORMULA function (#2361)
* #2311 Implemented ISFORMULA function * Added test for legacy arrayformulas
1 parent d55c846 commit cd1fe30

5 files changed

Lines changed: 170 additions & 0 deletions

File tree

src/EPPlus/ExcelWorksheet.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,23 @@ internal void LoadThreadedComments()
400400

401401
#endregion
402402

403+
404+
internal bool HasFormula(int row, int col)
405+
{
406+
var formula = _formulas.GetValue(row, col);
407+
if(formula is int formulaIndex)
408+
{
409+
var sharedFormula = _sharedFormulas[formulaIndex];
410+
if(sharedFormula.StartRow == row && sharedFormula.StartCol == col)
411+
{
412+
return true;
413+
}
414+
var md = _metadataStore.GetValue(sharedFormula.StartRow, sharedFormula.StartCol);
415+
return !Workbook.Metadata.IsFormulaDynamic(md.cm);
416+
}
417+
return !string.IsNullOrEmpty(formula?.ToString() ?? "");
418+
}
419+
403420
/// <summary>
404421
/// The Uri to the worksheet within the package
405422
/// </summary>

src/EPPlus/FormulaParsing/Excel/Functions/BuiltInFunctions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ public BuiltInFunctions()
310310
Functions["type"] = new TypeFunction();
311311
Functions["sheet"] = new Sheet();
312312
Functions["isref"] = new IsRef();
313+
Functions["isformula"] = new IsFormula();
313314
// Logical
314315
Functions["if"] = new If();
315316
Functions["ifs"] = new Ifs();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*************************************************************************************************
2+
Required Notice: Copyright (C) EPPlus Software AB.
3+
This software is licensed under PolyForm Noncommercial License 1.0.0
4+
and may only be used for noncommercial purposes
5+
https://polyformproject.org/licenses/noncommercial/1.0.0/
6+
7+
A commercial license to use this software can be purchased at https://epplussoftware.com
8+
*************************************************************************************************
9+
Date Author Change
10+
*************************************************************************************************
11+
05/25/2026 EPPlus Software AB Initial release EPPlus 8.6
12+
*************************************************************************************************/
13+
using OfficeOpenXml.FormulaParsing.Excel.Functions.Metadata;
14+
using OfficeOpenXml.FormulaParsing.FormulaExpressions;
15+
using OfficeOpenXml.FormulaParsing.Ranges;
16+
using System.Collections.Generic;
17+
18+
namespace OfficeOpenXml.FormulaParsing.Excel.Functions.Information
19+
{
20+
[FunctionMetadata(
21+
Category = ExcelFunctionCategory.Information,
22+
EPPlusVersion = "8.6",
23+
Description = "Returns TRUE if the reference is to a cell containing a formula.")]
24+
internal class IsFormula : ExcelFunction
25+
{
26+
public override int ArgumentMinLength => 1;
27+
28+
public override CompileResult Execute(IList<FunctionArgument> arguments, ParsingContext context)
29+
{
30+
var reference = arguments[0].ValueAsRangeInfo;
31+
var ws = reference.Worksheet;
32+
var adr = reference.Address;
33+
if(adr.IsSingleCell)
34+
{
35+
return CreateResult(ws.HasFormula(adr.FromRow, adr.FromCol), DataType.Boolean);
36+
}
37+
var returnRange = new InMemoryRange(reference);
38+
for(var col = adr.FromCol; col <= adr.ToCol; col++)
39+
{
40+
for (var row = adr.FromRow; row <= adr.ToRow; row++)
41+
{
42+
var retVal = ws.HasFormula(row, col);
43+
returnRange.SetValue(row - adr.FromRow, col - adr.FromCol, retVal);
44+
}
45+
}
46+
return CreateDynamicArrayResult(returnRange, DataType.ExcelRange);
47+
}
48+
}
49+
}

src/EPPlus/FormulaParsing/LexicalAnalysis/FormulaAddress.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ internal string Formula
130130
}
131131
}
132132
internal bool IsArray { get; set; }
133+
134+
internal bool IsDynamicArray { get; set; }
135+
133136
internal string Address
134137
{
135138
get

src/EPPlusTest/FormulaParsing/Excel/Functions/InformationFunctionsTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,5 +465,105 @@ public void BaseShouldReturnCorrectResultWithMinValue()
465465
Assert.AreEqual("00B4", result);
466466
}
467467
}
468+
469+
[TestMethod]
470+
public void IsFormulaShouldReturnCorrectValueWhenNoFormula()
471+
{
472+
using (var package = new ExcelPackage())
473+
{
474+
var sheet = package.Workbook.Worksheets.Add("sheet1");
475+
sheet.Cells["A1"].Value = 400;
476+
sheet.Cells["A4"].Formula = "ISFORMULA(A1)";
477+
sheet.Calculate();
478+
var result = sheet.Cells["A4"].Value;
479+
Assert.IsFalse((bool)result);
480+
}
481+
}
482+
483+
[TestMethod]
484+
public void IsFormulaShouldReturnCorrectValueWhenFormula()
485+
{
486+
using (var package = new ExcelPackage())
487+
{
488+
var sheet = package.Workbook.Worksheets.Add("sheet1");
489+
sheet.Cells["A1"].Formula = "1+0";
490+
sheet.Cells["A4"].Formula = "ISFORMULA(A1)";
491+
sheet.Calculate();
492+
var result = sheet.Cells["A4"].Value;
493+
Assert.IsTrue((bool)result);
494+
}
495+
}
496+
497+
[TestMethod]
498+
public void IsFormulaShouldReturnCorrectValueWhenArray()
499+
{
500+
using (var package = new ExcelPackage())
501+
{
502+
var sheet = package.Workbook.Worksheets.Add("sheet1");
503+
sheet.Cells["A1"].Formula = "1+0";
504+
sheet.Cells["A3"].Value = 1;
505+
sheet.Cells["A4"].Formula = "ISFORMULA(A1:A3)";
506+
sheet.Calculate();
507+
var result = sheet.Cells["A4"].Value;
508+
Assert.IsTrue((bool)result);
509+
result = sheet.Cells["A5"].Value;
510+
Assert.IsFalse((bool)result);
511+
}
512+
}
513+
514+
[TestMethod]
515+
public void IsFormulaShouldReturnCorrectValueWhenArrayFormula()
516+
{
517+
using (var package = new ExcelPackage())
518+
{
519+
var sheet = package.Workbook.Worksheets.Add("sheet1");
520+
sheet.Cells["A1:A3"].Formula = "1+0";
521+
sheet.Cells["A4"].Formula = "ISFORMULA(A1:A3)";
522+
sheet.Calculate();
523+
var result = sheet.Cells["A4"].Value;
524+
Assert.IsTrue((bool)result);
525+
result = sheet.Cells["A5"].Value;
526+
Assert.IsTrue((bool)result);
527+
}
528+
}
529+
530+
[TestMethod]
531+
public void IsFormulaShouldReturnCorrectValueWhenLegacyArrayFormula()
532+
{
533+
using (var package = new ExcelPackage())
534+
{
535+
var sheet = package.Workbook.Worksheets.Add("sheet1");
536+
sheet.Cells["A1"].Value = 1;
537+
sheet.Cells["A2"].Value = 3;
538+
sheet.Cells["A3"].Value = 5;
539+
sheet.Cells["B1"].Value = 2;
540+
sheet.Cells["B2"].Value = 4;
541+
sheet.Cells["B3"].Value = 6;
542+
sheet.Cells["E6"].CreateArrayFormula("A1:B3+1");
543+
sheet.Cells["E11"].Formula = "ISFORMULA(E6)";
544+
sheet.Cells["F11"].Formula = "ISFORMULA(F6)";
545+
sheet.Calculate();
546+
var result = sheet.Cells["E11"].Value;
547+
Assert.IsTrue((bool)result);
548+
result = sheet.Cells["F11"].Value;
549+
Assert.IsFalse((bool)result);
550+
}
551+
}
552+
553+
[TestMethod]
554+
public void IsFormulaShouldReturnCorrectValueWhenDynamicArrayFormula()
555+
{
556+
using (var package = new ExcelPackage())
557+
{
558+
var sheet = package.Workbook.Worksheets.Add("sheet1");
559+
sheet.Cells["A1"].Formula = "RANDARRAY(3,1)";
560+
sheet.Cells["A4"].Formula = "ISFORMULA(A1:A3)";
561+
sheet.Calculate();
562+
var result = sheet.Cells["A4"].Value;
563+
Assert.IsTrue((bool)result);
564+
result = sheet.Cells["A5"].Value;
565+
Assert.IsFalse((bool)result);
566+
}
567+
}
468568
}
469569
}

0 commit comments

Comments
 (0)