I've been using EPPlus for .net for a while now but only for simple data manipulation.
Are there any examples somewhere on how to use it to create pivot tables/charts?
It seems to support it as I can see PivotTable in the intellisense but just unsure on the syntax.
I could only find the likes of pie/bar charts in the samples provided.
I've produced a similar solution from Tim's Answer. First, I defined a simple interface that I use as part of my export methods:
public interface IPivotTableCreator
{
void CreatePivotTable(
OfficeOpenXml.ExcelPackage pkg, // reference to the destination book
string tableName, // "tab" name used to generate names for related items
string pivotRangeName); // Named range in the Workbook refers to data
}
Then I implemented a simple class that hold the variable values and procedural code to do the work:
public class SimplePivotTable : IPivotTableCreator
{
List<string> _GroupByColumns;
List<string> _SummaryColumns;
/// <summary>
/// Constructor
/// </summary>
public SimplePivotTable(string[] groupByColumns, string[] summaryColumns)
{
_GroupByColumns = new List<string>(groupByColumns);
_SummaryColumns = new List<string>(summaryColumns);
}
/// <summary>
/// Call-back handler that builds simple PivatTable in Excel
/// http://stackoverflow.com/questions/11650080/epplus-pivot-tables-charts
/// </summary>
public void CreatePivotTable(OfficeOpenXml.ExcelPackage pkg, string tableName, string pivotRangeName)
{
string pageName = "Pivot-" + tableName.Replace(" ", "");
var wsPivot = pkg.Workbook.Worksheets.Add(pageName);
pkg.Workbook.Worksheets.MoveBefore(PageName, tableName);
var dataRange = pkg.Workbook./*Worksheets[tableName].*/Names[pivotRangeName];
var pivotTable = wsPivot.PivotTables.Add(wsPivot.Cells["C3"], dataRange, "Pivot_" + tableName.Replace(" ", ""));
pivotTable.ShowHeaders = true;
pivotTable.UseAutoFormatting = true;
pivotTable.ApplyWidthHeightFormats = true;
pivotTable.ShowDrill = true;
pivotTable.FirstHeaderRow = 1; // first row has headers
pivotTable.FirstDataCol = 1; // first col of data
pivotTable.FirstDataRow = 2; // first row of data
foreach (string row in _GroupByColumns)
{
var field = pivotTable.Fields[row];
pivotTable.RowFields.Add(field);
field.Sort = eSortType.Ascending;
}
foreach (string column in _SummaryColumns)
{
var field = pivotTable.Fields[column];
ExcelPivotTableDataField result = pivotTable.DataFields.Add(field);
}
pivotTable.DataOnRows = false;
}
}
Then I create an instance of my SimplePivotTable creator class:
IPivotTableCreator ptCreator = new SimplePivotTable(
new string[] { "OrganizationTitle", "GroupingTitle", "DetailTitle" }, /* collapsible rows */
new string[] { "Baseline", "Increase", "Decrease", "NetChange", "CurrentCount"}); /* summary columns */
I have a third class that currently exposes about six different methods to take one-or-more data sets (usually List objects) and turn each of the data sets into a worksheet of data with a named range for the data. Now, I'm adapting those export methods to allow me to generate Pivot Tables for any/all of those export methods. They all do something like this:
OfficeOpenXml.ExcelPackage pkg = new ExcelPackage();
ExportCollectionToExcel(pkg, tableName, dataset); // Create worksheet filled with data
// Creates a NamedRange of data
ptCreator.CreatePivotTable(pkg, tableName, GetPivotRangeName(tableName));
By using an interface, I leave open more opportunities (I think) to generate, for example, a different pivot table for multiple sheets. My basic SimplePivotTable class just used for a single table with some specific assumptions, but it wouldn't be hard to put the configuration data into a dictionary keyed to the Table names.
Hope that helps someone.
Here's the code of a pivot i've created recently, maybe it does help:
DataTable table = getDataSource();
FileInfo fileInfo = new FileInfo(path);
var excel = new ExcelPackage(fileInfo);
var wsData = excel.Workbook.Worksheets.Add("Data-Worksheetname");
var wsPivot = excel.Workbook.Worksheets.Add("Pivot-Worksheetname");
wsData.Cells["A1"].LoadFromDataTable(table, true, OfficeOpenXml.Table.TableStyles.Medium6);
if (table.Rows.Count != 0)
{
foreach (DataColumn col in table.Columns)
{
// format all dates in german format (adjust accordingly)
if (col.DataType == typeof(System.DateTime))
{
var colNumber = col.Ordinal + 1;
var range = wsData.Cells[2, colNumber, table.Rows.Count + 1, colNumber];
range.Style.Numberformat.Format = "dd.MM.yyyy";
}
}
}
var dataRange = wsData.Cells[wsData.Dimension.Address.ToString()];
dataRange.AutoFitColumns();
var pivotTable = wsPivot.PivotTables.Add(wsPivot.Cells["A3"], dataRange, "Pivotname");
pivotTable.MultipleFieldFilters = true;
pivotTable.RowGrandTotals = true;
pivotTable.ColumGrandTotals = true;
pivotTable.Compact = true;
pivotTable.CompactData = true;
pivotTable.GridDropZones = false;
pivotTable.Outline = false;
pivotTable.OutlineData = false;
pivotTable.ShowError = true;
pivotTable.ErrorCaption = "[error]";
pivotTable.ShowHeaders = true;
pivotTable.UseAutoFormatting = true;
pivotTable.ApplyWidthHeightFormats = true;
pivotTable.ShowDrill = true;
pivotTable.FirstDataCol = 3;
pivotTable.RowHeaderCaption = "Claims";
var modelField = pivotTable.Fields["Model"];
pivotTable.PageFields.Add(modelField);
modelField.Sort = OfficeOpenXml.Table.PivotTable.eSortType.Ascending;
var countField = pivotTable.Fields["Claims"];
pivotTable.DataFields.Add(countField);
var countryField = pivotTable.Fields["Country"];
pivotTable.RowFields.Add(countryField);
var gspField = pivotTable.Fields["GSP / DRSL"];
pivotTable.RowFields.Add(gspField);
var oldStatusField = pivotTable.Fields["Old Status"];
pivotTable.ColumnFields.Add(oldStatusField);
var newStatusField = pivotTable.Fields["New Status"];
pivotTable.ColumnFields.Add(newStatusField);
var submittedDateField = pivotTable.Fields["Claim Submitted Date"];
pivotTable.RowFields.Add(submittedDateField);
submittedDateField.AddDateGrouping(OfficeOpenXml.Table.PivotTable.eDateGroupBy.Months | OfficeOpenXml.Table.PivotTable.eDateGroupBy.Days);
var monthGroupField = pivotTable.Fields.GetDateGroupField(OfficeOpenXml.Table.PivotTable.eDateGroupBy.Months);
monthGroupField.ShowAll = false;
var dayGroupField = pivotTable.Fields.GetDateGroupField(OfficeOpenXml.Table.PivotTable.eDateGroupBy.Days);
dayGroupField.ShowAll = false;
excel.Save();
Related
I have a .csv file structured like so, first row is the header column, for each SomeID I need to add the NetCharges together(or substract if the code calls for it) and put each item into its own column by the SomeCode column.
Heres the file I receive;
SomeID,OrderNumber,Code,NetCharge,Total
23473,30388,LI 126.0000, 132.00
96021, 000111, LI, 130.00, 126.00
23473,30388,FU 6.0000, 132.00
4571A,10452,LI,4100.0000, 4325.0000
4571A,10452,FU,150.00,4325.0000
4571A,10452,DT,75.00,4325.0000
I need to insert the data to my sql table which is structured like this. This is what I'm aiming for:
ID OrderNumber LICode LICodeValue FUCode FUCodeValue DTCode, DTCodeValue, total
23473 30388n LI 126.000 FU 6.0000 NULL NULL 132.0000
4571A 10452 LI 4100.0000 FU 150.0000 DT 75.00 4325.0000
My SomeID will not always be grouped together like the 4571A id is.I basically need to iterate over this file and create one record for each SomeID. I cannot seem to find a way with csvHelper. I'm using C# and csvHelper. I have trid this so far but I cannot get back to the SomeId after passing on to the nexr one:
using (var reader = new StreamReader( "C:\testFiles\some.csv" ))
using (var csv = new CsvReader( reader, CultureInfo.InvariantCulture ))
{
var badRecords = new List<string>();
var isRecordBad = false;
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.HeaderValidated = null;
csv.Configuration.IgnoreBlankLines = true;
csv.Configuration.Delimiter = ",";
csv.Configuration.BadDataFound = context =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
csv.Configuration.MissingFieldFound = ( s, i, context ) =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
List<DataFile> dataFile = csv.GetRecords<DataFile>().ToList();
//initialize variable
string lastSomeId = "";
if (!isRecordBad)
{
foreach (var item in dataFile)
{
// check if its same record
if (lastSomeId != item.SomeID)
{
MyClass someClass = new MyClass();
lastSomeId = item.SomeID;
//decimal? LI = 0;//was going to use these as vars for calculations not sure I need them???
//decimal? DSC = 0;
//decimal? FU = 0;
someClass.Id = lastSomeId;
someClass.OrdNum = item.OrderNumber;
if (item.Code == "LI")
{
someClass.LICode = item.Code;
someClass.LICodeValue = item.NetCharge;
}
if (item.Code == "DT")
{
someClass.DTCode = item.Code;
someClass.DTCodeValue = item.NetCharge
}
if (item.Code == "FU")
{
someClass.FUCode = item.Code;
someClass.FUCodeValue = item.NetCharge;
}
someClass.Total = (someClass.LICodeValue + someClass.FUCodeValue);
//check for other values to calculate
//insert record to DB
}
else
{
//Insert into db after maipulation of values
}
}
}
isRecordBad = false;
}//END Using
Any clues would be greatly appreciated. Thank you in advance.
I am using CsvHelper lib to read CSV file and I can successfully read the file with the lib. However I cannot use SQL condition to filter values. How can I do that without using SQL Server. I am really stuck on it.
It was very easy with Pandas and Pandasql libs in Python but it is being too hard in C#..
My Code:
public static void Main(string[] args)
{
var fileInfo = new FileInfo(#"filePath");
using (TextReader reader = fileInfo.OpenText())
using (var csvReader = new CsvReader(reader))
{
csvReader.Configuration.Delimiter = ",";
csvReader.Configuration.HasHeaderRecord = false;
csvReader.Configuration.IgnoreQuotes = true;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.WillThrowOnMissingField = false;
while (csvReader.Read())
{
var myStrinVar = csvReader.GetField<string>(0);
Console.Write(myStrinVar); //SELECT * FROM table...
}
}
}
I would suggest using LINQ to filter your results.
https://msdn.microsoft.com/en-us/library/bb397906.aspx
Say you have some class MyClass that you can serialize the lines in your file into.
For example:
public class MyClass
{
public int ID { get; set; }
}
var records = csv.GetRecords<MyClass>().ToList();
var filtered = records.Where(r => r.ID >= 10);
That example is a bit contrived but you can use any boolean expression you like in the where clause.
I know this is too late for OP, but the issue with the accepted answer is that you have to read in the entire result set to memory which may not be tenable for large files. Also, if you can extend this code below to get the top N rows without having to read the entire CSV if you find matches early in the file.
public static void Main(string[] args)
{
var fileInfo = new FileInfo(#"filePath");
var where = ""; //Code to set up where clause part of query goes here
using (TextReader reader = fileInfo.OpenText())
using (var csvReader = new CsvReader(reader))
{
csvReader.Configuration.Delimiter = ",";
csvReader.Configuration.HasHeaderRecord = false;
csvReader.Configuration.IgnoreQuotes = true;
csvReader.Configuration.TrimFields = true;
csvReader.Configuration.WillThrowOnMissingField = false;
DataTable dt = null;
while (csvReader.Read())
{
//Use the first row to initialize the columns.
if (dt == null)
{
dt = new DataTable();
for (var i = 0; i < csvReader.FieldCount; i++)
{
var fieldType = csvReader.GetFieldType(i);
DataColumn dc;
if (fieldType.IsNullableType())
{
dc = new DataColumn(csvReader.GetName(i), Nullable.GetUnderlyingType(fieldType));
dc.AllowDBNull = true;
}
else
dc = new DataColumn(csvReader.GetName(i), data.GetFieldType(i));
dt.Columns.Add(dc);
}
}
//Map DataReader to DataRow
var newRow = dt.Rows.Add();
foreach(DataColumn col in dt.Columns)
{
newRow[col.ColumnName] = csvReader[col.ColumnName];
}
//Create a temporary DataView and filter it with the where clause.
DataView dv = new DataView(dt);
dv.RowFilter = where;
var data = dv.Count > 0 ? dv[0] : null;
if(data != null)
{
//Row in here matches your where clause.
//Code to read this row or do something with it.
}
//Empty the temporary data table.
dt.Rows.Clear();
}
}
}
I am trying to export some data to an excel sheet S1 whose data would be shown as Pivoted views in the next two sheets S2 and S3. I am able to create a single pivot and it works perfect. But when I create two pivots, the consequent Excel file renders as corrupt.
By corrupt I mean,
On clicking yes, I get this -
Here is the code I am using to create the pivots -
using XL = ClosedXML.Excel;
...
XL.XLWorkbook wb = new XL.XLWorkbook();
dsData = Session["ExportData"] as DataSet;
var sheet1 = wb.Worksheets.Add("output table");
sheet1.Cell(1, 1).InsertTable(dsData.Tables[0], "output table", true);
// sheet1 is the reference sheet S1
var dataRange = sheet1.RangeUsed();
// First Pivot
XL.IXLWorksheet ptSheet1 = wb.Worksheets.Add("S2");
var pt1 = ptSheet1.PivotTables.AddNew("PivotTable1", ptSheet.Cell(3, 1), dataRange);
pt1.ReportFilters.Add("CX");
pt1.RowLabels.Add("C1");
pt1.RowLabels.Add("C2");
pt1.RowLabels.Add("C3");
pt1.RowLabels.Add("C4");
pt1.ColumnLabels.Add("CL1");
pt1.ColumnLabels.Add("CL2");
pt1.ColumnLabels.Add("CL3");
pt1.Values.Add("V").SummaryFormula = XL.XLPivotSummary.Sum;
// Second Pivot
XL.IXLWorksheet ptSheet2 = wb.Worksheets.Add("S3");
var pt2 = ptSheet2.PivotTables.AddNew("PivotTable2", ptSheet1.Cell(3, 1), dataRange);
pt2.ReportFilters.Add("QQ");
pt2.RowLabels.Add("C1");
pt2.RowLabels.Add("C2");
pt2.ColumnLabels.Add("CL1");
pt2.ColumnLabels.Add("CL2");
pt2.ColumnLabels.Add("CL3");
pt2.Values.Add("V").SummaryFormula = XL.XLPivotSummary.Sum;
C1, C2, C3. C4 and V are the column names in my reference sheet S1.
The issue is caused by a ClosedXML implementation bug.
It can easily be reproduced by using the following snippet (a modified version of their Pivot Tables example) and opening the resulting file in Excel:
static void CreateTestPivotTables(string filePath)
{
var wb = new XLWorkbook();
var wsData = wb.Worksheets.Add("Data");
wsData.Cell("A1").Value = "Category";
wsData.Cell("A2").Value = "A";
wsData.Cell("A3").Value = "B";
wsData.Cell("A4").Value = "B";
wsData.Cell("B1").Value = "Number";
wsData.Cell("B2").Value = 100;
wsData.Cell("B3").Value = 150;
wsData.Cell("B4").Value = 75;
var source = wsData.Range("A1:B4");
for (int i = 1; i <= 2; i++)
{
var name = "PT" + i;
var wsPT = wb.Worksheets.Add(name);
var pt = wsPT.PivotTables.AddNew(name, wsPT.Cell("A1"), source);
pt.RowLabels.Add("Category");
pt.Values.Add("Number")
.ShowAsPctFrom("Category").And("A")
.NumberFormat.Format = "0%";
}
wb.SaveAs(filePath);
}
The bug is located in XLWorkbook_Save.cs - GeneratePivotTables method:
private static void GeneratePivotTables(WorkbookPart workbookPart, WorksheetPart worksheetPart,
XLWorksheet xlWorksheet,
SaveContext context)
{
foreach (var pt in xlWorksheet.PivotTables)
{
var ptCdp = context.RelIdGenerator.GetNext(RelType.Workbook);
var pivotTableCacheDefinitionPart = workbookPart.AddNewPart<PivotTableCacheDefinitionPart>(ptCdp);
GeneratePivotTableCacheDefinitionPartContent(pivotTableCacheDefinitionPart, pt);
var pivotCaches = new PivotCaches();
var pivotCache = new PivotCache {CacheId = 0U, Id = ptCdp};
pivotCaches.AppendChild(pivotCache);
workbookPart.Workbook.AppendChild(pivotCaches);
var pivotTablePart =
worksheetPart.AddNewPart<PivotTablePart>(context.RelIdGenerator.GetNext(RelType.Workbook));
GeneratePivotTablePartContent(pivotTablePart, pt);
pivotTablePart.AddPart(pivotTableCacheDefinitionPart, context.RelIdGenerator.GetNext(RelType.Workbook));
}
}
by the line workbookPart.Workbook.AppendChild(pivotCaches); which adds multiple PivotCaches to workbookPart.Workbook while it's allowed to contain 0 or 1.
With that being said, the only way to fix it is inside the source code by modifying the above method as follows:
private static void GeneratePivotTables(WorkbookPart workbookPart, WorksheetPart worksheetPart,
XLWorksheet xlWorksheet,
SaveContext context)
{
var pivotCaches = workbookPart.Workbook.GetFirstChild<PivotCaches>();
foreach (var pt in xlWorksheet.PivotTables)
{
var ptCdp = context.RelIdGenerator.GetNext(RelType.Workbook);
var pivotTableCacheDefinitionPart = workbookPart.AddNewPart<PivotTableCacheDefinitionPart>(ptCdp);
GeneratePivotTableCacheDefinitionPartContent(pivotTableCacheDefinitionPart, pt);
if (pivotCaches == null)
workbookPart.Workbook.AppendChild(pivotCaches = new PivotCaches());
var pivotCache = new PivotCache { CacheId = (uint)pivotCaches.Count(), Id = ptCdp };
pivotCaches.AppendChild(pivotCache);
var pivotTablePart =
worksheetPart.AddNewPart<PivotTablePart>(context.RelIdGenerator.GetNext(RelType.Workbook));
GeneratePivotTablePartContent(pivotTablePart, pt);
pivotTablePart.PivotTableDefinition.CacheId = pivotCache.CacheId;
pivotTablePart.AddPart(pivotTableCacheDefinitionPart, context.RelIdGenerator.GetNext(RelType.Workbook));
}
}
Update: The good news are that my post triggered a ClosedXML source repository fix by Francois Botha (also credits to petelids who brought it up there), so you can take the code from there until their next release which hopefully will include it.
Try this modification. I made a note where I added an additional line. Also, I think the AddNew() method may have had the wrong worksheet reference? You may have been trying to add a pivot table on top of another one. That may have been the real issue rather than the additional line I added.
using XL = ClosedXML.Excel;
...
XL.XLWorkbook wb = new XL.XLWorkbook();
dsData = Session["ExportData"] as DataSet;
var sheet1 = wb.Worksheets.Add("output table");
sheet1.Cell(1, 1).InsertTable(dsData.Tables[0], "output table", true);
// sheet1 is the reference sheet S1
var dataRange = sheet1.RangeUsed();
PivotCache cache = wb.PivotCaches.Add(dataRange); //---THIS LINE HAS BEEN ADDED---
// First Pivot
XL.IXLWorksheet ptSheet1 = wb.Worksheets.Add("S2");
var pt1 = ptSheet1.PivotTables.AddNew("PivotTable1", ptSheet1.Cell(3, 1), cache);
//Changed ptSheet.Cell... to ptSheet1.Cell...
pt1.ReportFilters.Add("CX");
pt1.RowLabels.Add("C1");
pt1.RowLabels.Add("C2");
pt1.RowLabels.Add("C3");
pt1.RowLabels.Add("C4");
pt1.ColumnLabels.Add("CL1");
pt1.ColumnLabels.Add("CL2");
pt1.ColumnLabels.Add("CL3");
pt1.Values.Add("V").SummaryFormula = XL.XLPivotSummary.Sum;
// Second Pivot
XL.IXLWorksheet ptSheet2 = wb.Worksheets.Add("S3");
var pt2 = ptSheet2.PivotTables.AddNew("PivotTable2", ptSheet2.Cell(3, 1), cache);
//Changed ptSheet1.Cell... to ptSheet2.Cell...
pt2.ReportFilters.Add("QQ");
pt2.RowLabels.Add("C1");
pt2.RowLabels.Add("C2");
pt2.ColumnLabels.Add("CL1");
pt2.ColumnLabels.Add("CL2");
pt2.ColumnLabels.Add("CL3");
pt2.Values.Add("V").SummaryFormula = XL.XLPivotSummary.Sum;
To provide more context, if I simply want to export my model to an Excel file in my ActionResult, I have that worked out. I learned the commends to assign values to the workbook properties and cell formatting and the exported Excel file appears correctly.
My problem is that all that code exists within the ActionResult block. I would like to move out the code that assigns property values to a separate function. The problem I encounter is the null reference error. Below is the paste of the full ActionResult block to provide full context and in case it suggests any secondary issues I hadn't considered.
I tried creating a function in the controller private ExcelPackage AssignWorkbookProperties(ExcelPackage ep, string exportName), and while I had no compile time errors, there was a null argument exception. Essentially, the function either received nothing or returned nothing.
Is there some way I can move the blocked code into a helper function? (I've indicated with comment blocks in the code shown here).
public ActionResult ExportToExcel()
{
string exportName = "FourCourseAudit";
// This allows me to export only the columns I want.
var exportQuery = query.Select(t => new { t.Campus, t.Student_Name, t.Course_Count });
// epp is the model where I gather the predefined property values
var epp = new ExportToExcelProperties();
var prop = epp.WorkbookProperties.Where(t => t.ExportName == exportName);
try
{
byte[] response;
using (var excelFile = new ExcelPackage())
{
// Define worksheet data.
var worksheet = excelFile.Workbook.Worksheets.Add("Sheet1");
/* ------------------------------------------------------------------ */
/* -------------Begin: Move to helper function ---------------------- */
/* ------------------------------------------------------------------ */
// Define workbook properties.
var workbookProperties = excelFile.Workbook.Properties;
workbookProperties.Author = HttpContext.User.Identity.Name;
workbookProperties.Title = prop.Select(t => t.Title).ToString();
workbookProperties.Comments = prop.Select(t => t.Comments).ToString();
workbookProperties.Created = DateTime.Now;
workbookProperties.Category = prop.Select(t => t.Category).ToString();
// Define worksheet contextual data.
worksheet.Cells["A1"].Value = "Title: ";
worksheet.Cells["A2"].Value = "Export Date: ";
worksheet.Cells["A1:A2"].Style.Font.Bold = true;
worksheet.Cells["B1"].Value = prop.Select(t => t.Title).ToString();
worksheet.Cells["B2"].Value = DateTime.Now;
worksheet.Cells["B2"].Style.Numberformat.Format = "mm/dd/yyyy hh:mm";
worksheet.Cells["A1:B2"].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Medium);
worksheet.Cells["A1:B2"].AutoFitColumns();
Color bgColor = ColorTranslator.FromHtml("#2956B2");
worksheet.Cells["A1:B2"].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells["A1:B2"].Style.Fill.BackgroundColor.SetColor(bgColor);
worksheet.Cells["A1:B2"].Style.Font.Color.SetColor(Color.White);
worksheet.Cells["D1:F1"].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells["D1:F1"].Style.Fill.BackgroundColor.SetColor(bgColor);
worksheet.Cells["D1:F1"].Style.Font.Color.SetColor(Color.White);
/* ------------------------------------------------------------------ */
/* ---------------End: Move to helper function ---------------------- */
/* ------------------------------------------------------------------ */
worksheet
.Cells["D1"]
.LoadFromCollection(Collection: exportQuery, PrintHeaders: true)
.Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin);
worksheet.Cells["A1:F200"].AutoFitColumns();
response = excelFile.GetAsByteArray();
}
return File(response, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Export.xlsx");
}
catch (NullReferenceException) {
return ViewBag.Errormsg = "There was a null reference exception.";
}
catch (ArgumentNullException)
{
return ViewBag.Errormsg = "There was an argument null exception.";
}
}
I found a resource that helped me figure out the problem.
The problem was that my function was returning and accepting the wrong object.
I created two different functions. The first function returns type ExcelWorksheet and the second function returns type OfficeProperties. Both functions have ExcelPackage as an input parameter.
So first, this is how clean the ActionResult is now:
public ActionResult ExportToExcel()
{
string exportName = "FourCourseAudit";
// This allows me to export only the columns I want.
var exportQuery = query.Select(t => new { t.Campus, t.Student_Name, t.Course_Count });
try
{
byte[] response;
using (var excelFile = new ExcelPackage())
{
// Use helper functions to fill worksheet and workbook definitions
var worksheet = CreateSheet(excelFile,exportName);
var workbook = AssignProperties(excelFile,exportName);
// Fill worksheet with data to export
worksheet
.Cells["D1"]
.LoadFromCollection(Collection: exportQuery, PrintHeaders: true)
.Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin);
worksheet.Cells["A1:F200"].AutoFitColumns();
response = excelFile.GetAsByteArray();
}
return File(response, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Export.xlsx");
}
catch (NullReferenceException) {
return ViewBag.Errormsg = "There was a null reference exception.";
}
catch (ArgumentNullException)
{
return ViewBag.Errormsg = "There was an argument null exception.";
}
}
For now, these two functions are in the same controller. The more I work out how to generalize them, I might move them elsewhere.
private static ExcelWorksheet CreateSheet(ExcelPackage p,string exportName)
{
var epp = new ExportToExcelProperties().WorkbookProperties;
var prop = epp.Where(t => t.ExportName == "FourCourseAudit").Select(t=>t.Title).Single();
string sheetName = "Sheet1";
p.Workbook.Worksheets.Add(sheetName);
ExcelWorksheet worksheet = p.Workbook.Worksheets[1];
worksheet.Name = sheetName; //Setting Sheet's name
worksheet.Cells.Style.Font.Size = 11; //Default font size for whole sheet
worksheet.Cells.Style.Font.Name = "Calibri"; //Default Font name for whole sheet
// Define worksheet contextual data.
worksheet.Cells["A1"].Value = "Title: ";
worksheet.Cells["A2"].Value = "Export Date: ";
worksheet.Cells["A1:A2"].Style.Font.Bold = true;
worksheet.Cells["B1"].Value = prop;
worksheet.Cells["B2"].Value = DateTime.Now;
worksheet.Cells["B2"].Style.Numberformat.Format = "mm/dd/yyyy hh:mm";
worksheet.Cells["A1:B2"].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Medium);
worksheet.Cells["A1:B2"].AutoFitColumns();
Color bgColor = ColorTranslator.FromHtml("#2956B2");
worksheet.Cells["A1:B2"].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells["A1:B2"].Style.Fill.BackgroundColor.SetColor(bgColor);
worksheet.Cells["A1:B2"].Style.Font.Color.SetColor(Color.White);
worksheet.Cells["D1:F1"].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells["D1:F1"].Style.Fill.BackgroundColor.SetColor(bgColor);
worksheet.Cells["D1:F1"].Style.Font.Color.SetColor(Color.White);
return worksheet;
}
private static OfficeProperties AssignProperties(ExcelPackage p, string exportName)
{
// epp is the model where I gather the predefined property values
var epp = new ExportToExcelProperties().WorkbookProperties;
var query = from t in epp
where t.ExportName == exportName
select new { t.Title, t.Comments, t.Category };
var title = query.Select(t=>t.Title).Single();
var comments = query.Select(t=>t.Comments).Single();
var category = query.Select(t=>t.Category).Single();
OfficeProperties workbookProperties = p.Workbook.Properties;
workbookProperties.Author = System.Web.HttpContext.Current.User.Identity.Name;
workbookProperties.Title = title;
workbookProperties.Comments = comments;
workbookProperties.Created = DateTime.Now;
workbookProperties.Category = category;
return workbookProperties;
}
This directly answers the question of how I can move the worksheet property code outside of the ActionResult, but I can clearly see there can be more done to generalize the code and make it more modular.
I have an excel file which contains lots of data along with icon sets and data bars based on the values in the cell. It looks like this:
I want to import this excel sheet along with the conditional formatting. Is there any library for this?? I went through this http://www.sitecorecleveland.com/resources/blogs-posts/easy_excel_interaction_pt6 but it only imports data not format.
If that's not possible is there code in epplus to have these iconsets in excel sheet. I can have arrows, traffic lights, etc but not these.
I dont think EPP supports custom conditional formatting which are stored as "Workbook Extensions" in the xml of the Excel file. You could copy the xml node of the "extLst" which contains the custom formatting from one worksheet to another. Just make sure there is nothing else beside the cond formatting xml in the node that you do not want copied in which case you will have to select only the child nodes you want.
To test, i created the following excel sheet (temp.xlsx), did a copy.paste of values only and saved to a new file (temp2.xlsx):
Then ran the following and it successfully copied the formatting over:
public void Custom_Condition_Copy_Test()
{
//http://stackoverflow.com/questions/28493050/importing-excel-file-with-all-the-conditional-formatting-rules-to-epplus
//File with custom conditional formatting
var existingFile = new FileInfo(#"c:\temp\temp.xlsx");
//Copy of the file with the conditonal formatting removed
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();
}
}
Might want to put some try's in there but this should get you close.
UPDATE: Based on OPs comment.
If you want to be able to add the custom conditional format without the need of the original file that contains it, I see two options.
Option 1, you do it the more "correct" way and use the DocumentFormat.OpenXml namespace. BUT, this would require you to have the Office Open XML library available which may or may not be so easy depending on the environment you are running this in. You can get it from here http://www.microsoft.com/en-us/download/details.aspx?id=30425 and it comes with a Reflection tool that can generate the code you want which gets you this:
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using X14 = DocumentFormat.OpenXml.Office2010.Excel;
using Excel = DocumentFormat.OpenXml.Office.Excel;
......
WorksheetExtensionList worksheetExtensionList1 = new WorksheetExtensionList();
WorksheetExtension worksheetExtension1 = new WorksheetExtension(){ Uri = "{78C0D931-6437-407d-A8EE-F0AAD7539E65}" };
worksheetExtension1.AddNamespaceDeclaration("x14", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main");
X14.ConditionalFormattings conditionalFormattings1 = new X14.ConditionalFormattings();
X14.ConditionalFormatting conditionalFormatting1 = new X14.ConditionalFormatting();
conditionalFormatting1.AddNamespaceDeclaration("xm", "http://schemas.microsoft.com/office/excel/2006/main");
X14.ConditionalFormattingRule conditionalFormattingRule1 = new X14.ConditionalFormattingRule(){ Type = ConditionalFormatValues.IconSet, Priority = 2, Id = "{CD6B2710-0474-449D-881A-22CFE15D011D}" };
X14.IconSet iconSet1 = new X14.IconSet(){ IconSetTypes = X14.IconSetTypeValues.FiveArrows, Custom = true };
X14.ConditionalFormattingValueObject conditionalFormattingValueObject1 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula1 = new Excel.Formula();
formula1.Text = "0";
conditionalFormattingValueObject1.Append(formula1);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject2 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula2 = new Excel.Formula();
formula2.Text = "20";
conditionalFormattingValueObject2.Append(formula2);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject3 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula3 = new Excel.Formula();
formula3.Text = "40";
conditionalFormattingValueObject3.Append(formula3);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject4 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula4 = new Excel.Formula();
formula4.Text = "60";
conditionalFormattingValueObject4.Append(formula4);
X14.ConditionalFormattingValueObject conditionalFormattingValueObject5 = new X14.ConditionalFormattingValueObject(){ Type = X14.ConditionalFormattingValueObjectTypeValues.Percent };
Excel.Formula formula5 = new Excel.Formula();
formula5.Text = "80";
conditionalFormattingValueObject5.Append(formula5);
X14.ConditionalFormattingIcon conditionalFormattingIcon1 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeSymbols, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon2 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTrafficLights1, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon3 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)0U };
X14.ConditionalFormattingIcon conditionalFormattingIcon4 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)1U };
X14.ConditionalFormattingIcon conditionalFormattingIcon5 = new X14.ConditionalFormattingIcon(){ IconSet = X14.IconSetTypeValues.ThreeTriangles, IconId = (UInt32Value)2U };
iconSet1.Append(conditionalFormattingValueObject1);
iconSet1.Append(conditionalFormattingValueObject2);
iconSet1.Append(conditionalFormattingValueObject3);
iconSet1.Append(conditionalFormattingValueObject4);
iconSet1.Append(conditionalFormattingValueObject5);
iconSet1.Append(conditionalFormattingIcon1);
iconSet1.Append(conditionalFormattingIcon2);
iconSet1.Append(conditionalFormattingIcon3);
iconSet1.Append(conditionalFormattingIcon4);
iconSet1.Append(conditionalFormattingIcon5);
conditionalFormattingRule1.Append(iconSet1);
Excel.ReferenceSequence referenceSequence1 = new Excel.ReferenceSequence();
referenceSequence1.Text = "A1:C201";
conditionalFormatting1.Append(conditionalFormattingRule1);
conditionalFormatting1.Append(referenceSequence1);
conditionalFormattings1.Append(conditionalFormatting1);
worksheetExtension1.Append(conditionalFormattings1);
worksheetExtensionList1.Append(worksheetExtension1);
....
worksheet1.Append(worksheetExtensionList1);
Option 2 would be to do as you are asking and perform string manipulation. This is much easier but it is a slightly dirty in that you are messing with strings rather then objects but if the only thing you need to set is the cell range that doesnt seem so bad. I used the test method above to extract the string with = extensionlistnode.OuterXml:
[TestMethod]
public void Custom_Condition_From_String_Test()
{
//http://stackoverflow.com/questions/28493050/importing-excel-file-with-all-the-conditional-formatting-rules-to-epplus
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.Add(new DataColumn("Col1", typeof(int)));
datatable.Columns.Add(new DataColumn("Col2", typeof(int)));
datatable.Columns.Add(new DataColumn("Col3", typeof(int)));
for (var i = 0; i < 20; i++)
{
var row = datatable.NewRow();
row["Col1"] = i;
row["Col2"] = i * 10;
row["Col3"] = i * 100;
datatable.Rows.Add(row);
}
//Copy of the file with the conditonal formatting removed
var existingFile2 = new FileInfo(#"c:\temp\temp2.xlsx");
if (existingFile2.Exists)
existingFile2.Delete();
using (var package2 = new ExcelPackage(existingFile2))
{
//Add the data
var ws = package2.Workbook.Worksheets.Add("Content");
ws.Cells.LoadFromDataTable(datatable, true);
//The XML String extracted from the orginal excel doc using '= extensionlistnode.OuterXml'
var cellrange = "A1:C201";
var rawxml = String.Format(
"<extLst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><ext uri=\"{{78C0D931-6437-407d-A8EE-F0AAD7539E65}}\" xmlns:x14=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\"><x14:conditionalFormattings><x14:conditionalFormatting xmlns:xm=\"http://schemas.microsoft.com/office/excel/2006/main\"><x14:cfRule type=\"iconSet\" priority=\"2\" id=\"{{CD6B2710-0474-449D-881A-22CFE15D011D}}\"><x14:iconSet iconSet=\"5Arrows\" custom=\"1\"><x14:cfvo type=\"percent\"><xm:f>0</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>20</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>40</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>60</xm:f></x14:cfvo><x14:cfvo type=\"percent\"><xm:f>80</xm:f></x14:cfvo><x14:cfIcon iconSet=\"3Symbols\" iconId=\"0\" /><x14:cfIcon iconSet=\"3TrafficLights1\" iconId=\"0\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"0\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"1\" /><x14:cfIcon iconSet=\"3Triangles\" iconId=\"2\" /></x14:iconSet></x14:cfRule><xm:sqref>{0}</xm:sqref></x14:conditionalFormatting></x14:conditionalFormattings></ext></extLst>"
, cellrange);
var newxdoc = new XmlDocument();
newxdoc.LoadXml(rawxml);
//Create the import node and append it to the end of the xml document
var xdoc2 = ws.WorksheetXml;
var newnode = xdoc2.ImportNode(newxdoc.FirstChild, true);
xdoc2.LastChild.AppendChild(newnode);
package2.Save();
}
}