Without going into too much detail, this is my first project. I am very new at coding and this may be a very simple oversight on my part. I am an engineer and I need to add some "custom properties" to over 1000+ CAD files. I am using excel to assign set properties to each file.
I am using EPPlus.
I have a using OfficeOpenXml; statement.
During debugging, I get an error at line 73: worksheet.Cells[row, 6].Value = file;
System.NullReferenceException: 'Object reference not set to an instance of an object.worksheet was null."
I can see that the value for "var worksheet" at line 64 is null but I can't figure out why...
public static void InsertPropName2()
{
string ExFile = #"J:\VB Test\Excel\SW.Prop.xlsx";
var FileInfo = new FileInfo(ExFile);
List<string> DrwPropName = new List<string>()
{
"MPN",
"Description",
"OEM Location",
"Material",
"Surface Finish",
"Hardness",
"Color",
};
List<string> PartPropName = new List<string>()
{
"MPN",
"Description",
"Drawn",
"Q.A. Approved",
"Checked",
"Engineering Approved",
"Revision",
"Drawn By:",
"Drawn Date",
"Q.A. Approval",
"Q.A. App. Date",
"Checked By",
"Checked Date",
"Engineering Approval",
"Engineering App. Date",
};
List<int> PartPropType = new List<int>()
{
30,
30,
30,
30,
30,
30,
30,
30,
64,
30,
64,
30,
64,
30,
64,
};
using (ExcelPackage excel = new ExcelPackage(FileInfo))
{
string directory = #"J:\Move (Test)\a";
string[] Files = Directory.GetFiles(directory);
foreach (string file in Files)
{
string WSName = file.Substring(file.Length - 31);
//line 64
var worksheet = excel.Workbook.Worksheets[WSName];
string FileType = file.Substring(file.Length - 6);
switch (FileType)
{
case "SLDDRW":
int row = 1;
int a = 0;
//line 73
worksheet.Cells[row, 6].Value = file;
foreach (string prop in DrwPropName)
{
worksheet.Cells[row, 7].Value = DrwPropName[a];
worksheet.Cells[row, 8].Value = 30;
a++;
}
break;
case "SLDPRT":
int row2 = 1;
int b = 0;
worksheet.Cells[row2, 6].Value = file;
foreach (string prop in PartPropName)
{
worksheet.Cells[row2, 7].Value = PartPropName[b];
worksheet.Cells[row2, 8].Value = PartPropType[b];
b++;
row2++;
}
break;
case "SLDASM":
int row3 = 1;
int c = 0;
worksheet.Cells[row3, 6].Value = file;
foreach (string prop in PartPropName)
{
worksheet.Cells[row3, 7].Value = PartPropName[c];
worksheet.Cells[row3, 8].Value = PartPropType[c];
c++;
row3++;
}
break;
default:
break;
}
}
excel.Save();
}
}
Any help is greatly appreciated. Again sorry if this is a very simple noob error!
edit:
I have another class that I run before I run this that creates an excel file with the worksheets in question above so I know the worksheet exists. I have also visual checked to see if that the excel file has the worksheets in question. the code in the other class executes and I have no errors with that part of my project.
The WSName is not declared anywhere else in the class so It is not the same as other NullReferenceException threads.
Part of the code that's in the other class:
using (ExcelPackage excel = new ExcelPackage(FileInfo))
{
string WSName = file.Substring(file.Length - 31);
var worksheet = excel.Workbook.Worksheets.Add(WSName);
Thanks for trying to solve the issue. I figured out what the problem was.
WSName was set from code Directory.GetFiles(directory); so I can keep track of the properties to each CAD file.
When the first class member used the value of WSName to create the worksheet. The value for WSName had "\". The actual name of the worksheet omitted the "\" even though I used the "#" symbol in front of the string. It must me a limitation of excel. When the second class member tried to write to the worksheet even though WSName was coded the same way as the first member it could not find it due to the missing "\".
Related
We are developing web api from where excel will be downloaded with data. While searching on the net we found libraries like npoi, epplus, closedxml.
Do we really need to use these libraries to work with excel or go with the standard approach?
We are using asp.net core for web api development.
Edit: Basically our front end is in angular 5 from where we
are exposing the web api. In web api we have written logic to get data and after getting data we need to place in certain format/template provided(Cell, Column wise, sheet wise etc.). There are quite a number of rows which we need to export in excel.
Also our database and apis are azure based.
Any help on this appreciated !
I have used epplus, and i think it works well for this scenario. Let me give you an example.
Exporting Data
private ExcelPackage CreateDoc(string title, string subject, string keyword)
{
var p = new ExcelPackage();
p.Workbook.Properties.Title = title;
p.Workbook.Properties.Author = "Application Name";
p.Workbook.Properties.Subject = subject;
p.Workbook.Properties.Keywords = keyword;
return p;
}
public ExcelPackage getApplicantsStatistics()
{
ExcelPackage p = CreateDoc("Applicant Statistics", "Applicant statistics", "All Applicants");
var worksheet = p.Workbook.Worksheets.Add("Applicant Statistics");
//Add Report Header
worksheet.Cells[1, 1].Value = "Applicant Statistics";
worksheet.Cells[1, 1, 1, 3].Merge = true;
//Get the data you want to send to the excel file
var appProg = _unitOfWork.ApplicantsProgram
.AllIncluding(pr => pr.Program1)
.GroupBy(ap => ap.Program1.Name)
.Select(ap => new { programName = ap.Key, TotalNum = ap.Count() })
.ToList();
//First add the headers
worksheet.Cells[2, 1].Value = "SR No";
worksheet.Cells[2, 2].Value = "Program";
worksheet.Cells[2, 3].Value = "No. of Applicants";
//Add values
var numberformat = "#,##0";
var dataCellStyleName = "TableNumber";
var numStyle = p.Workbook.Styles.CreateNamedStyle(dataCellStyleName);
numStyle.Style.Numberformat.Format = numberformat;
for (int i = 0; i < appProg.Count; i++)
{
worksheet.Cells[i + 3, 1].Value = i + 1;
worksheet.Cells[i + 3, 2].Value = appProg[i].programName;
worksheet.Cells[i + 3, 3].Value = appProg[i].TotalNum;
}
// Add to table / Add summary row
var rowEnd = appProg.Count + 2;
var tbl = worksheet.Tables.Add(new ExcelAddressBase(fromRow: 2, fromCol: 1, toRow: rowEnd, toColumn: 3), "Applicants");
tbl.ShowHeader = true;
tbl.TableStyle = TableStyles.Dark9;
tbl.ShowTotal = true;
tbl.Columns[2].DataCellStyleName = dataCellStyleName;
tbl.Columns[2].TotalsRowFunction = RowFunctions.Sum;
worksheet.Cells[rowEnd, 3].Style.Numberformat.Format = numberformat;
// AutoFitColumns
worksheet.Cells[2, 1, rowEnd, 3].AutoFitColumns();
return p;
}
The returned ExcelPackage object can be sent as a download to the File with MVC
byte[] reportBytes;
using (var package = _excelRep.getApplicantsStatistics())
{
reportBytes = package.GetAsByteArray();
}
return File(reportBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
There are several good libraries for doing so, my favorite ones are
EPPlus and OpenXML by Microsoft
https://github.com/JanKallman/EPPlus
https://learn.microsoft.com/en-us/office/open-xml/open-xml-sdk
There is not much difference what your db and frontend are as everything is organized by the backend.
I know there are many topics on the issue, but my requirements are more specific.. I'm using EF to select records into my project, and then export them to Excel. I've used This snippet code.
Let me explain what I'm trying to do. Given the following table(It's for simplification, as you will see in the code the table is a bit larger):
Name | Content
A "Content1"
A "Content2"
A "Content3"
B "other content"
......
When I export to excel, I don't want A to appear 3 times next to each content, I'd like to have only one "A" (which I was able to do) and merge the 3 cells (align them to the center too if possible) into one(without touching the Content column) .
This is my code:
public IActionResult Index()
{
var knownCampaigns = _repository.getDataForExport();
//return View(result);
string sWebRootFolder = _hostingEnvironment.WebRootPath;
string sFileName = #"demo.xlsx";
string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
FileInfo file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
if (file.Exists)
{
file.Delete();
file = new FileInfo(Path.Combine(sWebRootFolder, sFileName));
}
using (ExcelPackage package = new ExcelPackage(file))
{
// add a new worksheet to the empty workbook
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("CampaignMatches");
//First add the headers
worksheet.Cells[1, 2].Value = "Customer Name";
worksheet.Cells[1, 3].Value = "Guid";
worksheet.Cells[1, 4].Value = "Campaign Title";
worksheet.Cells[1, 5].Value = "Referrer Title";
worksheet.Cells[1, 6].Value = "Activity Date";
worksheet.Cells[1, 7].Value = "Is clicked?";
int counter = 2;
string oldGuid = "";
foreach (var campaign in knownCampaigns)
{
if (oldGuid == campaign.Guid || worksheet.Cells["C" + (counter - 1)].Value.ToString() == campaign.Guid)
{
oldGuid = campaign.Guid;
worksheet.Cells["A" + counter].Value = "";
worksheet.Cells["B" + counter].Value = "";
}
else
{
oldGuid = "";
worksheet.Cells["A" + counter].Value = campaign.customerName;
worksheet.Cells["B" + counter].Value = campaign.Guid;
}
worksheet.Cells["C" + counter].Value = campaign.campaignTitle;
worksheet.Cells["D" + counter].Value = campaign.reffererTitle;
worksheet.Cells["E" + counter].Value = campaign.activityDate;
worksheet.Cells["F" + counter].Value = campaign.is_clicked;
counter++;
}
package.Save(); //Save the workbook.
}
var result = PhysicalFile(Path.Combine(sWebRootFolder, sFileName), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
Response.Headers["Content-Disposition"] = new ContentDispositionHeaderValue("attachment")
{
FileName = file.Name
}.ToString();
return result;
}
Right now, my Customer Name and Guid column only appears once as intended, but I don't know how to merge the cells together into one cell.
Image of current output:
[![enter image description here][2]][2]
Image of wanted output:
[![enter image description here][3]][3]
It looks like it's not that obvious. Looks like there is an Elements property on worksheet that you can add type of MergedCell. https://learn.microsoft.com/en-us/office/open-xml/how-to-merge-two-adjacent-cells-in-a-spreadsheet
If someone will face the same issue, I've managed to solve this using Merge attribute , I had to extract the positions of the start column index, row index and end row index & end column index.
var Wsc_Guid = worksheet.Cells[startRowIndex, guidIndex, startRowIndex + numOfSameRecords, guidIndex];//Select the correct cells for Guids
Wsc_Guid.Merge = true;
I want to create a C# Console app (or a service - not sure how to develop a service yet) that:
1) Knows when a new email is received in Inbox>LOTALogs folder. This email is sent by a mobile application and includes an attachment and some issues that the customer experienced.
2) Takes the new email content, which is comma-separated, parses and appends the content into an Excel worksheet that already has the columns set up.
I managed to create:
1) The parser:
public static string[] emailContentsArray()
{
string content = "Username = Customer1,User ID = 362592,Unit ID = 805618,Date = Mar 12, 2017,Device = Android LGE LG-H990,OS version = 7.0 (API 24),App version = 1.0.0.56,Description = some description,Message = some message";
string[] contentArray = content.Split(',');
// Case where date format includes another comma
if (contentArray.Length > 10)
{
// Parsing headers
contentArray[0] = contentArray[0].Substring(11);
contentArray[1] = contentArray[1].Substring(10);
contentArray[2] = contentArray[2].Substring(10);
contentArray[3] = contentArray[3].Substring(7) + ", " + contentArray[4].Substring(1);
contentArray[4] = contentArray[5].Substring(9);
contentArray[5] = contentArray[6].Substring(13);
contentArray[6] = contentArray[7].Substring(14);
contentArray[7] = contentArray[8].Substring(14);
contentArray[8] = contentArray[9].Substring(10);
contentArray[9] = null;
for (int i = 0; i < contentArray.Length; i++)
{
Console.Write(contentArray[i] + ",");
}
}
//else
//{
//}
return contentArray;
}
2) Accessed the folder and counted the number of items:
public static string[] emailContent()
{
string[] content = null;
Microsoft.Office.Interop.Outlook.Application app = null;
Microsoft.Office.Interop.Outlook.NameSpace ns = null;
Microsoft.Office.Interop.Outlook.MAPIFolder inboxFolder = null;
Microsoft.Office.Interop.Outlook.MAPIFolder logFolder = null;
app = new Microsoft.Office.Interop.Outlook.Application();
ns = app.GetNamespace("MAPI");
inboxFolder = ns.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
logFolder = app.ActiveExplorer().CurrentFolder = inboxFolder.Folders["LOTALogs"];
int itemCount = logFolder.Items.Count;
Console.WriteLine("\n\nFolder Name: {0}, Num Items: {1}\n", logFolder.Name, itemCount);
return content;
}
3) Opened and printed the contents of the spreadsheet:
Excel.Application xlApp = new Excel.Application();
string path = "C:\\SomeUser\\BugReports";
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(#path);
Excel.Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Excel.Range xlRange = xlWorksheet.UsedRange;
for (int i = 1; i <= xlRange.Row + xlRange.Rows.Count - 1; i++)
{
for (int j = 1; j <= xlRange.Column + xlRange.Columns.Count - 1; j++)
{
if (j == 1)
Console.Write("\r\n");
if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
Console.Write(xlRange.Cells[i, j].Value2.ToString() + "\t");
}
}
xlWorkbook.Save();
xlWorkbook.Close();
xlApp.Quit();
Console.ReadLine();
I am a little lost now :)
I still need to:
1) Create an event listener (I think that's what it's called) so I can tell the email body parser to go fetch the email contents.
2) Extract the email body from the email.
Got this using
Console.WriteLine(logFolder.Items[1].Body);
3) Take the email content and append it to the spreadsheet.
4) Should I create this as a Windows Service?
PS - I am not a developer, just fiddling around with code and trying to be as efficient as possible. I don't want to fill this spreadsheet out manually when there's a technological solution in sight. Please comment if you have any suggestions on being more efficient with the code and model it differently.
Looks solid to me. I'd refrain from the service, but it greatly depends on your users. Unless your customer really wants to be "blind" from the whole process, it adds a lot of unwarranted complications.
As for appending to the spreadsheet...
int lastRow = xlWorksheet.UsedRange.Rows;
Excel.Range xlRange = xlWorksheet.Cells[lastRow + 1, 1];
xlRange.Value = stuffFromInbox;
If you're only adding the one item to the spreadsheet, this will work fine. For mass read/write operations with the spreadsheet (like your "Opened and printed the contents of the spreadsheet"), it would be much more efficient to read the Value or Value2 of the entire Range into a object[,]. Then iterate through the local array.
I am currently using EPPlus project in order to manipulate some .xlsx files. The basic idea is that I have to create a new file from a given template.
But when I create the new file from a template, all calculated columns in the tables are messed up.
The code I am using is the following:
static void Main(string[] args)
{
const string templatePath = "template_worksheet.xlsx"; // the path of the template
const string resultPath = "result.xlsx"; // the path of our result
using (var pck = new ExcelPackage(new FileInfo(resultPath), new FileInfo(templatePath))) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
For example for a .xlsx file (that have a table with 3 columns, the last one is just the sum of the others) the program creates a .xlsx file where the last column have the same value (which is correct only for the first row) in all rows.
The following images shows the result:
Now the questions are:
What is going on here ? Is my code wrong ?
How can I accomplish this task without that unexpected behavior ?
That definitely on to something there. I was able to reproduce it myself. It has to do with the Table you created. if you open your file and remove it using the "Convert To Range" option in the Table Tools tab the problem goes away.
I looked at the source code and it extracts the xml files at the zip level and didnt see any indication that it was actually messing with them - seemed to be a straight copy.
Very strange because if we create and save the xlsx file including a table from EPPlus the problem is not there. This works just fine:
[TestMethod]
public void Template_Copy_Test()
{
//http://stackoverflow.com/questions/28722945/epplus-with-a-template-is-not-working-as-expected
const string templatePath = "c:\\temp\\testtemplate.xlsx"; // the path of the template
const string resultPath = "c:\\temp\\result.xlsx"; // the path of our result
//Throw in some data
var dtdata = new DataTable("tblData");
dtdata.Columns.Add(new DataColumn("Col1", typeof(string)));
dtdata.Columns.Add(new DataColumn("Col2", typeof(int)));
dtdata.Columns.Add(new DataColumn("Col3", typeof(int)));
for (var i = 0; i < 20; i++)
{
var row = dtdata.NewRow();
row["Col1"] = "String Data " + i;
row["Col2"] = i * 10;
row["Col3"] = i * 100;
dtdata.Rows.Add(row);
}
var templateFile = new FileInfo(templatePath);
if (templateFile.Exists)
templateFile.Delete();
using (var pck = new ExcelPackage(templateFile))
{
var ws = pck.Workbook.Worksheets.Add("Data");
ws.Cells["A1"].LoadFromDataTable(dtdata, true);
for (var i = 2; i <= dtdata.Rows.Count + 1; i++)
ws.Cells[i, 4].Formula = String.Format("{0}*{1}", ExcelCellBase.GetAddress(i, 2), ExcelCellBase.GetAddress(i, 3));
ws.Tables.Add(ws.Cells[1, 1, dtdata.Rows.Count + 1, 4], "TestTable");
pck.Save();
}
using (var pck = new ExcelPackage(new FileInfo(resultPath), templateFile)) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
BUT.....
If we open testtemplate.xlsx, remove the table, save/close the file, reopen, and reinsert the exact same table the problem shows up when you run this:
[TestMethod]
public void Template_Copy_Test2()
{
//http://stackoverflow.com/questions/28722945/epplus-with-a-template-is-not-working-as-expected
const string templatePath = "c:\\temp\\testtemplate.xlsx"; // the path of the template
const string resultPath = "c:\\temp\\result.xlsx"; // the path of our result
var templateFile = new FileInfo(templatePath);
using (var pck = new ExcelPackage(new FileInfo(resultPath), templateFile)) // creating a package with the given template, and our result as the new stream
{
// note that I am not doing any work ...
pck.Save(); // savin our work
}
}
It has to be something burried in their zip copy methods but I nothing jumped out at me.
But at least you can see about working around it.
Ernie
Try to use the following code. This code takes the formatting and other rules and add them as xml node to another file. Ernie described it really well here Importing excel file with all the conditional formatting rules to epplus The best part of the solution is that you can also import formatting along with your other rules. It should take you close to what you need.
//File with your rules, can be your template
var existingFile = new FileInfo(#"c:\temp\temp.xlsx");
//Other file where you want the rules
var existingFile2 = new FileInfo(#"c:\temp\temp2.xlsx");
using (var package = new ExcelPackage(existingFile))
using (var package2 = new ExcelPackage(existingFile2))
{
//Make sure there are document element for the source
var worksheet = package.Workbook.Worksheets.First();
var xdoc = worksheet.WorksheetXml;
if (xdoc.DocumentElement == null)
return;
//Make sure there are document element for the destination
var worksheet2 = package2.Workbook.Worksheets.First();
var xdoc2 = worksheet2.WorksheetXml;
if (xdoc2.DocumentElement == null)
return;
//get the extension list node 'extLst' from the ws with the formatting
var extensionlistnode = xdoc
.DocumentElement
.GetElementsByTagName("extLst")[0];
//Create the import node and append it to the end of the xml document
var newnode = xdoc2.ImportNode(extensionlistnode, true);
xdoc2.LastChild.AppendChild(newnode);
package2.Save();
}
}
Try this
var package = new ExcelPackage(excelFile)
var excelSheet = package.Workbook.Worksheets[1];
for (var i = 1; i < 5; i++){
excelWorkSheet.InsertRow(i, 1, 1); // Use value of i or whatever is suitable for you
}
package.Workbook.Calculate();
Inserting new row copies previous row format and its formula if last prm is set to 1
I have a program which modifies an existing spreadsheet. Part of the program creates copies of certain sheets. To create one copy, the program runs perfectly and the resulting file is also fine, presenting no errors when opened in Excel. However, when creating two copies of the same worksheet, the program still runs just fine but, when opened in Excel, the following error appears regarding unreadable content:
Repaired Records: Worksheet properties from /xl/workbook.xml part (Workbook)
Here is the code used to perform the copy:
private void CopySheet(int sNum, int pNum, string type)
{
var tempSheet = SpreadsheetDocument.Create(new MemoryStream(), SpreadsheetDocumentType.Workbook);
WorkbookPart tempWBP = tempSheet.AddWorkbookPart();
var part = Document.XGetWorkSheetPart(sNum);
var sheetData = part.Worksheet.ChildElements[5].Clone() as SheetData;
var merge = part.Worksheet.ChildElements[6].Clone() as MergeCells;
WorksheetPart tempWSP = tempWBP.AddPart<WorksheetPart>(part);
var copy = Document.WorkbookPart.AddPart<WorksheetPart>(tempWSP);
//copy.Worksheet.RemoveChild<SheetData>(copy.Worksheet.ChildElements[5] as SheetData);
//copy.Worksheet.InsertAt<SheetData>(sheetData, 5);
//copy.Worksheet.RemoveChild<MergeCells>(copy.Worksheet.ChildElements[6] as MergeCells);
//copy.Worksheet.InsertAt<MergeCells>(merge, 6);
//copy.Worksheet.SheetProperties.CodeName.Value = "Phase" + pNum + type;
var sheets = Document.WorkbookPart.Workbook.Sheets;
var sheet = new Sheet();
sheet.Id = Document.WorkbookPart.GetIdOfPart(copy);
sheet.Name = "Phase " + pNum + " " + type;
sheet.SheetId = (uint)sheets.ChildElements.Count;
sheets.Append(sheet);
}
This method makes use of the fact that .AddPart<>() performs a deep copy of any Part (and anything it references to) which does not already belong in the document to, with the help of a temporary sheet, create a deep copy of all referenced parts of a WorkSheet.
As stated above, this works quite well if the function is called only once for a given sheet. If it is called more than once, however, the file when opened in Excel gives an error of unreadable content. That being said, the file itself seems just fine, with no missing data or anything (which would help to figure out what exactly was wrong), just the error saying there was something wrong.
The lines that are commented out are a "hack" I had to do to deal with problems regarding .AddPart<>(), but I won't go into much detail with them here because I've already posted about this here (but I still haven't gotten a reply, so by all means, please answer that question, too!). That being said, those lines seem to have no relevance to this current problem since the error appears with or without those lines of code.
I noticed the source file's sheets were not numbered properly for whatever reason, so their SheetID's were not sequencial (6, 1, 3). Therefore, my program was, when creating two copies of the file with the code sheet.SheetID = sheets.ChildElements.Count + 1, setting the values as 4, 5, 6 and 7, which lead to a conflict with the existing sheets. I've therefore modified that section so that the program always gets a valid ID:
// ...
uint id = 1;
bool valid = false;
while (!valid)
{
uint temp = id;
foreach (OpenXmlElement e in sheets.ChildElements)
{
var s = e as Sheet;
if (id == s.SheetId.Value)
{
id++;
break;
}
}
if (temp == id)
valid = true;
}
sheet.SheetId = id;
//...
With this my sheetIDs become (6, 1, 3, 2, 4, 5, 7), and therefore there are no conflicts and no more errors!
static WorksheetPart GetWorkSheetPart(WorkbookPart workbookPart, string sheetName)
{
//Get the relationship id of the sheetname
string relId = workbookPart.Workbook.Descendants<Sheet>()
.Where(s => s.Name.Value.Equals(sheetName))
.First()
.Id;
return (WorksheetPart)workbookPart.GetPartById(relId);
}
static void FixupTableParts(WorksheetPart worksheetPart, int numTableDefParts)
{
//Every table needs a unique id and name
foreach (TableDefinitionPart tableDefPart in worksheetPart.TableDefinitionParts)
{
tableId++;
tableDefPart.Table.Id = (uint)tableId;
tableDefPart.Table.DisplayName = "CopiedTable" + tableId;
tableDefPart.Table.Name = "CopiedTable" + tableId;
tableDefPart.Table.Save();
}
}
private void CopySheet(SpreadsheetDocument mySpreadsheet, string sheetName, string clonedSheetName)
{
WorkbookPart workbookPart = mySpreadsheet.WorkbookPart;
IEnumerable<Sheet> source = workbookPart.Workbook.Descendants<Sheet>();
Sheet sheet = Enumerable.First<Sheet>(source, (Func<Sheet, bool>)(s => (string)s.Name == sheetName));
string sheetWorkbookPartId = (string)sheet.Id;
WorksheetPart sourceSheetPart = (WorksheetPart)workbookPart.GetPartById(sheetWorkbookPartId);
SpreadsheetDocument tempSheet = SpreadsheetDocument.Create(new MemoryStream(), mySpreadsheet.DocumentType);
WorkbookPart tempWorkbookPart = tempSheet.AddWorkbookPart();
WorksheetPart tempWorksheetPart = tempWorkbookPart.AddPart<WorksheetPart>(sourceSheetPart);
//Add cloned sheet and all associated parts to workbook
WorksheetPart clonedSheet = workbookPart.AddPart<WorksheetPart>(tempWorksheetPart);
int numTableDefParts = sourceSheetPart.GetPartsCountOfType<TableDefinitionPart>();
tableId = numTableDefParts;
if (numTableDefParts != 0)
FixupTableParts(clonedSheet, numTableDefParts);
CleanView(clonedSheet);
//Add new sheet to main workbook part
Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
Sheet copiedSheet = new Sheet();
copiedSheet.Name = clonedSheetName;
copiedSheet.Id = workbookPart.GetIdOfPart(clonedSheet);
copiedSheet.SheetId = (uint)sheets.ChildElements.Count + 1;
sheets.Append(copiedSheet);
workbookPart.Workbook.Save();
}
static void CleanView(WorksheetPart worksheetPart)
{
SheetViews views = worksheetPart.Worksheet.GetFirstChild<SheetViews>();
if (views != null)
{
views.Remove();
worksheetPart.Worksheet.Save();
}
}