Please consider this Excel:
and it's XML:
I want to create such this Excel that has multiple merged cells using OpenXML.
How I can do this?
thanks
You can use the MergeCells and MergeCell classes to create the merged cells you require. The MergeCells class is the collection of merge cells (<mergeCells count="3"> in your XML) and the MergeCell class represents each individual set of merged cells (<mergeCell ref="xx:xx" /> in your XML). To populate data in the merged cells you need to add the value to the upper left most cell; any other values will be ignored.
The following code will create a new file with merged cells.
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
{
WorkbookPart workbookpart = myDoc.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
SheetData sheetData = new SheetData();
//add a row
Row firstRow = new Row();
firstRow.RowIndex = (UInt32)1;
//create a cell in C1 (the upper left most cell of the merged cells)
Cell dataCell = new Cell();
dataCell.CellReference = "C1";
CellValue cellValue = new CellValue();
cellValue.Text = "99999";
dataCell.Append(cellValue);
firstRow.AppendChild(dataCell);
sheetData.AppendChild(firstRow);
// Add a WorkbookPart to the document.
worksheetPart.Worksheet = new Worksheet(sheetData);
//create a MergeCells class to hold each MergeCell
MergeCells mergeCells = new MergeCells();
//append a MergeCell to the mergeCells for each set of merged cells
mergeCells.Append(new MergeCell() { Reference = new StringValue("C1:F1") });
mergeCells.Append(new MergeCell() { Reference = new StringValue("A3:B3") });
mergeCells.Append(new MergeCell() { Reference = new StringValue("G5:K5") });
worksheetPart.Worksheet.InsertAfter(mergeCells, worksheetPart.Worksheet.Elements<SheetData>().First());
//this is the part that was missing from your code
Sheets sheets = myDoc.WorkbookPart.Workbook.AppendChild(new Sheets());
sheets.AppendChild(new Sheet()
{
Id = myDoc.WorkbookPart.GetIdOfPart(myDoc.WorkbookPart.WorksheetParts.First()),
SheetId = 1,
Name = "Sheet1"
});
}
The code above produces:
Related
There is code which works with Excel like this:
using SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open("filePath", true);
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart!;
SheetData sheetData = GetSheetData(workbookPart, outFile);
Worksheet worksheet = sheetData.Parent as Worksheet;
List<SharedStringItem> sharedStringTableElements = workbookPart.SharedStringTablePart!.SharedStringTable.Elements<SharedStringItem>().ToList();
List<Row> sheetRows = sheetData.Elements<Row>().ToList();
...
But it breaks when I try to open my generated file. SharedStringTablePart is null.
This is how I create Excel files:
using SpreadsheetDocument spreadSheet = CreateSpreadsheet(filePath);
WorkbookPart workbookPart = spreadSheet.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
SheetData sheetData = new();
worksheetPart.Worksheet = new Worksheet(sheetData);
Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
string relationshipId = workbookPart.GetIdOfPart(worksheetPart);
Sheet sheet = new() {
Id = relationshipId,
SheetId = FirstSheetId,
Name = "SheetName",
};
sheets.Append(sheet);
//fill sheetData here
workbookPart.Workbook.Save();
spreadSheet.Close();
How do I change this code so it doesn't break when accessing a SharedStringTablePart? How can I fill it in correctly?
Thanks in advance.
You create a new SpreadsheetDocument and add the WorkbookPart. Right after being created, that new WorkbookPart does not have any associated parts. Thus, you'll have to create all required parts, including the SharedStringTablePart, like so:
// Let's assume you created the WorkbookPart as follows.
WorkbookPart workbookPart = spreadSheet.AddWorkbookPart();
// Create and initialize the associated SharedStringTablePart as well.
var sharedStringTablePart = workbookPart.AddNewPart<SharedStringTablePart>();
sharedStringTablePart.SharedStringTable = new SharedStringTable();
Once added as shown above, workbookPart.SharedStringTablePart will no longer be null.
I came back to write how I did it.
First of all, we need to create SharedStringTablePart as Thomas said:
private static SharedStringTablePart GetSharedStringTablePart(SpreadsheetDocument spreadSheet)
{
if (spreadSheet.WorkbookPart!.GetPartsOfType<SharedStringTablePart>().Any())
{
return spreadSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
}
else
{
return spreadSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
}
}
Then I created a helper method to insert the data into this table:
private static string InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
shareStringPart.SharedStringTable ??= new SharedStringTable();
int i = 0;
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
return i.ToString();
}
i++;
}
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text)));
shareStringPart.SharedStringTable.Save();
return i.ToString();
}
So next time when I inserting cell value I do it like this:
string index = InsertSharedStringItem("some text", sharedTablePart);
Cell cell = new() {
DataType = CellValues.SharedString,
CellValue = new CellValue(index),
...
};
row.AppendChild(cell);
I'm using Documentformat.Openxml package
[https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet?view=openxml-2.8.1]
to generate a Pivot table in a CSV dynamically from a Dictionary<string, List<string>> object which has a structure similar to this:
I'm following the Sample here to create the table which should look somewhat like:
but not sure about how to create the table from the dictionary of data.
My current code snippet:
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "mySheet"
};
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("Microsoft")
};
row.Append(cell);
sheetData.Append(row);
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
// Close the document.
spreadsheetDocument.Close();
In the above snippet, you can see that I'm hardcoding the cell value but in reality I want to populate it dynamically from the dictionary and there is a large amount of data in it. What is the best way to do this? Can someone please help?
As your question shows you want to generate excel file using Open XML. You should take a look at these answers
1 Using open XML to create excel file
2 How do you convert Excel to CSV using OpenXML SDK?
and for other websites you can visit
3 https://www.c-sharpcorner.com/article/export-and-import-excel-file-using-closedxml-in-asp-net-mvc/
I'm fairly new to creating Excel spreadsheets in C# and I'm looking for advice.
I've spent 2 or 3 days now looking through documentation and blogs etc but I cannot seem to find an answer to a common task.
I need to insert a text value into a specific worksheet. I cant easily post my code at the moment but it appears to be an issue with every example I've seen of the definition of a sheet, it always gets the first child.
I need to iterate through all sheets and dependant on the sheet name then go and insert a value.
I.e. If the sheet name = "testA" then write TestA, if the sheet name = "testB" then write "TestB".
Currently I can insert a value for the workbook but it inserts the same value for every sheet.
Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
[Microsoft documentation] (https://learn.microsoft.com/en-us/office/open-xml/how-to-insert-text-into-a-cell-in-a-spreadsheet)
Please note I'm not just giving up on this I've just reached a bit of a wall with it and need some pointers.
Many Thanks, J
Namespaces I'm using:
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
private void btnExport_Click(object sender, EventArgs e)
{
//Using Microsofts Interop class to create Excel files gives mixed resuts and requires excel to be installed.
// This uses OpenXML library to achieve this and doesnt require excel to be installed: https://medium.com/swlh/openxml-sdk-brief-guide-c-7099e2391059
string filepath = "Test.xlsx";
// Create a spreadsheet document by supplying the filepath.
// By default, AutoSave = true, Editable = true, and Type = xlsx.
SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.
Create(filepath, SpreadsheetDocumentType.Workbook);
// Add a WorkbookPart to the document.
WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();
// Add a WorksheetPart to the WorkbookPart.
WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
// Add Sheets to the Workbook.
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.
AppendChild<Sheets>(new Sheets());
// Append a new worksheet and associate it with the workbook.
Sheet sheetOne = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Sheet1"
};
Sheet sheetTwo= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 2,
Name = "Sheet2"
};
Sheet sheetParameters = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 3,
Name = "Parameters"
};
Sheet sheetFour= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 4,
Name = "Sheet4"
};
Sheet sheetFive= new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.
GetIdOfPart(worksheetPart),
SheetId = 5,
Name = "Sheet5"
};
sheets.Append(sheetOne);
sheets.Append(sheetTwo);
sheets.Append(sheetParameters);
sheets.Append(sheetFour);
sheets.Append(sheetFive);
workbookpart.Workbook.Save();
// Close the document.
spreadsheetDocument.Close();
//InsertTextExistingExcel(filepath, "test1", "A", 1, "Parameters");
InsertInSheet(filepath, "test1", "A", 1, "Parameters");
//Any of these lists empty?
}
public static void InsertInSheet(string filePath, string value, string columnName, uint rowIndex, string sheetName)
{
using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(filePath, true))
{
getSheetDetails(spreadSheet);
}
}
//work here
public static void getSheetDetails(SpreadsheetDocument doc)
{
WorksheetPart worksheetPart;
//iterate through sheets
foreach (Sheet sheetDetail in doc.WorkbookPart.Workbook.Sheets)
{
if (sheetDetail.Name == "Parameters")
{
//each sheet has a worksheetPart
worksheetPart = (WorksheetPart)doc.WorkbookPart.GetPartById(sheetDetail.Id);
Cell cell = InsertCellInWorksheet("A", 1, worksheetPart);
cell.CellValue = new CellValue("test");
cell.DataType = new EnumValue<CellValues>(CellValues.String);
worksheetPart.Worksheet.Save();
}
}
}
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
//fix earlier worksheetPart only has a count of 1
Worksheet worksheet = worksheetPart.Worksheet;
//fix this to point to sheet
//SheetData sheetData = worksheet.Descendants<SheetData>();
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
MessageBox.Show(sheetData.InnerXml.ToString());
string cellReference = columnName + rowIndex;
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0)
{
row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
else
{
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
Cell refCell = row.Descendants<Cell>().LastOrDefault();
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertAfter(newCell, refCell);
worksheet.Save();
return newCell;
}
I have an .xlsx file, which already has some data.
I want to enter some data on I1 cell.
Update:
I tried this:
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
Row row1 = new Row()
{
RowIndex = (UInt32Value)1U
};
Cell cell = new Cell() { CellReference = "I1" };
CellFormula cellformula = new CellFormula();
cellformula.Text = "IF(A2 = A1,1,0)";
CellValue cellValue = new CellValue();
cellValue.Text = "0";
cell.Append(cellformula);
cell.Append(cellValue);
row1.Append(cell);
sheetData.Append(row1);
But it is not working.
Please provide sample code with your answer.
You are creating a new Row object even though one already exists in your document. You then give it a RowIndex which also already exists which means you'll end up with a corrupted file (you can't have two rows with the same index).
You need to find the existing Row and add your new Cell to that rather than creating a brand new Row. The easiest way to do that is to call GetFirstChild<Row> on sheetData.
As an aside, I would also not write the CellValue to the Cell as you are using a formula. In that instance the Value is used by Excel to prevent it having to recalculate the formula when the sheet loads. In your example though your value is wrong. Omitting the value altogether is probably the easiest way to guarentee correctness as Excel will then calculate the formula when the sheet loads.
That leads to the following code:
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, true))
{
WorkbookPart workBookPart = spreadsheetDocument.WorkbookPart;
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
//find the first row in the sheet data
Row row1 = sheetData.GetFirstChild<Row>();
//create a new cell
Cell cell = new Cell() { CellReference = "I1" };
CellFormula cellformula = new CellFormula();
cellformula.Text = "IF(A2 = A1,1,0)";
cell.Append(cellformula);
//append the cell to the row
row1.Append(cell);
}
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