c# Merged cells as header address epplus - c#

How do I set HeaderAddress to two cells of a clustered bar chart in EPPlus? I have the below data from my database output, where first column is as you can see merged cells.
I am looking for the following layout of the data
Please take note that the images are from a replica of the data, but generated inside Excel.
What I've tried so far is basically
ExcelChartSerie s = chart.Series.Add(axis.Address, xAxis.Address);
s.HeaderAddress = new ExcelAddress(startRow + r, GetColumnNumberByName(startColumn), startRow + 1, GetColumnNumberByName(startColumn) + 1);
where I more or less select the current row and two columns. This gives me "Address must be a row, column or single cell", but in order to get this to work, I must select multiple cells, no?

Epplus does not have the ability to set it. it is not so difficult but it requires XML manipulation. Kind of ugly but it gets the job done. Not knowing your code I made up a quick unit test that demos. It has to match to the right chart type so if it is not BarClustered let me know:
[TestMethod]
public void Chart_Meged_Header_Test()
{
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[]
{
new DataColumn("Col1", typeof (string)),
new DataColumn("Col2", typeof (int))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = $"item {(i%2 == 0 ? "A" : "B")}";
row[1] = i * 10;
datatable.Rows.Add(row);
}
//Create a test file
var fileInfo = new FileInfo(#"c:\temp\Chart_Meged_Header_Test.xlsx");
if (fileInfo.Exists)
fileInfo.Delete();
using (var pck = new ExcelPackage(fileInfo))
{
var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");
worksheet.Cells["B1"].LoadFromDataTable(datatable, true);
worksheet.Column(4).Style.Numberformat.Format = "m/d/yyyy";
var chart = worksheet.Drawings.AddChart("chart test", eChartType.BarClustered);
var serie = chart.Series.Add(worksheet.Cells["C2:C11"], worksheet.Cells["B2:B11"]);
chart.SetPosition(0, 0, 3, 0);
chart.SetSize(120);
//Add merged headers
worksheet.Cells["A2"].Value = "Group 1";
worksheet.Cells["A2:A6"].Merge = true;
worksheet.Cells["A7"].Value = "Group 2";
worksheet.Cells["A7:A11"].Merge = true;
//Get reference to the worksheet xml for proper namespace
var chartXml = chart.ChartXml;
var nsm = new XmlNamespaceManager(chartXml.NameTable);
var nsuri = chartXml.DocumentElement.NamespaceURI;
nsm.AddNamespace("c", nsuri);
//Get the Series ref and its cat
var serNode = chartXml.SelectSingleNode("c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser", nsm);
var catNode = serNode.SelectSingleNode("c:cat", nsm);
//Get Y axis reference to replace with multi level node
var numRefNode = catNode.SelectSingleNode("c:numRef", nsm);
var multiLvlStrRefNode = chartXml.CreateNode(XmlNodeType.Element, "c:multiLvlStrRef", nsuri);
//Set the proper cell reference and replace the node
var fNode = chartXml.CreateElement("c:f", nsuri);
fNode.InnerXml = numRefNode.SelectSingleNode("c:f", nsm).InnerXml;
fNode.InnerXml = fNode.InnerXml.Replace("$B$2", "$A$2");
multiLvlStrRefNode.AppendChild(fNode);
catNode.ReplaceChild(multiLvlStrRefNode, numRefNode);
//Set the multi level flag
var noMultiLvlLblNode = chartXml.CreateElement("c:noMultiLvlLbl", nsuri);
var att = chartXml.CreateAttribute("val");
att.Value = "0";
noMultiLvlLblNode.Attributes.Append(att);
var catAxNode = chartXml.SelectSingleNode("c:chartSpace/c:chart/c:plotArea/c:catAx", nsm);
catAxNode.AppendChild(noMultiLvlLblNode);
pck.Save();
}
}
Gives this as the output:

Related

ClosedXML how to set cell width for whole workbook?

i'm using closedXml to export data to Excel. But my columns need to be adjusted to the content therefore i've tried to specify the colums width by hand:
var ws = workbook.Worksheets.Add("Column Settings");
col.Width = 50;
Wasn't working for me.I've also tried:
Columns().AdjustToContents();
But also no results. So looking for an solution that will pre set the cell width in the whole sheet or workbook.
This is my code:
//variables
XLWorkbook workbook = new XLWorkbook();
var wsDep = workbook.Worksheets.Add("MBR");
wsDep.Columns().AdjustToContents();
DataTable dt = new DataTable() { TableName = "MBR" };
DataSet ds = new DataSet();
TagRepo tr = new TagRepo();
List<string> tags = new List<string>();
tags.Add("TimeStamp");
//list min,max,avg.
foreach (var item in tr.getAllTagNames())
{
tags.Add(item.Trim()+"_Min");
tags.Add(item.Trim() + "_Max");
tags.Add(item.Trim() + "_Avg");
}
//input data
int l = tags.Count();
//Colum names
string[] columns = new string [l];
for (int i = 0; i < l; i++)
{
columns[i] = tags[i];
}
var rows = new object[][]
{
new object[] {"1", 2, false },
new object[] { "test", 10000, 19.9 },
};
//Add columns
dt.Columns.AddRange(columns.Select(c => new DataColumn(c.ToString())).ToArray());
//Add rows
foreach (var row in rows)
{
dt.Rows.Add(row);
}
//Convert datatable to dataset and add it to the workbook as worksheet
ds.Tables.Add(dt);
workbook.Worksheets.Add(ds);
//save
//string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string savePath = Path.Combine(#"\\nas\TreviMap\Pilootproeven en opvolging-IC\Export data", "toon.xlsx");
workbook.SaveAs(savePath, false);
You need to call Columns().AdjustToContents(); after you have filled the worksheet(s).

How to sort PivotTable (DESC values) with Interop.Excel (C#)

How to sort a PivotTable with Interop.Excel and C#?
I got the generation of the PivotTable fully like I want it to, but struggling with the sorting...
Sorting should be done, that for "Title" and "SubTitle" the fields "Value2" are always having the highest descending values at the top per project.
Was trying around via Range.Sort() but without luck.
Tested way:
Defining a region (like I did manually in Excel for figuring out the way) with "C4" and the doing a Range.Sort on this. But then it is working for the first project, but not for all. Imho, because I set this only to the pointed range to one cell. The next try was to extend the range, but then I always got HRESULT exceptions with just an memory address given (useless for me).
The screenshots are showing the wanted sorting.
public void GeneratePivot()
{
const string numberFormat = "#,##0 €;-#,##0 €";
var missing = Type.Missing;
string dataContext = #"DataContext";
#region // Create Data
var dt = new DataTable();
dt.Columns.Add(new DataColumn() { ColumnName = "Project", DataType = typeof(string) });
dt.Columns.Add(new DataColumn() { ColumnName = "Title", DataType = typeof(string) });
dt.Columns.Add(new DataColumn() { ColumnName = "SubTitle", DataType = typeof(string) });
dt.Columns.Add(new DataColumn() { ColumnName = "Value1", DataType = typeof(decimal) });
dt.Columns.Add(new DataColumn() { ColumnName = "Value2", DataType = typeof(decimal) });
var row1 = dt.NewRow();
row1["Project"] = "Project1";
row1["Title"] = "Title1";
row1["SubTitle"] = "SubTitle1-1";
row1["Value1"] = 1000M;
row1["Value2"] = 40000M;
dt.Rows.Add(row1);
var row2 = dt.NewRow();
row2["Project"] = "Project2-1";
row2["Title"] = "Title2";
row2["SubTitle"] = "SubTitle2-1";
row2["Value1"] = 100M;
row2["Value2"] = 4000M;
dt.Rows.Add(row2);
var row3 = dt.NewRow();
row3["Project"] = "Project2-2";
row3["Title"] = "Title2";
row3["SubTitle"] = "SubTitle2-2";
row3["Value1"] = 220M;
row3["Value2"] = 222000M;
dt.Rows.Add(row3);
var row4 = dt.NewRow();
row4["Project"] = "Project3-1";
row4["Title"] = "Title3";
row4["SubTitle"] = "SubTitle3-1";
row4["Value1"] = 32423M;
row4["Value2"] = 430M;
dt.Rows.Add(row4);
var row5 = dt.NewRow();
row5["Project"] = "Project3-2";
row5["Title"] = "Title3";
row5["SubTitle"] = "SubTitle3-2";
row5["Value1"] = 2341M;
row5["Value2"] = 4002000M;
dt.Rows.Add(row5);
#endregion
// Create Workbook with Excel Interop
Excel.Application excelApplication = new Excel.Application();
Excel.Workbooks workbooks = excelApplication.Workbooks;
var workbook = workbooks.Add();
#region // Create DataSheet
Excel.Worksheet worksheet1 = workbook.Sheets[1];
worksheet1.Name = "DataSheet";
var colsCount = dt.Columns.Count;
var rowsCount = dt.Rows.Count;
Excel.Range range;
// Create DataArray from DataTable
object[,] dtArray = new object[rowsCount, colsCount];
for (int i = 0; i < rowsCount; i++)
{
for (int j = 0; j < colsCount; j++) { dtArray[i, j] = dt.Rows[i][j]; }
}
// Create header
range = worksheet1.Cells[1, 1];
range = range.get_Resize(1, colsCount);
range.NumberFormat = "#";
range.Font.Bold = true;
range.Value = new string[5] { "Project", "Title", "SubTitle", "Value1", "Value2" };
// Get an Excel Range of the same dimensions
range = (Excel.Range)worksheet1.Cells[2, 1];
range = range.get_Resize(rowsCount, colsCount);
// Assign the 2-d array to the Excel Range
range.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, dtArray);
range = worksheet1.UsedRange;
worksheet1.Names.Add("DataContext", range);
#endregion
#region // Create PivotSheet
Excel.Worksheet worksheet2 = workbook.Sheets.Add(After: workbook.Sheets[workbook.Sheets.Count]);
worksheet2.Name = "PivotSheet";
Excel.PivotCache pivotCache;
Excel.PivotTable pivotTable;
Excel.Range pivotData;
Excel.Range pivotDestination;
// Select a range of data for the Pivot Table.
pivotData = worksheet1.get_Range(dataContext);
// Select location of the Pivot Table.
pivotDestination = worksheet2.get_Range("A1", missing);
// create Pivot Cache and Pivot Table
pivotCache = (Excel.PivotCache)workbook.PivotCaches()
.Add(Excel.XlPivotTableSourceType.xlDatabase, pivotData);
pivotTable = (Excel.PivotTable)worksheet2.PivotTables()
.Add(PivotCache: pivotCache, TableDestination: pivotDestination, TableName: dataContext);
// Style Pivot Table
pivotTable.Format(Excel.XlPivotFormatType.xlReport2);
pivotTable.InGridDropZones = false;
pivotTable.SmallGrid = false;
pivotTable.TableStyle2 = "PivotStyleLight16";
// ROW FIELDS
Excel.PivotField rowField3 = (Excel.PivotField)pivotTable.PivotFields("SubTitle");
rowField3.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
rowField3.LayoutForm = Excel.XlLayoutFormType.xlOutline;
rowField3.LayoutSubtotalLocation = Excel.XlSubtototalLocationType.xlAtTop;
rowField3.LayoutCompactRow = true;
Excel.PivotField rowField2 = (Excel.PivotField)pivotTable.PivotFields("Title");
rowField2.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
rowField2.LayoutForm = Excel.XlLayoutFormType.xlOutline;
rowField2.LayoutSubtotalLocation = Excel.XlSubtototalLocationType.xlAtTop;
rowField2.LayoutCompactRow = true;
Excel.PivotField rowField1 = (Excel.PivotField)pivotTable.PivotFields("Project");
rowField1.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
rowField1.LayoutForm = Excel.XlLayoutFormType.xlOutline;
rowField1.LayoutSubtotalLocation = Excel.XlSubtototalLocationType.xlAtTop;
rowField1.LayoutCompactRow = true;
// FILTER FIELDS
Excel.PivotField pageField1 = (Excel.PivotField)pivotTable.PivotFields("Project");
pageField1.Orientation = Excel.XlPivotFieldOrientation.xlPageField;
pageField1.EnableMultiplePageItems = true;
// DATA FIELDS
int position = 1;
Excel.PivotField dataField1 = (Excel.PivotField)pivotTable.PivotFields("Value1");
dataField1.Orientation = Excel.XlPivotFieldOrientation.xlDataField;
dataField1.Function = Excel.XlConsolidationFunction.xlSum;
dataField1.NumberFormat = numberFormat;
dataField1.Position = position++;
Excel.PivotField dataField2 = (Excel.PivotField)pivotTable.PivotFields("Value2");
dataField2.Orientation = Excel.XlPivotFieldOrientation.xlDataField;
dataField2.Function = Excel.XlConsolidationFunction.xlSum;
dataField2.NumberFormat = numberFormat;
dataField2.Position = position++;
#endregion
// Close Excel
workbook.SaveAs("Interop.Excel_Pivot.xlsx");
workbook.Close();
excelApplication.Quit();
}
This did it!
var pivotLine = (Excel.PivotLine)pivotTable.PivotColumnAxis.PivotLines[2];
rowField2.AutoSortEx((int)Excel.XlSortOrder.xlDescending, "Summe von Value2", pivotLine, 1);
rowField3.AutoSortEx((int)Excel.XlSortOrder.xlDescending, "Summe von Value2", pivotLine, 1);
Hint: Beware of, that given String "Summe von " is german language and will be "Sum of " with Excel in English. ;)

Gembox spreedsheet excel file upload

I want to read excel file using gembox like first row as header to check weather required column are present or not.
You can find the reading example here.
Also, here is another example of how you can read just the first row:
var workbook = ExcelFile.Load("Sample.xlsx");
var worksheet = workbook.Worksheets[0];
var firstRow = worksheet.Rows[0];
foreach (var cell in firstRow.AllocatedCells)
{
Console.WriteLine(cell.Name);
Console.WriteLine(cell.Value);
Console.WriteLine("---");
}
I hope this helps.
UPDATE
Another attempt to guess what exactly is requested here:
string[] expectedColumns = new string[] { "Column 1", "Column 2", "Column 3" };
var workbook = ExcelFile.Load("Sample.xlsx");
var worksheet = workbook.Worksheets[0];
var firstRow = worksheet.Rows[0];
string[] actualColumns = firstRow.AllocatedCells
.Select(cell => cell.Value != null ? cell.Value.ToString() : string.Empty)
.ToArray();
for (int i = 0; i < expectedColumns.Length; i++)
if (expectedColumns[i] != actualColumns[i])
throw new Exception("Unexpected column name detected!");
Note, Select and ToArraymethods are provided by System.Linq.

C# Change Excel Chart Axis Format using EPPLUS

Is there anyway to change the chart X or Y axis to set a custom label formatting?
For example, in the XAxis my labels are datetime but the formatting is yyyy-mm-dd hh:mm:ss. I would like to change it to just yyyy-mm-dd.
Was wondering if it is possible in EPPLUS without using interop
This is a bit of a pain with charts and EPPlus. They have exposed the chart.XAxis.Format property to set the format but does not account for the attribute souceLinked which, when not set to 0 (default) tells excel to just go with the source data format. So you have to set it manually with xml:
[TestMethod]
public void Chart_Row_Offset_Test()
{
//Throw in some data
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[]
{
new DataColumn("Col1", typeof (DateTime)), new DataColumn("Col2", typeof (int)), new DataColumn("Col3", typeof (object))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = DateTime.Now.AddDays(i) ;row[1] = i*10;row[2] = Path.GetRandomFileName();
datatable.Rows.Add(row);
}
//Create a test file
var fi = new FileInfo(#"c:\temp\Chart_Row_Offset.xlsx");
if (fi.Exists)
fi.Delete();
using (var pck = new ExcelPackage(fi))
{
var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");
worksheet.Cells.LoadFromDataTable(datatable, true);
//This would be the axis label format if the XML Attribute is not set below
worksheet.Cells["A2:A11"].Style.Numberformat.Format = "m/d/yyyy";
const int EXCELCHARTWIDTH = 375;
const int EXCELCHARTHEIGHT = 350;
const double EXCELDEFAULTROWHEIGHT = 20.0; //Assuming standard screen dpi
var startCell = (ExcelRangeBase)worksheet.Cells["A1"];
for (var i = 0; i < 10; i++)
{
var chart = worksheet.Drawings.AddChart("chart " + i, eChartType.XYScatterLines);
chart.SetSize(EXCELCHARTWIDTH, EXCELCHARTHEIGHT);
chart.SetPosition(startCell.Start.Row, 0, startCell.Start.Column, 0);
chart.XAxis.Format = "yyyy-mm-dd";
var series = chart.Series.Add(worksheet.Cells["B2:B11"], worksheet.Cells["A2:A11"]);
var chartcellheight = (int)Math.Ceiling(EXCELCHARTHEIGHT / EXCELDEFAULTROWHEIGHT);
startCell = startCell.Offset(chartcellheight, 0);
//Get reference to the chart xml for proper namespace
var xdoc = chart.ChartXml;
var nsm = new XmlNamespaceManager(xdoc.NameTable);
nsm.AddNamespace("default", xdoc.DocumentElement.NamespaceURI);
//Add the attribute to not link to the source data format
var att = xdoc.CreateAttribute("sourceLinked");
att.Value = "0";
var numFmtNode = xdoc.SelectSingleNode("/default:chartSpace/default:chart/default:plotArea/default:valAx/default:numFmt", nsm);
numFmtNode.Attributes.Append(att);
}
pck.Save();
}
}

Exporting to Excel rich text box

In c#, Rich text content are saved to database in below format
< b>Hello< /b>.
I need to export this to MS Excel sheet as Hello (with bold letters)
When ever i try to export the content in excel , its not showing in rich text. Kindly help me . Thanks,
var workbook = new HSSFWorkbook();
var sheet = workbook.CreateSheet("");
int rowNumber = 1;
NPOI.SS.UserModel.IFont font = workbook.CreateFont();
font.Boldweight = (short)(NPOI.SS.UserModel.FontBoldWeight.BOLD);
font.FontHeight = 210;
font.FontName = "Calibri";
NPOI.SS.UserModel.ICellStyle headerCell = workbook.CreateCellStyle();
headerCell.FillBackgroundColor = NPOI.SS.UserModel.IndexedColors.GREY_25_PERCENT.Index;
headerCell.FillForegroundColor = NPOI.SS.UserModel.IndexedColors.GREY_25_PERCENT.Index;
headerCell.FillPattern = NPOI.SS.UserModel.FillPatternType.SOLID_FOREGROUND;
headerCell.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.CENTER;
headerCell.WrapText = true;
headerCell.SetFont(font);
headerCell.BorderBottom = NPOI.SS.UserModel.BorderStyle.THIN;
headerCell.BorderLeft = NPOI.SS.UserModel.BorderStyle.THIN;
headerCell.BorderRight = NPOI.SS.UserModel.BorderStyle.THIN;
headerCell.BorderTop = NPOI.SS.UserModel.BorderStyle.THIN;
NPOI.SS.UserModel.IFont fontShort = workbook.CreateFont();
fontShort.Boldweight = (short)(NPOI.SS.UserModel.FontBoldWeight.NORMAL);
fontShort.FontName = "Calibri";
NPOI.SS.UserModel.ICellStyle normalCell = workbook.CreateCellStyle();
normalCell.FillBackgroundColor = 50;
normalCell.VerticalAlignment = NPOI.SS.UserModel.VerticalAlignment.CENTER;
normalCell.WrapText = true;
normalCell.SetFont(fontShort);
normalCell.BorderBottom = NPOI.SS.UserModel.BorderStyle.THIN;
normalCell.BorderLeft = NPOI.SS.UserModel.BorderStyle.THIN;
normalCell.BorderRight = NPOI.SS.UserModel.BorderStyle.THIN;
normalCell.BorderTop = NPOI.SS.UserModel.BorderStyle.THIN;
NPOI.SS.UserModel.ICellStyle emptyCell = workbook.CreateCellStyle();
emptyCell.SetFont(font);
emptyCell.BorderBottom = NPOI.SS.UserModel.BorderStyle.NONE;
emptyCell.BorderLeft = NPOI.SS.UserModel.BorderStyle.NONE;
emptyCell.BorderRight = NPOI.SS.UserModel.BorderStyle.NONE;
emptyCell.BorderTop = NPOI.SS.UserModel.BorderStyle.NONE;
var headerRow = sheet.CreateRow(0);
var output ="<b>HelloWorld</b>"
var row = sheet.CreateRow(rowNumber++);
row.CreateCell(0).SetCellValue(output);
row.Cells[0].CellStyle = normalCell;

Categories