I have a cell with a formula, that I need to invoke Calculate() method on, to get the result.
Somehow I cannet invoke the Calculate method on any cells in my sheet, what am I missing?
I am using EPPlus version 4.0.5.0.
My code is as follows:
ws2.Cells[failedRow, failedColumn + 1].Formula = "SUM(B20:B" + (failedRow - 1) + ")";
And I need Calculate on that same cell. Any ideas?
Revising my answer based on #Stewart mentioning the Formula Calculations they added.
I tried again and it DOES seem to work. So, Jesper, in you case, cell.Calculate() should do it for you. I tried this:
public void Formula_Calc_Test()
{
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[] {new DataColumn("Col1", typeof (int)), new DataColumn("Col2", typeof (int))});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
datatable.Rows.Add(row);
}
using (var pck = new ExcelPackage())
{
var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");
var cells = worksheet.Cells;
cells.LoadFromDataTable(datatable, true);
var failedRow = 11;
cells["C1"].Formula = "SUM(B2:B" + (failedRow - 1) + ")";
cells["C1"].Calculate();
Console.WriteLine(cells["C1"].Value);
}
}
And did get 360 in the output.
XlsIO is a .NET library that reads and writes Excel 2003/2007/2010/2013/2016 files. Using XlsIO, you can calculated the formula of the cell very easily. The whole suite of controls is available for free (commercial applications also) through the community license program if you qualify. The community license is the full product with no limitations or watermarks.
Step 1: Create a console application
Step 2: Add reference to Syncfusion.XlsIO.Base and Syncfusion.Compression.Base; You can add these reference to your project using NuGet also.
Step 3: Copy & paste the following code snippet.
The following code snippet illustrates how to calculate the formula value in the cell using XlsIO
using (ExcelEngine excelEngine = new ExcelEngine())
{
IApplication application = excelEngine.Excel;
application.DefaultVersion = ExcelVersion.Excel2013;
IWorkbook workbook = application.Workbooks.Create(1);
IWorksheet sheet = workbook.Worksheets[0];
//Setting values to the cells
sheet.Range["A1"].Number = 10;
sheet.Range["B1"].Number = 10;
//Setting formula in the cell
sheet.Range["C1"].Formula = "=SUM(A1,B1)";
// Enabling CalcEngine for computing the formula
sheet.EnableSheetCalculations();
string computedValue = sheet.Range["C1"].CalculatedValue;
workbook.SaveAs("Formula.xlsx");
// Disabling the CalcEngine
sheet.DisableSheetCalculations();
}
For further information about XlsIO, please refer our help documentation
Note: I work for Syncfusion
Related
Using the below link, I am able to loop through the rows and able to get exact cell, but unable to set its color. Please see the code below.
How do I dynamically set the forecolour of my column data based on the value when exporting excel using EPPLUS?
var costStructureReport = new
{
CurrentQuotation = rptCostStructure.GetCostStructureReport()
};
var reportEngine = new ReportEngine();
string fileName = reportEngine.ProcessReport(ReportNames.ProjectDownload_Template, reportname + ".xlsx", costStructureReport);
var ep = new ExcelPackage(new FileInfo(fileName));
var sheet1 = ep.Workbook.Worksheets["SPR_ProjectDownload"];
var row = sheet1.Dimension.End.Row;
for(int i=0;i< costStructureReport.CurrentQuotation.Count;i++)
{
if (costStructureReport.CurrentQuotation[i].MaterialCost_ByUser)
{
sheet1.Cells[i+2, 2].Style.Font.Color.SetColor(System.Drawing.Color.Red);
sheet1.Cells[i + 2, 12].Style.Font.Color.SetColor(System.Drawing.Color.Red);
sheet1.Cells[i + 2, 12].Style.Font.Bold = true;
}
}
You can Change the Text Color using
[RangeObject].Font.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
And Font Colour using
[RangeObject].Interior.Color =System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Red);
We are developing web api from where excel will be downloaded with data. While searching on the net we found libraries like npoi, epplus, closedxml.
Do we really need to use these libraries to work with excel or go with the standard approach?
We are using asp.net core for web api development.
Edit: Basically our front end is in angular 5 from where we
are exposing the web api. In web api we have written logic to get data and after getting data we need to place in certain format/template provided(Cell, Column wise, sheet wise etc.). There are quite a number of rows which we need to export in excel.
Also our database and apis are azure based.
Any help on this appreciated !
I have used epplus, and i think it works well for this scenario. Let me give you an example.
Exporting Data
private ExcelPackage CreateDoc(string title, string subject, string keyword)
{
var p = new ExcelPackage();
p.Workbook.Properties.Title = title;
p.Workbook.Properties.Author = "Application Name";
p.Workbook.Properties.Subject = subject;
p.Workbook.Properties.Keywords = keyword;
return p;
}
public ExcelPackage getApplicantsStatistics()
{
ExcelPackage p = CreateDoc("Applicant Statistics", "Applicant statistics", "All Applicants");
var worksheet = p.Workbook.Worksheets.Add("Applicant Statistics");
//Add Report Header
worksheet.Cells[1, 1].Value = "Applicant Statistics";
worksheet.Cells[1, 1, 1, 3].Merge = true;
//Get the data you want to send to the excel file
var appProg = _unitOfWork.ApplicantsProgram
.AllIncluding(pr => pr.Program1)
.GroupBy(ap => ap.Program1.Name)
.Select(ap => new { programName = ap.Key, TotalNum = ap.Count() })
.ToList();
//First add the headers
worksheet.Cells[2, 1].Value = "SR No";
worksheet.Cells[2, 2].Value = "Program";
worksheet.Cells[2, 3].Value = "No. of Applicants";
//Add values
var numberformat = "#,##0";
var dataCellStyleName = "TableNumber";
var numStyle = p.Workbook.Styles.CreateNamedStyle(dataCellStyleName);
numStyle.Style.Numberformat.Format = numberformat;
for (int i = 0; i < appProg.Count; i++)
{
worksheet.Cells[i + 3, 1].Value = i + 1;
worksheet.Cells[i + 3, 2].Value = appProg[i].programName;
worksheet.Cells[i + 3, 3].Value = appProg[i].TotalNum;
}
// Add to table / Add summary row
var rowEnd = appProg.Count + 2;
var tbl = worksheet.Tables.Add(new ExcelAddressBase(fromRow: 2, fromCol: 1, toRow: rowEnd, toColumn: 3), "Applicants");
tbl.ShowHeader = true;
tbl.TableStyle = TableStyles.Dark9;
tbl.ShowTotal = true;
tbl.Columns[2].DataCellStyleName = dataCellStyleName;
tbl.Columns[2].TotalsRowFunction = RowFunctions.Sum;
worksheet.Cells[rowEnd, 3].Style.Numberformat.Format = numberformat;
// AutoFitColumns
worksheet.Cells[2, 1, rowEnd, 3].AutoFitColumns();
return p;
}
The returned ExcelPackage object can be sent as a download to the File with MVC
byte[] reportBytes;
using (var package = _excelRep.getApplicantsStatistics())
{
reportBytes = package.GetAsByteArray();
}
return File(reportBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
There are several good libraries for doing so, my favorite ones are
EPPlus and OpenXML by Microsoft
https://github.com/JanKallman/EPPlus
https://learn.microsoft.com/en-us/office/open-xml/open-xml-sdk
There is not much difference what your db and frontend are as everything is organized by the backend.
I am currently using EPPlus project in order to manipulate some .xlsx files. The basic idea is that I have to create a new file from a given template.
But when I create the new file from a template, all calculated columns in the tables are messed up.
The code I am using is the following:
static void Main(string[] args)
{
const string templatePath = "template_worksheet.xlsx"; // the path of the template
const string resultPath = "result.xlsx"; // the path of our result
using (var pck = new ExcelPackage(new FileInfo(resultPath), new FileInfo(templatePath))) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
For example for a .xlsx file (that have a table with 3 columns, the last one is just the sum of the others) the program creates a .xlsx file where the last column have the same value (which is correct only for the first row) in all rows.
The following images shows the result:
Now the questions are:
What is going on here ? Is my code wrong ?
How can I accomplish this task without that unexpected behavior ?
That definitely on to something there. I was able to reproduce it myself. It has to do with the Table you created. if you open your file and remove it using the "Convert To Range" option in the Table Tools tab the problem goes away.
I looked at the source code and it extracts the xml files at the zip level and didnt see any indication that it was actually messing with them - seemed to be a straight copy.
Very strange because if we create and save the xlsx file including a table from EPPlus the problem is not there. This works just fine:
[TestMethod]
public void Template_Copy_Test()
{
//http://stackoverflow.com/questions/28722945/epplus-with-a-template-is-not-working-as-expected
const string templatePath = "c:\\temp\\testtemplate.xlsx"; // the path of the template
const string resultPath = "c:\\temp\\result.xlsx"; // the path of our result
//Throw in some data
var dtdata = new DataTable("tblData");
dtdata.Columns.Add(new DataColumn("Col1", typeof(string)));
dtdata.Columns.Add(new DataColumn("Col2", typeof(int)));
dtdata.Columns.Add(new DataColumn("Col3", typeof(int)));
for (var i = 0; i < 20; i++)
{
var row = dtdata.NewRow();
row["Col1"] = "String Data " + i;
row["Col2"] = i * 10;
row["Col3"] = i * 100;
dtdata.Rows.Add(row);
}
var templateFile = new FileInfo(templatePath);
if (templateFile.Exists)
templateFile.Delete();
using (var pck = new ExcelPackage(templateFile))
{
var ws = pck.Workbook.Worksheets.Add("Data");
ws.Cells["A1"].LoadFromDataTable(dtdata, true);
for (var i = 2; i <= dtdata.Rows.Count + 1; i++)
ws.Cells[i, 4].Formula = String.Format("{0}*{1}", ExcelCellBase.GetAddress(i, 2), ExcelCellBase.GetAddress(i, 3));
ws.Tables.Add(ws.Cells[1, 1, dtdata.Rows.Count + 1, 4], "TestTable");
pck.Save();
}
using (var pck = new ExcelPackage(new FileInfo(resultPath), templateFile)) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
BUT.....
If we open testtemplate.xlsx, remove the table, save/close the file, reopen, and reinsert the exact same table the problem shows up when you run this:
[TestMethod]
public void Template_Copy_Test2()
{
//http://stackoverflow.com/questions/28722945/epplus-with-a-template-is-not-working-as-expected
const string templatePath = "c:\\temp\\testtemplate.xlsx"; // the path of the template
const string resultPath = "c:\\temp\\result.xlsx"; // the path of our result
var templateFile = new FileInfo(templatePath);
using (var pck = new ExcelPackage(new FileInfo(resultPath), templateFile)) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
It has to be something burried in their zip copy methods but I nothing jumped out at me.
But at least you can see about working around it.
Ernie
Try to use the following code. This code takes the formatting and other rules and add them as xml node to another file. Ernie described it really well here Importing excel file with all the conditional formatting rules to epplus The best part of the solution is that you can also import formatting along with your other rules. It should take you close to what you need.
//File with your rules, can be your template
var existingFile = new FileInfo(#"c:\temp\temp.xlsx");
//Other file where you want the rules
var existingFile2 = new FileInfo(#"c:\temp\temp2.xlsx");
using (var package = new ExcelPackage(existingFile))
using (var package2 = new ExcelPackage(existingFile2))
{
//Make sure there are document element for the source
var worksheet = package.Workbook.Worksheets.First();
var xdoc = worksheet.WorksheetXml;
if (xdoc.DocumentElement == null)
return;
//Make sure there are document element for the destination
var worksheet2 = package2.Workbook.Worksheets.First();
var xdoc2 = worksheet2.WorksheetXml;
if (xdoc2.DocumentElement == null)
return;
//get the extension list node 'extLst' from the ws with the formatting
var extensionlistnode = xdoc
.DocumentElement
.GetElementsByTagName("extLst")[0];
//Create the import node and append it to the end of the xml document
var newnode = xdoc2.ImportNode(extensionlistnode, true);
xdoc2.LastChild.AppendChild(newnode);
package2.Save();
}
}
Try this
var package = new ExcelPackage(excelFile)
var excelSheet = package.Workbook.Worksheets[1];
for (var i = 1; i < 5; i++){
excelWorkSheet.InsertRow(i, 1, 1); // Use value of i or whatever is suitable for you
}
package.Workbook.Calculate();
Inserting new row copies previous row format and its formula if last prm is set to 1
Can someone provide a link with a tutorial about exporting data to an excel file using c# in an asp.net web application.I searched the internet but I didn't find any tutorials that will explain how they do it.
You can use Interop http://www.c-sharpcorner.com/UploadFile/Globalking/datasettoexcel02272006232336PM/datasettoexcel.aspx
Or if you don't want to install Microsoft Office on a webserver
I recommend using CarlosAg.ExcelXmlWriter which can be found here: http://www.carlosag.net/tools/excelxmlwriter/
code sample for ExcelXmlWriter:
using CarlosAg.ExcelXmlWriter;
class TestApp {
static void Main(string[] args) {
Workbook book = new Workbook();
Worksheet sheet = book.Worksheets.Add("Sample");
WorksheetRow row = sheet.Table.Rows.Add();
row.Cells.Add("Hello World");
book.Save(#"c:\test.xls");
}
}
There is a easy way to use npoi.mapper with just below 2 lines
var mapper = new Mapper();
mapper.Save("test.xlsx", objects, "newSheet");
Pass List to below method, that will convert the list to buffer and then return buffer, a file will be downloaded.
List<T> resultList = New List<T>();
byte[] buffer = Write(resultList, true, "AttendenceSummary");
return File(buffer, "application/excel", reportTitle + ".xlsx");
public static byte[] Write<T>(IEnumerable<T> list, bool xlsxExtension = true, string sheetName = "ExportData")
{
if (list == null)
{
throw new ArgumentNullException("list");
}
XSSFWorkbook hssfworkbook = new XSSFWorkbook();
int Rowspersheet = 15000;
int TotalRows = list.Count();
int TotalSheets = TotalRows / Rowspersheet;
for (int i = 0; i <= TotalSheets; i++)
{
ISheet sheet1 = hssfworkbook.CreateSheet(sheetName + "_" + i);
IRow row = sheet1.CreateRow(0);
int index = 0;
foreach (PropertyInfo property in typeof(T).GetProperties())
{
ICellStyle cellStyle = hssfworkbook.CreateCellStyle();
IFont cellFont = hssfworkbook.CreateFont();
cellFont.Boldweight = (short)NPOI.SS.UserModel.FontBoldWeight.Bold;
cellStyle.SetFont(cellFont);
ICell cell = row.CreateCell(index++);
cell.CellStyle = cellStyle;
cell.SetCellValue(property.Name);
}
int rowIndex = 1;
// int rowIndex2 = 1;
foreach (T obj in list.Skip(Rowspersheet * i).Take(Rowspersheet))
{
row = sheet1.CreateRow(rowIndex++);
index = 0;
foreach (PropertyInfo property in typeof(T).GetProperties())
{
ICell cell = row.CreateCell(index++);
cell.SetCellValue(Convert.ToString(property.GetValue(obj)));
}
}
}
MemoryStream file = new MemoryStream();
hssfworkbook.Write(file);
return file.ToArray();
}
You can try the following links :
http://www.codeproject.com/Articles/164582/8-Solutions-to-Export-Data-to-Excel-for-ASP-NET
Export data as Excel file from ASP.NET
http://codeissue.com/issues/i14e20993075634/how-to-export-gridview-control-data-to-excel-file-using-asp-net
I've written a C# class, which lets you write your DataSet, DataTable or List<> data directly into a Excel .xlsx file using the OpenXML libraries.
http://mikesknowledgebase.com/pages/CSharp/ExportToExcel.htm
It's completely free to download, and very ASP.Net friendly.
Just pass my C# function the data to be written, the name of the file you want to create, and your page's "Response" variable, and it'll create the Excel file for you, and write it straight to the Page, ready for the user to Save/Open.
class Employee;
List<Employee> listOfEmployees = new List<Employee>();
// The following ASP.Net code gets run when I click on my "Export to Excel" button.
protected void btnExportToExcel_Click(object sender, EventArgs e)
{
// It doesn't get much easier than this...
CreateExcelFile.CreateExcelDocument(listOfEmployees, "Employees.xlsx", Response);
}
(I work for a finanical company, and we'd be lost without this functionality in every one of our apps !!)
I am using Microsoft.Office.Interop.Excel to read a spreadsheet that is open in memory.
gXlWs = (Microsoft.Office.Interop.Excel.Worksheet)gXlApp.ActiveWorkbook.ActiveSheet;
int NumCols = 7;
string[] Fields = new string[NumCols];
string input = null;
int NumRow = 2;
while (Convert.ToString(((Microsoft.Office.Interop.Excel.Range)gXlWs.Cells[NumRow, 1]).Value2) != null)
{
for (int c = 1; c <= NumCols; c++)
{
Fields[c-1] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)gXlWs.Cells[NumRow, c]).Value2);
}
NumRow++;
//Do my other processing
}
I have 180,000 rows and this turns out be very slow. I am not sure the "Convert" is efficient. Is there anyway I could do this faster?
Moon
Hi I found a very much faster way.
It is better to read the entire data in one go using "get_range". This loads the data into memory and I can loop through that like a normal array.
Microsoft.Office.Interop.Excel.Range range = gXlWs.get_Range("A1", "F188000");
object[,] values = (object[,])range.Value2;
int NumRow=1;
while (NumRow < values.GetLength(0))
{
for (int c = 1; c <= NumCols; c++)
{
Fields[c - 1] = Convert.ToString(values[NumRow, c]);
}
NumRow++;
}
There are several options - all involve some additional library:
OpenXML 2.0 (free library from MS) can be used to read/modify the content of an .xlsx so you can do with it what you want
some (commercial) 3rd-party libraries come with grid controls allowing you to do much more with excel files in your application (be it Winforms/WPF/ASP.NET...) like SpreadsheetGear, Aspose.Cells etc.
I am not sure the "Convert" is efficient. Is there anyway I could do
this faster?
What makes you believe this? I promise you that Convert.ToString() is the most effective method in the code you posted. Your problem is that your looping through 180,000 records in an excel document...
You could split the work up since you know the number of row this is trival to do.
Why are you coverting Value2 to a string exactly?
I found really fast way to read excel in my specific way. I need to get it as a two dimensional array of string. With really big excel, it took about one hour in old way. In this way, I get my values in 20sec.
I am using this nugget: https://reposhub.com/dotnet/office/ExcelDataReader-ExcelDataReader.html
And here is my code:
DataSet result = null;
//https://reposhub.com/dotnet/office/ExcelDataReader-ExcelDataReader.html
using (var stream = File.Open(path, FileMode.Open, FileAccess.Read))
{
// Auto-detect format, supports:
// - Binary Excel files (2.0-2003 format; *.xls)
// - OpenXml Excel files (2007 format; *.xlsx)
using (var reader = ExcelReaderFactory.CreateReader(stream))
{
result = reader.AsDataSet();
}
}
foreach (DataTable table in result.Tables)
{
if (//my conditions)
{
continue;
}
var rows = table.AsEnumerable().ToArray();
var dataTable = new string[table.Rows.Count][];//[table.Rows[0].ItemArray.Length];
Parallel.For(0, rows.Length, new ParallelOptions { MaxDegreeOfParallelism = 8 },
i =>
{
var row = rows[i];
dataTable[i] = row.ItemArray.Select(x => x.ToString()).ToArray();
});
importedList.Add(dataTable);
}
I guess it's not the Convert the source of "slowing"...
Actually, retrieving cell values is very slow.
I think this conversion is not necessary:
(Microsoft.Office.Interop.Excel.Range)gXlWs
It should work without that.
And you can ask directly:
gXlWs.Cells[NumRow, 1].Value != null
Try to move the entire range or, at least, the entire row to an object Matrix and work with it instead of the range itself.
Use the OleDB Method. That is the fastest as follows;
string con =
#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\temp\test.xls;" +
#"Extended Properties='Excel 8.0;HDR=Yes;'";
using(OleDbConnection connection = new OleDbConnection(con))
{
connection.Open();
OleDbCommand command = new OleDbCommand("select * from [Sheet1$]", connection);
using(OleDbDataReader dr = command.ExecuteReader())
{
while(dr.Read())
{
var row1Col0 = dr[0];
Console.WriteLine(row1Col0);
}
}
}