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 !!)
Related
I have a scenario, where i have a storage Blob which will have the Excel file, so in code level there is no Physical File path i have, so its Stream of file i will get the code. I need to Convert the same to CSV & push it back to the storage.
Tried below:-
Tried with Microsoft.Office.Interop.Excel
Excel.Application app = new Excel.Application();
app.DisplayAlerts = false;
// Open Excel Workbook for conversion.
Excel.Workbook excelWorkbook = app.Workbooks.Open(sourceFile);
// Save file as CSV file.
excelWorkbook.SaveAs(destinationFile, Excel.XlFileFormat.xlCSV);
Issue:- in the SourcePath , i don't have a physical location, and moreover there is no overload seems to take Byte or stream of file.
Tried https://github.com/youngcm2/CsvHelper.Excel , Demo code as follows.
using var reader = new CsvReader(new ExcelParser(FileContent, "JOB STATUSES", new CsvConfiguration(CultureInfo.CurrentCulture)));
Tried Below code even:-
using var parser = new ExcelParser(FileContent,CultureInfo.InvariantCulture); using var reader = new CsvReader(parser);
But here the ExcelParser is failing with Corrupterdfile with a valid CSV :(
Issue:- Here although there is a OverLoad to pass the Stream but is critical in my case. As there is no specific file format i have. It can be any Random EXCEL file. There no Specific class i can define.
I am missing something , can anyone help on this.
Scenario in my case:-
No Physical path to the File location . it's in Storage account, so Stream/Byte .
EXCEL File can be of any number of rows or columns no Fixed Model i can have but single sheet.
Use ExcelDataReader. It's available in NuGet.
using (var reader = ExcelReaderFactory.CreateReader(memoryStream, excelConfig))
{
var spreadsheet = reader.AsDataSet();
var table = spreadsheet.Tables[0];
var csv = table.ToCSV();
var bytes = Encoding.UTF8.GetBytes(csv);
return new StreamDataSource(bytes, table.TableName);
}
public static class TableExtension
{
public static string ToCSV(this DataTable dtDataTable)
{
var builder = new StringBuilder();
foreach (DataRow dr in dtDataTable.Rows)
{
for (int i = 0; i < dtDataTable.Columns.Count; i++)
{
if (!Convert.IsDBNull(dr[i]))
{
string value = dr[i].ToString();
if (value.Contains(","))
{
value = string.Format("\"{0}\"", value);
builder.Append(value);
}
else
{
builder.Append(dr[i].ToString());
}
}
if (i < dtDataTable.Columns.Count - 1)
{
builder.Append(",");
}
}
builder.Append(Environment.NewLine);
}
var csv = builder.ToString();
return csv;
}
}
I have a DataTable being generated using the C# NI DAQmx code. I want to take this DataTable and put it in an excel file when a CheckBox is checked. The DAQmx code records this data 'x' number of samples at a time. When this number is high, the program is slow, but it still works. I want to record a low number of samples at a time, and then save that data into an excel file.
In my current code, the data in the excel file is constantly overwritten. This is not desirable, as I need all recorded data.
Currently the data will actively record when the box is checked, but it will not concatenate. I have tried many searches and explored many methods for this, but I haven't quite been able to adapt anything for my needs.
Relevant code will be included below. Any help is appreciated, thanks.
Note: Data does not have to be a .xlsx file. It can be a .csv
This code is the DataTable generation via DAQmx:
private void DataToDataTable(AnalogWaveform<double>[] sourceArray, ref DataTable dataTable)
{
// Iterate over channels
int currentLineIndex = 0;
string test = currentLineIndex.ToString();
foreach (AnalogWaveform<double> waveform in sourceArray)
{
for (int sample = 0; sample < waveform.Samples.Count; ++sample)
{
if (sample == 50)
break;
dataTable.Rows[sample][currentLineIndex] = waveform.Samples[sample].Value;
}
currentLineIndex++;
}
}
public void InitializeDataTable(AIChannelCollection channelCollection, ref DataTable data)
{
int numOfChannels = channelCollection.Count;
data.Rows.Clear();
data.Columns.Clear();
dataColumn = new DataColumn[numOfChannels];
int numOfRows = 50;
for (int currentChannelIndex = 0; currentChannelIndex < numOfChannels; currentChannelIndex++)
{
dataColumn[currentChannelIndex] = new DataColumn()
{
DataType = typeof(double),
ColumnName = channelCollection[currentChannelIndex].PhysicalName
};
}
data.Columns.AddRange(dataColumn);
for (int currentDataIndex = 0; currentDataIndex < numOfRows ; currentDataIndex++)
{
object[] rowArr = new object[numOfChannels];
data.Rows.Add(rowArr);
}
}
This is my current method of saving to an Excel file:
private void Excel_cap_CheckedChanged(object sender, EventArgs e)
{
int i = 0;
for (excel_cap.Checked = true; excel_cap.Checked == true; i ++) {
{
StringBuilder sb = new StringBuilder();
IEnumerable<string> columnNames = dataTable.Columns.Cast<DataColumn>().
Select(column => column.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
foreach (DataRow row in dataTable.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(",", fields));
}
File.AppendAllText(filename_box.Text, sb.ToString());
}
}
}
Since you mentioned it does not have to be Excel, it could be a CSV, then you can use your CSV code but change the File.WriteAllText line to File.AppendAllText which will append the text rather than replacing the existing file. AppendAllText will create the file if it doesn't exist.
File.AppendAllText("test.csv", sb.ToString());
Are you sure you are using EPPlus? This CreateExcelFile looks like it is a copied code snippet.
With EPPlus, this would be as easy as
using (var package = new ExcelPackage(new FileInfo(#"a.xslx")))
{
if (!package.Workbook.Worksheets.Any())
package.Workbook.Worksheets.Add("sheet");
var sheet = package.Workbook.Worksheets.First();
var appendRow = (sheet.Dimension?.Rows ?? 0) + 1;
sheet.Cells[appendRow, 1].LoadFromDataTable(new DataTable(), false);
package.SaveAs(new FileInfo(#"a.xslx"));
}
It looks like you have some objects, then convert them to DataTable and then write them to Excel/CSV. If you skip the to DataTable conversion, you'll speed things up. EPPlus has LoadFromCollection which may just work with your AnalogWaveform<double>.
Shameless advertisement: I got these snippets from my blog post about EPPlus.
I am creating a DataGrid by importing an excel file. I want users manually to be able to change column names from the application.
Edit: Workaround at the bottom
My desktop app will have below logic:
Load excel file and display table in DataGrid
Manually change Column names to match fixed text. (e.x. Column "PricesZZZ" renamed to "Prices", "LeadTimeXXX to "LeadTime")
Export DataGrid to new excel template with only relevant columns that are matched by fixed text (thus the need to have correct
names).
Excel file can have multiple columns and only several of those columns have relevant information and the only way to identify them is to match header name or some other way have user "tell" program which column holds which information.
I need to find a way to change Column name based on user input as I think it's most straightforward. I'm new to c# so sorry if my thinking is a little backwards.
Below is the code snippet I have so far. Might not be relevant for this specific problem, but may help visualize. I use EPPlus library
Import excel
private void btnOpenXL_Click(object sender, RoutedEventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".xls";
dlg.Filter = "Excel Files|*.xlsx;*.xls;*.xlsm;*.csv";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
// Get the selected file name
if (result == true)
{
// Open document
string filename = dlg.FileName;
//call another class to draw the table
dataGrid.ItemsSource = GetDataTableFromExcel(filename).DefaultView;
MessageBox.Show("import done");
}
}
public static DataTable GetDataTableFromExcel(string path, bool hasHeader = true)
{
using (var pck = new OfficeOpenXml.ExcelPackage())
{
using (var stream = File.OpenRead(path))
{
pck.Load(stream);
}
var ws = pck.Workbook.Worksheets.First();
DataTable tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = hasHeader ? 2 : 1;
for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
DataRow row = tbl.Rows.Add();
foreach (var cell in wsRow)
{
row[cell.Start.Column - 1] = cell.Text;
}
}
return tbl;
}
}
Export excel
private void btnExportToXL_Click(object sender, RoutedEventArgs e)
{
DataTable dataTable = new DataTable();
dataTable = ((DataView)dataGrid.ItemsSource).ToTable();
ExportDataTableToExcel(dataTable);
MessageBox.Show("export done");
}
public void ExportDataTableToExcel(DataTable dataTable)
{
string path = "C:\\test";
var newFile = new FileInfo(path + "\\" +
DateTime.Now.Ticks + ".xlsx");
using (ExcelPackage pck = new ExcelPackage(newFile))
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Sheet1");
ws.Cells["A1"].LoadFromDataTable(dataTable, true);
pck.Save();
System.Diagnostics.Process.Start(newFile.ToString());
}
}
EDIT:
Workaround by double clicking on any cell in datagrid:
private void dataGrid_MouseDoubleClick(object sender, RoutedEventArgs e)
{
if (dataGrid.SelectedIndex == -1) //if column selected, cant use .CurrentColumn property
{
MessageBox.Show("Please double click on a row");
}
else
{
DataGridColumn columnHeader = dataGrid.CurrentColumn;
if (columnHeader != null)
{
string input = Interaction.InputBox("Title", "Prompt", "Default", 0, 0);
columnHeader.Header = input;
}
}
}
You can change the column names of the datagridview. But note, that this change is limited only to the grid and not it's data source. So in a nutshell, for simple representational purposes, you can use the following code:
dataGrid.Columns[i].HeaderText = "New Column Name"; //i is the index of the column
You can call this code form a Button click event of a Text change event of the input where the user provides the header name. Additionally, if you have the column names beforehand, you can replace then column headers with new values right after the data source has been bound to the grid. Change the headers after this line:
dataGrid.ItemsSource = GetDataTableFromExcel(filename).DefaultView;
//Set new column names here
I am using Epplus library in order to upload data from excel file.The code i am using is perfectly works for excel file which has standard form.ie if first row is column and rest all data corresponds to column.But now a days i am getting regularly , excel files which has different structure and i am not able to read
excel file like as shown below
what i want is on third row i wan only Region and Location Id and its values.Then 7th row is columns and 8th to 15 are its values.Finally 17th row is columns for 18th to 20th .How to load all these datas to seperate datatables
code i used is as shown below
I created an extension method
public static DataSet Exceltotable(this string path)
{
DataSet ds = null;
using (var pck = new OfficeOpenXml.ExcelPackage())
{
try
{
using (var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
pck.Load(stream);
}
ds = new DataSet();
var wss = pck.Workbook.Worksheets;
////////////////////////////////////
//Application app = new Application();
//app.Visible = true;
//app.Workbooks.Add("");
//app.Workbooks.Add(#"c:\MyWork\WorkBook1.xls");
//app.Workbooks.Add(#"c:\MyWork\WorkBook2.xls");
//for (int i = 2; i <= app.Workbooks.Count; i++)
//{
// for (int j = 1; j <= app.Workbooks[i].Worksheets.Count; j++)
// {
// Worksheet ws = app.Workbooks[i].Worksheets[j];
// ws.Copy(app.Workbooks[1].Worksheets[1]);
// }
//}
///////////////////////////////////////////////////
//for(int s=0;s<5;s++)
//{
foreach (var ws in wss)
{
System.Data.DataTable tbl = new System.Data.DataTable();
bool hasHeader = true; // adjust it accordingly( i've mentioned that this is a simple approach)
string ErrorMessage = string.Empty;
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
tbl.Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = hasHeader ? 2 : 1;
for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
var row = tbl.NewRow();
foreach (var cell in wsRow)
{
//modifed by faras
if (cell.Text != null)
{
row[cell.Start.Column - 1] = cell.Text;
}
}
tbl.Rows.Add(row);
tbl.TableName = ws.Name;
}
DataTable dt = RemoveEmptyRows(tbl);
ds.Tables.Add(dt);
}
}
catch (Exception exp)
{
}
return ds;
}
}
If you're providing the template for users to upload, you can mitigate this some by using named ranges in your spreadsheet. That's a good idea anyway when programmatically working with Excel because it helps when you modify your own spreadsheet, not just when the user does.
You probably know how to name a range, but for the sake of completeness, here's how to name a range.
When you're working with the spreadsheet in code you can get a reference to the range using [yourworkbook].Names["yourNamedRange"]. If it's just a single cell and you need to reference the row or column index you can use .Start.Row or .Start.Column.
I add named ranges for anything - cells containing particular values, columns, header rows, rows where sets of data begin. If I need row or column indexes I assign useful variable names. That protects you from having all sorts of "magic numbers" in your spreadsheet. You (or your users) can move quite a bit around without breaking anything.
If they modify the structure too much then it won't work. You can also use protection on the workbook and worksheet to ensure that they can't accidentally modify the structure - tabs, rows, columns.
This is loosely taken from a test I was working with last weekend when I was learning this. It was just a "hello world" so I wasn't trying to make it all streamlined and perfect. (I was working on populating a spreadsheet, not reading one, so I'm just learning the properties as I go.)
// Open the workbook
using (var package = new ExcelPackage(new FileInfo("PriceQuoteTemplate.xlsx")))
{
// Get the worksheet I'm looking for
var quoteSheet = package.Workbook.Worksheets["Quote"];
//If I wanted to get the text from one named range
var cellText = quoteSheet.Workbook.Names["myNamedRange"].Text
//If I wanted to get the cell's value as some other type
var cellValue = quoteSheet.Workbook.Names["myNamedRange"].GetValue<int>();
//If I had a named range and I wanted to loop through the rows and get
//values from certain columns
var myRange = quoteSheet.Workbook.Names["rangeContainingRows"];
//This is a named range used to mark a column. So instead of using a
//magic number, I'll read from whatever column has this named range.
var someColumn = quoteSheet.Workbook.Names["columnLabel"].Start.Column;
for(var rowNumber = myRange.Start.Row; rowNumber < myRange.Start.Row + myRange.Rows; rowNumber++)
{
var getTheTextForTheRowAndColumn = quoteSheet.Cells(rowNumber, someColumn).Text
}
There might be a more elegant way to go about it. I just started using this myself. But the idea is you tell it to find a certain named range on the spreadsheet, and then you use the row or column number of that range instead of a magic row or column number.
Even though a range might be one cell, one row, or one column, it can potentially be a larger area. That's why I use .Start.Row. In other words, give me the row for the first cell in the range. If a range has more than one row, the .Rows property indicates the number of rows so I know how many there are. That means someone could even insert rows without breaking the code.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
namespace ReadData
{
public partial class ImportExelDataInGridView : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnUpload_Click(object sender, EventArgs e)
{
//Coneection String by default empty
string ConStr = "";
//Extantion of the file upload control saving into ext because
//there are two types of extation .xls and .xlsx of excel
string ext = Path.GetExtension(FileUpload1.FileName).ToLower();
//getting the path of the file
string path = Server.MapPath("~/MyFolder/"+FileUpload1.FileName);
//saving the file inside the MyFolder of the server
FileUpload1.SaveAs(path);
Label1.Text = FileUpload1.FileName + "\'s Data showing into the GridView";
//checking that extantion is .xls or .xlsx
if (ext.Trim() == ".xls")
{
//connection string for that file which extantion is .xls
ConStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";
}
else if (ext.Trim() == ".xlsx")
{
//connection string for that file which extantion is .xlsx
ConStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + ";Extended Properties=\"Excel 12.0;HDR=Yes;IMEX=2\"";
}
//making query
string query = "SELECT * FROM [Sheet1$]";
//Providing connection
OleDbConnection conn = new OleDbConnection(ConStr);
//checking that connection state is closed or not if closed the
//open the connection
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
//create command object
OleDbCommand cmd = new OleDbCommand(query, conn);
// create a data adapter and get the data into dataadapter
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
//fill the excel data to data set
da.Fill(ds);
if (ds.Tables != null && ds.Tables.Count > 0)
{
for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
{
if (ds.Tables[0].Columns[0].ToString() == "ID" && ds.Tables[0].Columns[1].ToString() == "name")
{
}
//else if (ds.Tables[0].Rows[0][i].ToString().ToUpper() == "NAME")
//{
//}
//else if (ds.Tables[0].Rows[0][i].ToString().ToUpper() == "EMAIL")
//{
//}
}
}
//set data source of the grid view
gvExcelFile.DataSource = ds.Tables[0];
//binding the gridview
gvExcelFile.DataBind();
//close the connection
conn.Close();
}
}
}
try
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}
REF_User oREF_User = new REF_User();
oREF_User = (REF_User)Session["LoggedUser"];
string pdfFilePath = Server.MapPath("~/FileUpload/" + oREF_User.USER_ID + "");
if (Directory.Exists(pdfFilePath))
{
System.IO.DirectoryInfo di = new DirectoryInfo(pdfFilePath);
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
Directory.Delete(pdfFilePath);
}
Directory.CreateDirectory(pdfFilePath);
string path = Server.MapPath("~/FileUpload/" + oREF_User.USER_ID + "/");
if (Path.GetExtension(FileUpload1.FileName) == ".xlsx")
{
string fullpath1 = path + Path.GetFileName(FileUpload1.FileName);
if (FileUpload1.FileName != "")
{
FileUpload1.SaveAs(fullpath1);
}
FileStream Stream = new FileStream(fullpath1, FileMode.Open);
IExcelDataReader ExcelReader = ExcelReaderFactory.CreateOpenXmlReader(Stream);
DataSet oDataSet = ExcelReader.AsDataSet();
Stream.Close();
bool result = false;
foreach (System.Data.DataTable oDataTable in oDataSet.Tables)
{
//ToDO code
}
oBL_PlantTransactions.InsertList(oListREF_PlantTransactions, null);
ShowMessage("Successfully saved!", REF_ENUM.MessageType.Success);
}
else
{
ShowMessage("File Format Incorrect", REF_ENUM.MessageType.Error);
}
}
catch (Exception ex)
{
ShowMessage("Please check the details and submit again!", REF_ENUM.MessageType.Error);
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}
}
I found this article to be very helpful.
It lists various libraries you can choose from. One of the libraries I used is EPPlus as shown below.
Nuget: EPPlus Library
Excel Sheet 1 Data
Cell A2 Value :
Cell A2 Color :
Cell B2 Formula :
Cell B2 Value :
Cell B2 Border :
Excel Sheet 2 Data
Cell A2 Formula :
Cell A2 Value :
static void Main(string[] args)
{
using(var package = new ExcelPackage(new FileInfo("Book.xlsx")))
{
var firstSheet = package.Workbook.Worksheets["First Sheet"];
Console.WriteLine("Sheet 1 Data");
Console.WriteLine($"Cell A2 Value : {firstSheet.Cells["A2"].Text}");
Console.WriteLine($"Cell A2 Color : {firstSheet.Cells["A2"].Style.Font.Color.LookupColor()}");
Console.WriteLine($"Cell B2 Formula : {firstSheet.Cells["B2"].Formula}");
Console.WriteLine($"Cell B2 Value : {firstSheet.Cells["B2"].Text}");
Console.WriteLine($"Cell B2 Border : {firstSheet.Cells["B2"].Style.Border.Top.Style}");
Console.WriteLine("");
var secondSheet = package.Workbook.Worksheets["Second Sheet"];
Console.WriteLine($"Sheet 2 Data");
Console.WriteLine($"Cell A2 Formula : {secondSheet.Cells["A2"].Formula}");
Console.WriteLine($"Cell A2 Value : {secondSheet.Cells["A2"].Text}");
}
}
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