OpenXML Spread sheet style always set to the first font - c#

I am using C#.net with OpenXML for creating a spread sheet. I create few fonts and added to the style sheet . Th problem is I can't switch to the second font.
private Cell CreateCell(string text, uint styleIndex)
{
Cell cell = new Cell();
cell.StyleIndex = styleIndex;
cell.DataType = ResolveCellDataTypeOnValue(text);
cell.CellValue = new CellValue(text);
return cell;
}
using (SpreadsheetDocument document = SpreadsheetDocument.Create("somefile.xls", SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
WorkbookStylesPart sp = workbookPart.AddNewPart<WorkbookStylesPart>();
sp.Stylesheet = new DocumentFormat.OpenXml.Spreadsheet.Stylesheet();
Stylesheet stylesheet = sp.Stylesheet;
stylesheet.Fonts = new DocumentFormat.OpenXml.Spreadsheet.Fonts();
Font font = new Font(new FontSize() { Val = 11 }, new DocumentFormat.OpenXml.Spreadsheet.Color() { Rgb = HexBinaryValue.FromString("4b53cb") }, new Bold());
Font font2 = new Font(new FontSize() { Val = 15 }, new DocumentFormat.OpenXml.Spreadsheet.Color() { Rgb = HexBinaryValue.FromString("c93653") }, new Italic());
stylesheet.Fonts.Append(font);
stylesheet.Fonts.Append(font2);
stylesheet.Save();
SheetData sheetData = new DocumentFormat.OpenXml.Spreadsheet.SheetData();
MergeCells mergeCells = new MergeCells();
workRow.Append(CreateCell("Some text", (UInt32Value)2U));
worksheetPart.Worksheet = new Worksheet(sheetData);
worksheetPart.Worksheet.Save();
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
Sheet sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Trial" };
sheets.Append(sheet);
workbookPart.Workbook.Save();
}

Related

Write IEnumerable<object> to Excel, File isn't editable in Excel web

I have written this class to write a collection of any type to an excel file:
public static class ExcelWriter
{
public static void WriteToExcelFile(IEnumerable<object> collection, string filePath)
{
if (collection?.Any() != true || String.IsNullOrWhiteSpace(filePath))
return;
if (File.Exists(filePath))
File.Delete(filePath);
using (var document = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
Sheet sheet = new Sheet()
{
Id = workbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
sheets.Append(sheet);
workbookPart.Workbook.Save();
SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
sheetData.AppendChild(generateHeaderRow(collection.First().GetType()));
foreach (var item in collection)
{
sheetData.AppendChild(generateValuesRow(item));
}
worksheetPart.Worksheet.Save();
}
}
private static Row generateHeaderRow(Type dataType)
{
var propertyNames = dataType.GetProperties().Select(p => p.Name).ToList();
var headerCells = new Cell[propertyNames.Count];
for (int i = 0; i < propertyNames.Count; i++)
{
headerCells[i] = createCell(propertyNames[i], CellValues.String);
}
return new Row(headerCells);
}
private static Row generateValuesRow(object rowData)
{
var cells = new List<Cell>();
foreach (var property in rowData.GetType().GetProperties())
{
var propertyValue = property.GetValue(rowData);
cells.Add(createCell(propertyValue.ToString(), getCellValueByType(propertyValue)));
}
return new Row(cells);
}
private static CellValues getCellValueByType(object propertyValue)
{
string propertyValueString = propertyValue.ToString();
if (Double.TryParse(propertyValueString, out _))
{
return CellValues.Number;
}
if (DateTime.TryParse(propertyValueString, out _))
{
return CellValues.Date;
}
if (Boolean.TryParse(propertyValueString, out _))
{
return CellValues.Boolean;
}
return CellValues.String;
}
private static Cell createCell(string value, CellValues dataType)
{
return new Cell()
{
CellValue = new CellValue(value),
DataType = new EnumValue<CellValues>(dataType)
};
}
}
This actually generates the excel file, but when I open that file in Excel web it opens in Viewing mode and says you need to repair it, in order to edit(Excel can't repair it though):
I changed my code to work with a specific type and it worked without this issue. That code looks like this:
public class TypeDependentWriter
{
public static void WriteToExcelFile(IEnumerable<TestDataType> collection, string filePath)
{
if (collection?.Any() != true || String.IsNullOrWhiteSpace(filePath))
return;
if (File.Exists(filePath))
File.Delete(filePath);
using (var document = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
Sheet sheet = new Sheet()
{
Id = workbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
sheets.Append(sheet);
workbookPart.Workbook.Save();
SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
// Header row.
Row row = new Row();
row.Append(
createCell("Id", CellValues.String),
createCell("Name", CellValues.String),
createCell("Birth Date", CellValues.String));
sheetData.AppendChild(row);
// Data rows.
foreach (var item in collection)
{
row = new Row();
row.Append(
createCell(item.Id.ToString(), CellValues.Number),
createCell(item.Name, CellValues.String),
createCell(item.DateOfBirth.ToString("yyyy/MM/dd"), CellValues.String));
sheetData.AppendChild(row);
}
worksheetPart.Worksheet.Save();
}
}
private static Cell createCell(string value, CellValues dataType)
{
return new Cell()
{
CellValue = new CellValue(value),
DataType = new EnumValue<CellValues>(dataType)
};
}
}
How can I make the first code work?
The reason my second code wasn't generating the issue was this part:
row.Append(
createCell(item.Id.ToString(), CellValues.Number),
createCell(item.Name, CellValues.String),
createCell(item.DateOfBirth.ToString("yyyy/MM/dd"), CellValues.String /* I'm using string for DateTime here. */));
I figured out that I should:
use CellValues.Number for dates.
and call .ToOADate() extension method on the DateTime variable before calling ToString().
I should define a cell style and apply that style to the cells that store dates.
So I'm now creating date cells this way:
var dateCell = new Cell()
{
CellValue = new CellValue(((DateTime)propertyValue).ToOADate()
.ToString(CultureInfo.InvariantCulture)),
DataType = new EnumValue<CellValues>(CellValues.Number),
StyleIndex = 1 /* Style index for date cells */
}
And the style for date cells can be defined this way(I took this from Dave Williams post):
var CellFormats = new CellFormats();
CellFormats.Append(new CellFormat()
{
BorderId = 0,
FillId = 0,
FontId = 0,
NumberFormatId = 14,
FormatId = 0,
ApplyNumberFormat = true
});
CellFormats.Count = (uint)CellFormats.ChildElements.Count;
var StyleSheet = new Stylesheet();
StyleSheet.Append(CellFormats);
But if you don't have any other defined Fill, Font... this is not going to work because we're using FillId = 0, etc. which we haven't defined, a minimum style you can define to get this work also can be found on Dave Williams post:
private static Stylesheet GetStylesheet()
{
var StyleSheet = new Stylesheet();
// Create "fonts" node.
var Fonts = new Fonts();
Fonts.Append(new Font()
{
FontName = new FontName() { Val = "Calibri" },
FontSize = new FontSize() { Val = 11 },
FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
});
Fonts.Count = (uint)Fonts.ChildElements.Count;
// Create "fills" node.
var Fills = new Fills();
Fills.Append(new Fill()
{
PatternFill = new PatternFill() { PatternType = PatternValues.None }
});
Fills.Append(new Fill()
{
PatternFill = new PatternFill() { PatternType = PatternValues.Gray125 }
});
Fills.Count = (uint)Fills.ChildElements.Count;
// Create "borders" node.
var Borders = new Borders();
Borders.Append(new Border()
{
LeftBorder = new LeftBorder(),
RightBorder = new RightBorder(),
TopBorder = new TopBorder(),
BottomBorder = new BottomBorder(),
DiagonalBorder = new DiagonalBorder()
});
Borders.Count = (uint)Borders.ChildElements.Count;
// Create "cellStyleXfs" node.
var CellStyleFormats = new CellStyleFormats();
CellStyleFormats.Append(new CellFormat()
{
NumberFormatId = 0,
FontId = 0,
FillId = 0,
BorderId = 0
});
CellStyleFormats.Count = (uint)CellStyleFormats.ChildElements.Count;
// Create "cellXfs" node.
var CellFormats = new CellFormats();
// A default style that works for everything but DateTime
CellFormats.Append(new CellFormat()
{
BorderId = 0,
FillId = 0,
FontId = 0,
NumberFormatId = 0,
FormatId = 0,
ApplyNumberFormat = true
});
// A style that works for DateTime (just the date)
CellFormats.Append(new CellFormat()
{
BorderId = 0,
FillId = 0,
FontId = 0,
NumberFormatId = 14, // or 22 to include the time
FormatId = 0,
ApplyNumberFormat = true
});
CellFormats.Count = (uint)CellFormats.ChildElements.Count;
// Create "cellStyles" node.
var CellStyles = new CellStyles();
CellStyles.Append(new CellStyle()
{
Name = "Normal",
FormatId = 0,
BuiltinId = 0
});
CellStyles.Count = (uint)CellStyles.ChildElements.Count;
// Append all nodes in order.
StyleSheet.Append(Fonts);
StyleSheet.Append(Fills);
StyleSheet.Append(Borders);
StyleSheet.Append(CellStyleFormats);
StyleSheet.Append(CellFormats);
StyleSheet.Append(CellStyles);
return StyleSheet;
}

How to add colour to particular excel columns based on conditions using openxml?

I'm generating an excel sheet and I want to color the excel columns based on conditions. Now my all excel columns get red colors. I want to color only particular column names.
I'm using open XML for generating excel, is there any way to find the cells to apply color
using (SpreadsheetDocument document = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
Sheet sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Template" };
sheets.Append(sheet);
var stylesheet = new Stylesheet() { MCAttributes = new MarkupCompatibilityAttributes() { Ignorable = "x14ac" } };
stylesheet.AddNamespaceDeclaration("mc", "http: //schemas.openxmlformats.org/markup-compatibility/2006");
stylesheet.AddNamespaceDeclaration("x14ac", "http: //schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
var fills = new Fills() { Count = 5U };
var fonts = new Fonts() { Count = 1U, KnownFonts = true };
// var cellFormats = new CellFormats();
Font font = new Font();
font.Append(new Color() { Rgb = "ff0000" });
fonts.Append(font);
// cellFormats.AppendChild(new CellFormat() { FontId = 0U });
stylesheet.Append(fonts);
stylesheet.Append(fills);
//stylesheet.Append(cellFormats);
//stylesheet.Append(fill);
var stylePart = workbookPart.AddNewPart<WorkbookStylesPart>();
stylePart.Stylesheet = stylesheet;
stylePart.Stylesheet.Save();
workbookPart.Workbook.Save();
SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
// Constructing header
Row row = new Row();
foreach (DataExchangeDefinition a in importColList)
{
defnExist = true;
if (a.MustFieldYN == true)
{
row.Append(
ConstructCell(a.FieldCaption, CellValues.String));
}
else
{
row.Append(
ConstructCell(a.FieldCaption, CellValues.String));
}
}
if (defnExist == false)
{
row.Append(
ConstructCell("Excel Template Definition Missing", CellValues.String));
}
// Insert the header row to the Sheet Data
sheetData.AppendChild(row);
// Inserting each employee
worksheetPart.Worksheet.Save();
}
Here is construct cell method
private Cell ConstructCell(string value, CellValues dataType)
{
Cell c = new Cell()
{
CellValue = new CellValue(value),
DataType = new EnumValue<CellValues>(dataType)
//StyleIndex=0U
};
return c;
}
Is there any way to solve this?
1) Add style sheet to your WorkBookPart
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorkbookStylesPart stylePart = workbookPart.AddNewPart<WorkbookStylesPart>();
stylePart.Stylesheet = GenerateStylesheet();
stylePart.Stylesheet.Save();
Note: Add above WorkbookStylesPart code just below the WorkbookPart otherwise you can't get it to work.
2) Add below function that return stylesheet,
private Stylesheet GenerateStylesheet()
{
Stylesheet styleSheet = null;
Fonts fonts = new Fonts(
new Font( // Index 0 - default
new FontSize() { Val = 10 }
),
new Font( // Index 1 - header
new FontSize() { Val = 10 },
new Bold(),
new Color() { Rgb = "FFFFFF" }
));
Fills fills = new Fills(
new Fill(new PatternFill() { PatternType = PatternValues.None }), // Index 0 - default
new Fill(new PatternFill() { PatternType = PatternValues.Gray125 }), // Index 1 - default
new Fill(new PatternFill(new ForegroundColor { Rgb = new HexBinaryValue() { Value = "66666666" } })
{ PatternType = PatternValues.Solid }) // Index 2 - header
);
Borders borders = new Borders(
new Border(), // index 0 default
new Border( // index 1 black border
new LeftBorder(new Color() { Auto = true }) { Style = BorderStyleValues.Thin },
new RightBorder(new Color() { Auto = true }) { Style = BorderStyleValues.Thin },
new TopBorder(new Color() { Auto = true }) { Style = BorderStyleValues.Thin },
new BottomBorder(new Color() { Auto = true }) { Style = BorderStyleValues.Thin },
new DiagonalBorder())
);
CellFormats cellFormats = new CellFormats(
new CellFormat(), // default
new CellFormat { FontId = 0, FillId = 0, BorderId = 1, ApplyBorder = true }, // body
new CellFormat { FontId = 1, FillId = 2, BorderId = 1, ApplyFill = true } // header
);
styleSheet = new Stylesheet(fonts, fills, borders, cellFormats);
return styleSheet;
}
3) And your ConstructCell method,
private Cell ConstructCell(string value, CellValues dataType, uint styleIndex = 0)
{
return new Cell()
{
CellValue = new CellValue(value),
DataType = new EnumValue<CellValues>(dataType),
StyleIndex = styleIndex
};
}
4) And call your above method like
If you dont want to apply style then use below
ConstructCell(a.FieldCaption, CellValues.String));
And if you want to apply style on body cell then use below
ConstructCell(a.FieldCaption, CellValues.String, 1);
And if you want to apply style on header cell then use below
ConstructCell(a.FieldCaption, CellValues.String, 2);

OpenXML: Creating a workbook, then putting a validator in--vs. Opening a workbook, then putting a validator in

The following method creates a new workbook, and inserts two validator dropdowns:
public static void ForValidator() {
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Create("validator output.xlsx", SpreadsheetDocumentType.Workbook)) {
WorkbookPart workbookpart = myDoc.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
SheetData sheetData_ = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData_);
Sheets sheets_ = myDoc.WorkbookPart.Workbook.AppendChild(new Sheets());
sheets_.AppendChild(new Sheet() {
Id = myDoc.WorkbookPart.GetIdOfPart(myDoc.WorkbookPart.WorksheetParts.First()), SheetId = 1, Name = "Sheet1"
});
DataValidations dataValidations = new DataValidations();
DataValidation dataValidation = new DataValidation() {
Type = DataValidationValues.List, AllowBlank = true, SequenceOfReferences = new ListValue<StringValue>() { InnerText = "F2:F3" }
};
Formula1 formula = new Formula1();
formula.Text = "\"Selection 1,Selection 2,Selection 3\"";
dataValidation.Append(formula);
dataValidations.Append(dataValidation);
worksheetPart.Worksheet.AppendChild(dataValidations);
}
}
Can the same routine be modified to instead just open an existing "validator output.xlsx" file and do the same thing?
For over a week I've tried everything I know to try and have come up with nothing. Thank you for any help.

Setting Font.Bold and Column.Width for Cells in OPEN XML

I've been using OPEN XML to create a spread sheet which can then be downloaded. I want to set the column widths and also change the header to bold. I've done this using Office.Interop.Excel but I'm struggling with this format. My code is below, you can see I've set up a font but have been unable to assign it to my spread sheet, I have got messages saying it's not possible to assign to a tree.
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook); //Open(fileName, true);
try
{
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new DocumentFormat.OpenXml.Spreadsheet.Workbook();
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet(new SheetData());
DocumentFormat.OpenXml.Spreadsheet.Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>(new DocumentFormat.OpenXml.Spreadsheet.Sheets());
Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Models" };
sheets.Append(sheet);
DocumentFormat.OpenXml.Spreadsheet.Font boldFont = new DocumentFormat.OpenXml.Spreadsheet.Font();
Bold bFontBold = new Bold();
boldFont.Append(bFontBold);
DocumentFormat.OpenXml.Spreadsheet.Worksheet worksheet = new DocumentFormat.OpenXml.Spreadsheet.Worksheet();
SheetData sheetData = new SheetData();
........
Row row = new Row();
Cell cell = new Cell()
{
CellReference = "A" + (intI + 1),
DataType = CellValues.String,
CellValue = new CellValue(ModelBookrowDetail[0])
};
row.Append(cell);
Cell cell2 = new Cell()
{
CellReference = "B" + (intI + 1),
DataType = CellValues.String,
CellValue = new CellValue(ModelBookrowDetail[1])
};
row.Append(cell2);
.........
sheetData.Append(row);
}
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
spreadsheetDocument.Close();
}
catch
{
spreadsheetDocument.Close();
}
I don't know how set you are on using OpenXml. I might suggest looking into ClosedXml. Up until now it has saved me a lot of manhours. It only has one downside to it, and that is that it doesn't manage Word documents like OpenXml does.
ClosedXml is a more object oriented Open Source Library, and is far more well documented in contrast to what i have experienced with OpenXml.
You can find ClosedXml here:
https://github.com/ClosedXML/ClosedXML
Best of luck
Olliver

Adding multiple Cells to a single Row

I am new to this and when I try to add more than one cell to a row it says there is unreadable content. Here is what I have.
SpreadsheetDocument ssDoc = SpreadsheetDocument.Create(saveFile, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document
WorkbookPart workbookPart = ssDoc.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
// Add a WorksheetPart to theWorkbookPart
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
Sheets sheets = ssDoc.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
Sheet sheet = new Sheet()
{ Id = ssDoc.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1, Name = "Sheet1"
};
sheets.Append(sheet);
Worksheet worksheet = new Worksheet();
SheetData sheetData = new SheetData();
Row row = new Row();
Cell cell = new Cell()
{
CellReference = "A1",
DataType = CellValues.String,
CellValue = new CellValue("Cell1")
};
Cell cell2 = new Cell()
{
CellReference = "A2",
DataType = CellValues.String,
CellValue = new CellValue("Cell2")
};
row.Append(cell);
row.Append(cell2);
sheetData.Append(row);
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
// Close the document.
ssDoc.Close();
If I remove the second cell, it works as expected.
Instead of "A2" the cell reference should be "B1"
SpreadSheet.Cell cell = new SpreadSheet.Cell()
{
CellReference = "A1",
DataType = SpreadSheet.CellValues.String,
CellValue = new SpreadSheet.CellValue("Cell1")
};
SpreadSheet.Cell cell2 = new SpreadSheet.Cell()
{
CellReference = "B1",
DataType = SpreadSheet.CellValues.String,
CellValue = new SpreadSheet.CellValue("Cell2")
};
I had a similar issue. If anyone wants to insert cells in the same column, Row Index needs to be incremented and cell reference in order to insert cell in the same column. Took me entire weekend to figure out :D
Row row2 = new Row { RowIndex = 2};
SpreadSheet.Cell cell2 = new SpreadSheet.Cell()
{
CellReference = "B1",
DataType = SpreadSheet.CellValues.String,
CellValue = new SpreadSheet.CellValue("Cell2")
};
row2.Append(cell2);
sheetData.Append(row);
It's better to use loop.

Categories