I am working on generating an excel template from code. When I run the piece to create my WorkBook, I get no errors in code, however when I go to open the Excel document, I get an error indicating that the file is unreadable. I am able to click to open it anyway, and I get the following message
Removed Records: Worksheet properties from /xl/workbook.xml part (Workbook)
Any idea what might be wrong in my code please?
public void CreatePackage()
{
using (SpreadsheetDocument package = SpreadsheetDocument.Create(FilePath, SpreadsheetDocumentType.Workbook))
{
CreateParts(package);
}
}
private void CreateParts(SpreadsheetDocument document)
{
ExcelWorkBook excelworkbook = new ExcelWorkBook();
ExcelSheetHelper excelworksheet = new ExcelSheetHelper();
ExcelSharedStringsTable excelsharedtable = new ExcelSharedStringsTable();
ExcelWorkSheetPartBuilder excelworksheetbuilder = new ExcelWorkSheetPartBuilder();
ExtendedFilePropertiesPart extendedFilePropertiesPart1 = document.AddNewPart<ExtendedFilePropertiesPart>("rId3");
ExcelWorkSheetPartBuilder.GenerateExtendedFilePropertiesPart1Content(extendedFilePropertiesPart1);
WorkbookPart workbookPart1 = document.AddWorkbookPart();
excelworkbook.GenerateWorkbookPartContent(workbookPart1);
WorkbookStylesPart workbookStylesPart1 = workbookPart1.AddNewPart<WorkbookStylesPart>("rId5");
ExcelWorkBook.GenerateWorkbookStylesPart1Content(workbookStylesPart1);
SetPackageProperties(document);
}
public void GenerateWorkbookPartContent(WorkbookPart workbookPart1)
{
Workbook workbook = new Workbook();
workbook.AddNamespaceDeclaration("r", rNameSpace);
FileVersion fileVersion1 = GenerateFileVersion();
WorkbookProperties workbookProperties1 = GenerateWorkbookProperties();
BookViews bookViews1 = GenerateBookViews();
Sheets sheets1 = GenerateSheets();
DefinedNames definedNames1 = GenerateDefinedNames();
CalculationProperties calculationProperties1 = GenerateCalculationProperties();
CustomWorkbookViews customWorkbookViews1 = GenerateCustomWorkbookViews();
workbook.Append(fileVersion1);
workbook.Append(workbookProperties1);
workbook.Append(bookViews1);
workbook.Append(sheets1);
workbook.Append(definedNames1);
workbook.Append(calculationProperties1);
workbook.Append(customWorkbookViews1);
workbookPart1.Workbook = workbook;
}
// Creates an FileVersion instance and adds its children.
public FileVersion GenerateFileVersion()
{
FileVersion fileVersion1 = new FileVersion() { ApplicationName = "xl", LastEdited = "5", LowestEdited = "5", BuildVersion = "9303" };
return fileVersion1;
}
// Creates an WorkbookProperties instance and adds its children.
public WorkbookProperties GenerateWorkbookProperties()
{
WorkbookProperties workbookProperties1 = new WorkbookProperties() { HidePivotFieldList = true };
return workbookProperties1;
}
// Creates an BookViews instance and adds its children.
public BookViews GenerateBookViews()
{
BookViews bookViews1 = new BookViews();
WorkbookView workbookView1 = new WorkbookView() { XWindow = -75, YWindow = 270, WindowWidth = (UInt32Value)15435U, WindowHeight = (UInt32Value)6930U };
bookViews1.Append(workbookView1);
return bookViews1;
}
// Creates an Sheets instance and adds its children.
public Sheets GenerateSheets()
{
Sheets sheets1 = new Sheets();
Sheet sheet1 = new Sheet() { Name = String.Format("{0}", worksheetname), SheetId = (UInt32Value)8U, Id = "rId1" };
sheets1.Append(sheet1);
return sheets1;
}
// Creates an DefinedNames instance and adds its children.
public DefinedNames GenerateDefinedNames()
{
DefinedNames definedNames1 = new DefinedNames();
DefinedName definedName1 = new DefinedName() { Name = "_xlnm._FilterDatabase", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName1.Text = String.Format("\'{0}\'!$A$6:$EO$1269", worksheetname);
DefinedName definedName2 = new DefinedName() { Name = "Z_32BE30F1_B609_44A0_A38A_666CEFFB64E2_.wvu.Cols", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName2.Text = String.Format("\'{0}\'!#REF!", worksheetname);
DefinedName definedName3 = new DefinedName() { Name = "Z_32BE30F1_B609_44A0_A38A_666CEFFB64E2_.wvu.FilterData", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName3.Text = String.Format("\'{0}\'!#REF!", worksheetname);
DefinedName definedName4 = new DefinedName() { Name = "Z_5098B70B_692A_450A_8DAE_5172C296966E_.wvu.FilterData", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName4.Text = String.Format("\'{0}\'!#REF!", worksheetname);
DefinedName definedName5 = new DefinedName() { Name = "Z_7C00A233_927A_41FE_802C_48F5F9E9D5B6_.wvu.FilterData", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName5.Text = String.Format("\'{0}\'!#REF!",worksheetname);
DefinedName definedName6 = new DefinedName() { Name = "Z_AC112ED6_0017_40BF_884A_9B7959C37BF0_.wvu.FilterData", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName6.Text = String.Format("\'{0}\'!#REF!", worksheetname);
DefinedName definedName7 = new DefinedName() { Name = "Z_E444BF53_6DCE_4910_823C_F60AE88C96EE_.wvu.FilterData", LocalSheetId = (UInt32Value)0U, Hidden = true };
definedName7.Text = String.Format("\'{0}\'!#REF!",worksheetname);
definedNames1.Append(definedName1);
definedNames1.Append(definedName2);
definedNames1.Append(definedName3);
definedNames1.Append(definedName4);
definedNames1.Append(definedName5);
definedNames1.Append(definedName6);
definedNames1.Append(definedName7);
return definedNames1;
}
// Creates an CalculationProperties instance and adds its children.
public CalculationProperties GenerateCalculationProperties()
{
CalculationProperties calculationProperties1 = new CalculationProperties() { CalculationId = (UInt32Value)125725U };
return calculationProperties1;
}
// Creates an CustomWorkbookViews instance and adds its children.
public CustomWorkbookViews GenerateCustomWorkbookViews()
{
CustomWorkbookViews customWorkbookViews1 = new CustomWorkbookViews();
CustomWorkbookView customWorkbookView1 = new CustomWorkbookView() { Name = "A - Personal View", Guid = "{5098B70B-692A-450A-8DAE-5172C296966E}", MergeInterval = (UInt32Value)0U, PersonalView = true, Maximized = true, XWindow = 1, YWindow = 1, WindowWidth = (UInt32Value)1366U, WindowHeight = (UInt32Value)494U, ActiveSheetId = (UInt32Value)3U };
CustomWorkbookView customWorkbookView2 = new CustomWorkbookView() { Name = "B - Personal View", Guid = "{7C00A233-927A-41FE-802C-48F5F9E9D5B6}", MergeInterval = (UInt32Value)0U, PersonalView = true, Maximized = true, XWindow = 1, YWindow = 1, WindowWidth = (UInt32Value)1024U, WindowHeight = (UInt32Value)487U, ActiveSheetId = (UInt32Value)3U };
CustomWorkbookView customWorkbookView3 = new CustomWorkbookView() { Name = "C - Personal View", Guid = "{32BE30F1-B609-44A0-A38A-666CEFFB64E2}", MergeInterval = (UInt32Value)0U, PersonalView = true, Maximized = true, XWindow = 1, YWindow = 1, WindowWidth = (UInt32Value)1280U, WindowHeight = (UInt32Value)481U, ActiveSheetId = (UInt32Value)3U };
customWorkbookViews1.Append(customWorkbookView1);
customWorkbookViews1.Append(customWorkbookView2);
customWorkbookViews1.Append(customWorkbookView3);
return customWorkbookViews1;
}
public void ValidateDocument()
{
try
{
OpenXmlValidator validator = new OpenXmlValidator();
int count = 0;
IDictionary<String, String> ErrorLog = new Dictionary<String, String>();
using (StreamWriter f = new StreamWriter("Errolog.txt"))
{
foreach (ValidationErrorInfo error in validator.Validate(WordprocessingDocument.Open(FilePath, true)))
{
count++;
f.WriteLine("Error " + count);
f.WriteLine("Description: " + error.Description);
f.WriteLine("Path: " + error.Path.XPath);
f.WriteLine("Part: " + error.Part.Uri);
f.WriteLine("-------------------------------------------");
f.WriteLine("-------------------------------------------");
f.WriteLine("-------------------------------------------");
}
f.Flush();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
In my specific case, I was having this problem because of too long Worksheet names. This is what was happening to me:
On the first run of my program, I was trying to creating Worksheets with really big names.
Excel automatically cropped the big names to 31 characters. No exception was thrown. That is, I thought I was saving the original big name but I was actually saving the 31 characters long cropped string.
On the second run of my program, I was checking if this specific Worksheet existed already, but I thought it didn't exist because the existing one was cropped.
I was saving the Worksheet again. No exception thrown, but then, the new one was also cropped and now the XML contains 2 definitions of the same worksheet.
This causes Excel to attempt to repair the resulting Spreadsheet. Even though it works properly, I think it was simply throwing away the second one and using the first, which wasn't what I wanted.
I fixed the problem by cropping the 31 characters beforehand, before all the comparisons. Now it works perfectly
I know this question asked long ago, i thought my experience will solve someones problem. So am posting my answer here.
I had a similar problem. It occurs due the no. of characters in a sheet name exceeds the limit 31. Sheet name characters must be <= 31.
It won't throw any exception while creating, but gives error while opening in Microsoft Excel.
The Open XML SDK does not constrain you to producing a valid document. However, there is the OpenXmlValidator class which you can use to report any errors in the generated document. See this, which has a good example.
I had this error as well, because I was using 0 as my SheetId, something like:
var sheet1 = new Sheet() { Name = "Test", SheetId = (UInt32Value)0U, Id = "rId1" };
Excel starts counting from 1, not 0. This is not only true for row numbers, it applies to sheet numbers as well.
Yes, As Murugesan Said in the above comment, i fixed this error by renaming the file name of my excel document.
For example: In my case , the name of the document was 'My Sample Export Data Sheet'. I have renamed the file to 'MySampleExportDataSheet' and everything worked well later.
In my case the tab name consists single quote, i.e.: "my tab's".
I just omitted them:
title = title.replace(/'/g, "");
In our case, some of our worksheet names contained square brackets, e.g.:
Students [Primary]
Students [Secondary]
Replacing square brackets with round brackets fixed it:
Students (Primary)
Students (Secondary)
(For some reason, our customer didn't want these worksheets called Primary Students, Secondary Students, hence using brackets)
For us, the error message when opening the workbook in Excel was:
We found a problem with some content in 'workbook.xlsx'.
Do you want us to try and recover as much as we can?
If you trust the source of this workbook, click Yes.
Clicking Yes then allowed Excel to open the workbook, and it displayed the message
Repaired Records: Worksheet properties from /xl/workbook.xml part (Workbook)
Just in case someone finds this. It may also be because your sheet names are not unique. My issue was that, because of the char limit, 3 sheets wound up with the exact same name. When I repaired the file Excel renamed the duplicate sheets.
So if the above answers don't work, repair the file then look for a sheet named Recovered_Sheet1. That will tell you what sheet name has the issue (duplicate in my case).
Related
I have a code that its purpose is to rename a specific sheet, but when executing the BatchUpdate and the code is crached, does anyone have any ideas?
public void UpdateSheetName(string sheetName,string newSheetName)
{
//get sheet id by sheet name
Spreadsheet spr = service.Spreadsheets.Get(SpreadsheetId).Execute();
Sheet sh = spr.Sheets.Where(s => s.Properties.Title == sheetName).FirstOrDefault();
int sheetId = (int)sh.Properties.SheetId;
BatchUpdateSpreadsheetRequest bussr = new BatchUpdateSpreadsheetRequest();
var request = new Request()
{
UpdateSpreadsheetProperties= new UpdateSpreadsheetPropertiesRequest(){
Properties=new SpreadsheetProperties()
{
Title= newSheetName,
},
Fields ="title"
}
};
bussr.Requests = new List<Request>();
bussr.Requests.Add(request);
var bur = service.Spreadsheets.BatchUpdate(bussr, SpreadsheetId);
bur.Execute();
}
Error Message:
Invalid value at 'requests[0]' (oneof), oneof field 'kind' is already set. Cannot set 'updateSpreadsheetProperties' [400]
I found the problem, the problem was that I had used the wrong class, use UpdateSpreadsheetProperties instead of UpdateSheetPropertiesRequest
var request = new Request()
{
UpdateSheetProperties =new UpdateSheetPropertiesRequest {
Properties=new SheetProperties()
{
Title=newName,
SheetId=sheetId
},
Fields = "Title"
}
};
I am using below code to highlight text in power point presentation (.pptx) using openxml but below code for pptx - it corrupts the file and ask to repair while opening pptx and after opening it highlights the word but it does not preserves the formatting. So total 2 problems:
1. File gets corrupted
2. Formatting is not preserved
But it highlights the text which i want (but it does not preserve formatting)
I have debuged my code line by line and problem is at below line, but i am not able to figure out what is the problem.
highlightRun.InsertAt(runPro, 0);
I have used openxml productivity tool to compare two files of pptx one with highlighting one without highlighting: The difference i see is as below : i am not using two times RunProperties but it is showing 2 times :
Corrupted file :
public Run GenerateRun()
{
Run run1 = new Run();
RunProperties runProperties1 = new RunProperties(){ Language = "en-US", Dirty = false };
runProperties1.SetAttribute(new OpenXmlAttribute("", "smtClean", "", "0"));
SolidFill solidFill1 = new SolidFill();
RgbColorModelHex rgbColorModelHex1 = new RgbColorModelHex(){ Val = "FFF000" };
solidFill1.Append(rgbColorModelHex1);
runProperties1.Append(solidFill1);
RunProperties runProperties2 = new RunProperties(){ Language = "en-US", Dirty = false };
runProperties2.SetAttribute(new OpenXmlAttribute("", "smtClean", "", "0"));
SolidFill solidFill2 = new SolidFill();
RgbColorModelHex rgbColorModelHex2 = new RgbColorModelHex(){ Val = "FFFF00" };
solidFill2.Append(rgbColorModelHex2);
runProperties2.Append(solidFill2);
Text text1 = new Text();
text1.Text = "gaits";
run1.Append(runProperties1);
run1.Append(runProperties2);
run1.Append(text1);
return run1;
}
<a:r xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:rPr lang="en-US" dirty="0" smtClean="0">
<a:solidFill>
<a:srgbClr val="FFF000" />
</a:solidFill>
</a:rPr>
<a:rPr lang="en-US" dirty="0" smtClean="0">
<a:solidFill>
<a:srgbClr val="FFFF00" />
</a:solidFill>
</a:rPr>
<a:t>gaits</a:t>
</a:r>
Correct file :
public class GeneratedClass
{
// Creates an Run instance and adds its children.
public Run GenerateRun()
{
Run run1 = new Run();
RunProperties runProperties1 = new RunProperties(){ Language = "en-US", Dirty = false };
runProperties1.SetAttribute(new OpenXmlAttribute("", "smtClean", "", "0"));
SolidFill solidFill1 = new SolidFill();
RgbColorModelHex rgbColorModelHex1 = new RgbColorModelHex(){ Val = "FFFF00" };
solidFill1.Append(rgbColorModelHex1);
runProperties1.Append(solidFill1);
Text text1 = new Text();
text1.Text = "gaits";
run1.Append(runProperties1);
run1.Append(text1);
return run1;
}
<a:r xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
<a:rPr lang="en-US" dirty="0" smtClean="0">
<a:solidFill>
<a:srgbClr val="FFFF00" />
</a:solidFill>
</a:rPr>
<a:t>gaits</a:t>
</a:r>
What i am missing ? My complete code is as below :
using OpenXmlDrawing = DocumentFormat.OpenXml.Drawing;
private void HighLightTextPresentation(OpenXmlDrawing.Paragraph paragraph, string text)
{
var found = paragraph
.Descendants<OpenXmlDrawing.Run>()
.Where(r => !string.IsNullOrEmpty(r.InnerText) && r.InnerText != "\\s")
.Select(r =>
{
var runText = r.GetFirstChild<OpenXmlDrawing.Text>();
int index = runText.Text.IndexOf(text, StringComparison.OrdinalIgnoreCase);
// 'Run' is a reference to the text run we found,
// TextNode is a reference to the run's Text object,
// 'TokenIndex` is the index of the search string in run's text
return new { Run = r, TextNode = runText, TokenIndex = index };
})
.FirstOrDefault(o => o.TokenIndex >= 0);
// Nothing found -- escape
if (found == null)
{
return;
}
// Create a node for highlighted text as a clone (to preserve formatting etc)
var highlightRun = found.Run.CloneNode(true);
// Add the highlight node after the found text run and set up the highlighting
paragraph.InsertAfter(highlightRun, found.Run);
highlightRun.GetFirstChild<OpenXmlDrawing.Text>().Text = text;
DocumentFormat.OpenXml.Drawing.RunProperties runPro = new DocumentFormat.OpenXml.Drawing.RunProperties() { Language = "en-US", Dirty = false };
runPro.SetAttribute(new OpenXmlAttribute("", "smtClean", "", "0"));
//Apply color to searched text
DocumentFormat.OpenXml.Drawing.SolidFill solidFill1 = new DocumentFormat.OpenXml.Drawing.SolidFill();
DocumentFormat.OpenXml.Drawing.RgbColorModelHex rgbColorModelHex1 = new DocumentFormat.OpenXml.Drawing.RgbColorModelHex() { Val = "FFF000" };//Set Font-Color to Green (Hex "00B050").
solidFill1.Append(rgbColorModelHex1);
runPro.Append(solidFill1);
highlightRun.InsertAt(runPro, 0);
// Check if there's some text in the text run *after* the found text
int remainderLength = found.TextNode.Text.Length - found.TokenIndex - text.Length;
if (remainderLength > 0)
{
// There is some text after the highlighted section --
// insert it in a separate text run after the highlighted text run
var remainderRun = found.Run.CloneNode(true);
paragraph.InsertAfter(remainderRun, highlightRun);
OpenXmlDrawing.Text textNode = remainderRun.GetFirstChild<OpenXmlDrawing.Text>();
textNode.Text = found.TextNode.Text.Substring(found.TokenIndex + text.Length);
// We need to set up this to preserve the spaces between text runs
//textNode.Space = new EnumValue<SpaceProcessingModeValues>(SpaceProcessingModeValues.Preserve);
}
// Check if there's some text *before* the found text
if (found.TokenIndex > 0)
{
// Something is left before the highlighted text,
// so make the original text run contain only that portion
found.TextNode.Text = found.TextNode.Text.Remove(found.TokenIndex);
// We need to set up this to preserve the spaces between text runs
//found.TextNode.Space = new EnumValue<SpaceProcessingModeValues>(SpaceProcessingModeValues.Preserve);
}
else
{
// There's nothing before the highlighted text -- remove the unneeded text run
paragraph.RemoveChild(found.Run);
}
}
As noted in the comments, the reason of "getting corrupted" is that you make the XML structure invalid by creating an additional a:rPr element (RunProperties) in the a:r element (Run) while only one is allowed.
So you should first check whether there already is a RunProperties element in the Run before inserting a new one. If a RunProperties element already exists, you should instead reuse it.
// Either reuse an existing RunProperties element,
// or create a new one if there's none
RunProperties runPro = highlightRun.Descendants<RunProperties>().FirstOrDefault() ??
new RunProperties { Language = "en-US", Dirty = false };
// only add the element if it's really new, don't add existing one
if (runPro.Parent == null)
{
highlightRun.InsertAt(runPro, 0);
}
Someone suggested to me a method to improve my code by making it more manageable through the use of objects:
string[,] values = new string[15, 35]; //or objects
values[7, 7] = "2016";
values[7, 28] = drag24;
values[7, 33] = drag25;
values[10, 8] = digit1;
values[10, 11] = digit2;
// etc.
Range range = WS.Range[WS.Cells[1, 1], WS.Cells[15, 35]];
range.Value = values;
His suggestion, but since I moved from interop to EPPLUS, the following syntax no longer works.
Range range = WS.Range[WS.Cells[1, 1], WS.Cells[15, 35]];
What would a working form of this look like in EPPLUS Syntax?
Replacing it with this didnt work out right and replaced everything in the sheet:
ExcelRange range = WS.Cells["A1: AH106"]
Perhaps something like this?
= sheet.Cells["A1:,12:12,14:14"]
Not sure if this is exactly what you mean, but here's a simple example with a custom object, and arrays:
Test object:
class TestObject
{
public int Id { get; set; }
public string Name { get; set; }
}
Populating a sheet with the custom object, and arrays:
IEnumerable<TestObject> objectList = new List<TestObject>()
{
{ new TestObject() {Id = 0, Name = "zero" } },
{ new TestObject() {Id = 1, Name = "one" } }
};
var values = new List<object[]>()
{
new string[] { "one", "two" },
new string[] { "three", "four" }
};
using (var package = new ExcelPackage())
{
var sheet = package.Workbook.Worksheets.Add("Sheet1");
// note second parameter gives you headings
sheet.Cells["A1"].LoadFromCollection<TestObject>(objectList, true);
sheet.Cells["A4"].LoadFromArrays(values);
File.WriteAllBytes(OUTPUT, package.GetAsByteArray());
}
I am able to run the following code without error or exception (on the code side)
private ExcelRow CreateContentRow(int index, StockHolder stockholder)
{
ExcelRow r = new ExcelRow();
r.RowIndex = (UInt32)index + 1;
int i = 0;
string[] headerColumns = new string[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K" };
var eraseThis = stockholder.GetType().GetProperties().Count();
foreach (var prop in stockholder.GetType().GetProperties())
{
ExcelCell c = new ExcelCell();
c.CellReference = headerColumns[i].ToString() + index;
if (prop.PropertyType.ToString().Equals("System.string", StringComparison.InvariantCultureIgnoreCase))
{
var result = prop.GetValue(stockholder, null);
if (result == null)
{
result = "";
}
c.DataType = CellValues.String;
InlineString inlineString = new InlineString();
Text t = new Text();
t.Text = result.ToString();
inlineString.AppendChild(t);
c.AppendChild(inlineString);
}
//Other "if" statements here for checking bool and numeric (similar to the above "if" statement, but none of them are used at this time
return r;
}
the "index" parameter is the number of rows in the sheet I am attempting to add to (it seems able to read the sheet name (as there is only one anyway)), and the "Stockholder" parameter is the custom object with filled data (all are not null)
The issue is when i attempt to open the Excel document afterwards like this
and when I click yes, it shows this popup
results when I tried searching are more useful for another 3rd party library (starts with "ERPPS" or something like that). When debugging, the properties seem to map correctly (using another method for mapping, and they're all strings anyway). Any help or links would be appreciated as I've been working through this for a while now. and no, using a web app or a form is not an alternative at this time (shame as it would be over a long time ago). Thanks
EDIT -
Also, here is the calling method (since that may be part of it as well)
private void InsertIntoExistingExcel(StockHolder sh)
{
using (SpreadsheetDocument myWorkbook = SpreadsheetDocument.Open(locationOfExcelFile, true))
{
WorkbookPart workbookPart = myWorkbook.WorkbookPart;
var stockHolderInfo_Sheet = myWorkbook.WorkbookPart.Workbook.Sheets.GetFirstChild<Sheet>();
IEnumerable<Sheet> Sheets = new List<Sheet>() { stockHolderInfo_Sheet };
if (Sheets.Count() > 0)
{
string relationshipId = Sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)myWorkbook.WorkbookPart.GetPartById(relationshipId);
SheetData sheetdata = worksheetPart.Worksheet.GetFirstChild<SheetData>();
var sheetDataCount = sheetdata.Count();
var newContentRow = CreateContentRow(sheetDataCount, sh);
sheetdata.AppendChild(newContentRow);
workbookPart.Workbook.Save();
}
}
}
solved it! or rather, used a different method
private void interopExcelInsertion(StockHolder sh, int rowNumberToUpdate)
{
ExcelInterop.Application oApp;
ExcelInterop.Worksheet oSheet;
ExcelInterop.Workbook oBook;
oApp = new ExcelInterop.Application();
oBook = oApp.Workbooks.Open(locationAndNameOfExcelFile);
oSheet = oBook.Worksheets[1] as Microsoft.Office.Interop.Excel.Worksheet;
var rowToUpdate = rowNumberToUpdate > 0 ? rowNumberToUpdate : oSheet.Cells.SpecialCells(ExcelInterop.XlCellType.xlCellTypeLastCell, System.Type.Missing).Row;
int i = 1;
foreach (var prop in sh.GetType().GetProperties())
{
var result = prop.GetValue(sh, null);
oSheet.Cells[rowToUpdate + 1, i] = result == null ? "" : result.ToString();
i++;
}
oBook.Save();
oBook.Close();
oApp.Quit();
//Marshal.ReleaseComObject(oApp);
}
I have a IEnumerable<object> dataSource which contains a collection anonymous types. The actual structure of the anonymous type won't be known at design time, so I'm trying to find a generic solution that can handle any anonymous type.
How can I load them into epplus to create a spreadsheet? I have a worksheet called ws and I tried:
ws.Cells["A1"].LoadFromCollection(dataSource, true);
However when that runs it outputs all of the anonymous type's properties into a single cell:
{ Id = 10000, Title = This is a test }
I've tried passing in MemberInfo using:
var members = dataSource.First().GetType().GetMembers();
ws.Cells["A1"].LoadFromCollection(this._dataSource, true,
TableStyles.Medium1, BindingFlags.Public, members);
But that throws an exception:
Supplied properties in parameter Properties must be of the same type as T
Any suggestions on how I can create a spreadsheet using anonymous types in c#?
I have tested
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromCollection(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
with this sample data:
var dataSource = Enumerable.Range(1, 100).Select(i => new{ ID=i, Title="Title " + i });
It works fine. It creates two columns with the correct headers and 100 rows.
But you should use anonymous types only if you know the structure at compile time.
You could use a DataTable and LoadFromDataTable instead. Since i don't know how you create the anonymous type i show you just a small sample:
DataTable dataSource = new DataTable();
dataSource.Columns.Add("Id"); // default type is string
dataSource.Columns.Add("Title");
// add other columns
dataSource.Rows.Add("1", "Title1");
// add other rows
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromDataTable(dataSource, true);
excel.SaveAs(new FileInfo(#"C:\Temp\Test.xlsx"));
}
You could group the anonymous types to make it easier for exporting with dataTables. The bug "Supplied properties in parameter Properties must be of the same type as T" is still there and a workaround is using DataTables.
// Imagine list is your main datasource
IEnumerable<object> list = Enumerable.Empty<object>(); // Data Source of <object>
// Added anon types at runtime added to the object list
var anonTypesOne = new object[]
{
new { GuidID = Guid.NewGuid(), StringProperty = "the string property" },
new { IntegerID = 1, IntegerProperty = 99 }
};
var anonTypesTwo = new object[]
{
new { StringID = "1", BooleanProperty = true, NumberProperty = 3, StringProperty = "Four" },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 },
new { GuidID = Guid.NewGuid(), NumberThree = 3 }
};
list = list.Concat(anonTypesOne).Concat(anonTypesTwo);
// Grouping works on anon types so we can group the export into their own tables
var groupings = list.GroupBy(i => i.GetType());
using(var package = new ExcelPackage(new FileInfo("C:\\Temp\\Anon.xlsx")))
{
var ws = package.Workbook.Worksheets.Add("Anonymous Types");
// add each "anon type matched grouping"
foreach(var grouping in groupings)
{
var isNew = ws.Dimension == null; // the sheet is empty if Dimension is null.
var row = 0;
if(isNew)
{
row = 1; // start from the first row
}
else
{
// otherwise there are tables already, start from the bottom
row = ws.Dimension.End.Row;
}
// because of EPP inheritance bug of T, we can just use dataTable
DataTable dt = new DataTable(grouping.Key.Name);
var properties = grouping.Key.GetProperties(); // Get anon type Properties
foreach(var property in properties)
{
dt.Columns.Add(property.Name);
}
foreach(var item in grouping.ToList())
{
var dataRow = dt.NewRow();
foreach(var p in properties) // populate a single row
{
dataRow[p.Name] = p.GetValue(item); // item is anon object instance
}
dt.Rows.Add(dataRow);
}
if(isNew) // load into the top most left cell of the worksheet
ws.Cells[1, 1].LoadFromDataTable(dt, PrintHeaders: true);
else // load from the dimension of current items + 1 row for spacing
ws.Cells[ws.Dimension.End.Row + 1, 1].LoadFromDataTable(dt, PrintHeaders: true);
ws.InsertRow(ws.Dimension.End.Row + 2, 5); // Insert some padding between each group
}
package.Save();
}
I was, this thread is older, but I'm looking for the same problem.
With the following code (VB) I have success.
Carsten
Dim targetFile = New IO.FileInfo(sFN)
Dim dataSource = Enumerable.Range(0, 1).Select(Function(i) New With {.ID = 1000, .Titel = "This is a test "}).ToList
Using epp = New OfficeOpenXml.ExcelPackage(targetFile)
Dim ws = epp.Workbook.Worksheets.Add("lst_Anonymous")
ws.Cells(1, 1).LoadFromCollection(dataSource, True,
OfficeOpenXml.Table.TableStyles.Medium1,
Reflection.BindingFlags.Public,
dataSource.GetType.GetGenericArguments()(0).GetProperties)
epp.Save()
End Using