I have a spreadsheet template which has a table (Table inserted into sheet from Insert tab) and when the user clicks on the export button these header values of the table should be updated by new values dynamically. For this I'm using c# and DocumentFormat.OpenXml library.
After exporting header values are getting updated in the spreadsheet table but when I open the excel document it says "We found problem with some content in '<>.xlsx' . Do you want us to recover as much as we can ? ".
Also, when I tried updating table rows other than header row, I wasn't getting above popup.
I have tried below approaches to update table header values.
Approach 1
using (SpreadsheetDocument document = SpreadsheetDocument.Open("test.xlsx", true))
{
WorkbookPart wbPart = document.WorkbookPart;
Sheet sheet = wbPart.Workbook.Descendants<Sheet>().Where(s => s.name =="Sheet1").FirstOrDefault();
WorksheetPart wsPart = (WorksheetPart) (wbPart.GetPartById(sheet.Id));
WorkSheet wSheet = wsPart.WorkSheet;
Row row = wSheet.Elements<Row>().Where(r => r.RowIndex ==1 ).FirstOrDefault();
Cell cell = row.Elements<Cell>().Where(c => string.Compare
(c.CellReference.Value, "A1" , true) == 0).First();
cell.CellValue = new CellValue("new Header");
cell.DataType = CellValues.String;
wbPart.WorkBook.save();
}
Approach 2
foreach(TableDefinitionPart tdp in wsPart.TableDefinitionParts )
{
QueryTablePart qtp= tdp .QueryTableParts.FirstOrDefault();
Table excelTable = tdp .Table;
int i = 0;
foreach(TableColumn col in excelTable.TableColumns)
{
col.name.Value = "Header"+i;
i++;
}
}
Related
My requirement is to change the text of the textbox in excel using openxml c#. i can find the textbox using the below code:
WorkbookPart workbookPart = document.WorkbookPart;
Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
//To add the month in the first KPMG sheet
string sheetName = "Test";
Sheet sheet1 = document.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName).FirstOrDefault();
string relationshipId = sheet1.Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(relationshipId);
var ocaElems = worksheetPart.DrawingsPart.WorksheetDrawing.Elements<TwoCellAnchor>();
foreach (TwoCellAnchor twoCellAnchor in ocaElems)
{
if (twoCellAnchor.InnerText.Contains("google"))
{
//twoCellAnchor.InnerText = "Ted Report - August";
//while setting innerText its showing error as set is not accessible
}
}
I have been struggling to find a solution on how to read a large xlsx file with OpenXml. I have tried the microsoft samples without luck. I simply need to read an excel file into a DataTable in c#. I am not concerned with value types in the datatable, everything can be stored as a string values.
The samples I have found so far don't retain the structure of the spreadsheet and only return the values of the cells.
Any ideas?
The open xml SDK can be a little hard to understand. However, I have found it useful to use http://simpleooxml.codeplex.com/ this code plex project. It adds a thin layer over the sdk to more easily parse through excel files and work with styles.
Then you can use something like the following with their worksheet reader to recurse through and grab the values you want
System.IO.MemoryStream ms = Utility.StreamToMemory(xslxTemplate);
using (SpreadsheetDocument document = SpreadsheetDocument.Open(ms, true))
{
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
if (sheets.Count() == 0)
{
// The specified worksheet does not exist.
return null;
}
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(relationshipId);
string myval =WorksheetReader.GetCell("A", 0, worksheetPart).CellValue.InnerText;
// Put in a loop to go through contents of document
}
You can get DataTable this way:
using (SpreadsheetDocument spreadsheet = SpreadsheetDocument.Open(fileName, false))
{
DataTable data = ToDataTable(spreadsheet, "Employees");
}
This method will read Excel sheet data as DataTable
public DataTable ToDataTable(SpreadsheetDocument spreadsheet, string worksheetName)
{
var workbookPart = spreadsheet.WorkbookPart;
var sheet = workbookPart
.Workbook
.Descendants<Sheet>()
.FirstOrDefault(s => s.Name == worksheetName);
var worksheetPart = sheet == null
? null
: workbookPart.GetPartById(sheet.Id) as WorksheetPart;
var dataTable = new DataTable();
if (worksheetPart != null)
{
var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
foreach (Row row in sheetData.Descendants<Row>())
{
var values = row
.Descendants<Cell>()
.Select(cell =>
{
var value = cell.CellValue.InnerXml;
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
value = workbookPart
.SharedStringTablePart
.SharedStringTable
.ChildElements[int.Parse(value)]
.InnerText;
}
return (object)value;
})
.ToArray();
dataTable.Rows.Add(values);
}
}
return dataTable;
}
Using C# how can I "jump" to a specific cell in an Excel spreadsheet and go to the next row? I need to populate an existing spreadsheet with data from a list. This is how I thought it would work:
Globals.LookupTable.Range["A2"].Select();
foreach (CFolderType ft in FolderTypes) {
Globals.LookupTable.Rows.Next.Value2 = ft.name;
}
This shall move down in column A and insert the values.
Something like this should do the work:
Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.ApplicationClass();
excel.Workbooks.Add(System.Reflection.Missing.Value);
int rowIdx = 2;
foreach (CFolderType ft in FolderTypes)
{
Microsoft.Office.Interop.Excel.Range aCell = excel.get_Range("A" + rowIdx.ToString(), System.Reflection.Missing.Value);
aRange.Value = ft.name;
rowIdx++;
}
I need to embed an Excel document in a Word document. I used the answer on this SO question -> How can I embed any file type into Microsoft Word using OpenXml 2.0;
Everything works fine except that:
DrawAspect = OVML.OleDrawAspectValues.Icon lets you edit the Excel object by opening a new Excel instance. However, when I edit the data, it is not updated in the Word document.
DrawAspect = OVML.OleDrawAspectValues.Content lets you edit the Excel object directly in the Word document.
My question is, what do I have to change in the code so can I edit the Excel object in the new instance and have it properly reflected in the Word document? I tried everything to no avail.
Something tells me that DrawAspect = OVML.OleDrawAspectValues.Icon suggests that the object acts as an Icon, and changes cannot be properly reflected in this icon.
You could try a way I prorosed here:
How to insert an Image in WORD after a bookmark using OpenXML.
In short, use Open XML SDK 2.0 Productivity Tool (which is a part of Open XML SDK 2.0). Do whatever you need to do with document manually in MS Word. Then open this file in Open XML SDK 2.0 Productivity Tool. Then find the edits you are interested in to see how it is represented in OpenXML format , as well as how to do that programmaticaly.
Hope that helps!
UPDATED:
Okay - I have got better now what's the problem is... So in addition to my advice above I would recommend you to look at this threads on MSDN Forum:
How to modify the imbedded Excel in a Word document supplying chart
data
Embedded excel sheet in word
I let myself to repost the code sample (posted on MSDN Forum by Ji Zhou) just to avoid the deletion of original thread there.
Hope it is helpful enough to retrieve the Excel object from Word, change some cells and embed it back into Word.
public static void Main()
{
using (WordprocessingDocument wDoc = WordprocessingDocument.Open(#"D:\test.docx", true))
{
Stream stream = wDoc.MainDocumentPart.ChartParts.First().EmbeddedPackagePart.GetStream();
using (SpreadsheetDocument ssDoc = SpreadsheetDocument.Open(stream, true))
{
WorkbookPart wbPart = ssDoc.WorkbookPart;
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
Where(s => s.Name == "Sheet1").FirstOrDefault();
if (theSheet != null)
{
Worksheet ws = ((WorksheetPart)(wbPart.GetPartById(theSheet.Id))).Worksheet;
Cell theCell = InsertCellInWorksheet("C", 2, ws);
theCell.CellValue = new CellValue("5");
theCell.DataType = new EnumValue<CellValues>(CellValues.Number);
ws.Save();
}
}
}
}
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, Worksheet worksheet)
{
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
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);
}
if (row.Elements<Cell>().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0)
{
return row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).First();
}
else
{
Cell refCell = null;
foreach (Cell cell in row.Elements<Cell>())
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheet.Save();
return newCell;
}
}
I have a spreadsheet document that has 182 columns in it. I need to place the spreadsheet data into a data table, tab by tab, but i need to find out as I'm adding data from each tab, what is the tab name, and add the tab name to a column in the data table.
This is how I set up the data table.
I then loop in the workbook and drill down to the sheetData object and walk through each row and column, getting cell data.
DataTable dt = new DataTable();
for (int i = 0; i <= col.GetUpperBound(0); i++)
{
try
{
dt.Columns.Add(new DataColumn(col[i].ToString(), typeof(string)));
}
catch (Exception e)
{
MessageBox.Show("Uploader Error" + e.ToString());
return null;
}
}
dt.Columns.Add(new DataColumn("SheetName", typeof(string)));
However at the end of the string array that I use for the Data Table, I need to add the tab name. How can I find out the tab name as I'm looping in the sheet in Open XML?
Here is my code so far:
using (SpreadsheetDocument spreadSheetDocument =
SpreadsheetDocument.Open(Destination, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
Workbook workbook = spreadSheetDocument.WorkbookPart.Workbook;
Sheets sheets =
spreadSheetDocument
.WorkbookPart
.Workbook
.GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();
OpenXmlElementList list = sheets.ChildElements;
foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
{
Worksheet worksheet = worksheetpart.Worksheet;
foreach (SheetData sheetData in worksheet.Elements<SheetData>())
{
foreach (Row row in sheetData.Elements())
{
string[] thisarr = new string[183];
int index = 0;
foreach (Cell cell in row.Elements())
{
thisarr[(index)] = GetCellValue(spreadSheetDocument, cell);
index++;
}
thisarr[182] = ""; //need to add tabname here
if (thisarr[0].ToString() != "")
{
dt.Rows.Add(thisarr);
}
}
}
}
}
return dt;
Just a note: I did previously get the tab names from the InnerXML property of "list" in
OpenXmlElementList list = sheets.ChildElements;
however I noticed as I'm looping in the spreadsheet it does not get the tab names in the right order.
Here is a handy helper method to get the Sheet corresponding to a WorksheetPart:
public static Sheet GetSheetFromWorkSheet
(WorkbookPart workbookPart, WorksheetPart worksheetPart)
{
string relationshipId = workbookPart.GetIdOfPart(worksheetPart);
IEnumerable<Sheet> sheets = workbookPart.Workbook.Sheets.Elements<Sheet>();
return sheets.FirstOrDefault(s => s.Id.HasValue && s.Id.Value == relationshipId);
}
Then you can get the name from the sheets Name-property:
Sheet sheet = GetSheetFromWorkSheet(myWorkbookPart, myWorksheetPart);
string sheetName = sheet.Name;
...this will be the "tab name" OP referred to.
For the record the opposite method would look like:
public static Worksheet GetWorkSheetFromSheet(WorkbookPart workbookPart, Sheet sheet)
{
var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
return worksheetPart.Worksheet;
}
...and with that we can also add the following method:
public static IEnumerable<KeyValuePair<string, Worksheet>> GetNamedWorksheets
(WorkbookPart workbookPart)
{
return workbookPart.Workbook.Sheets.Elements<Sheet>()
.Select(sheet => new KeyValuePair<string, Worksheet>
(sheet.Name, GetWorkSheetFromSheet(workbookPart, sheet)));
}
Now you can easily enumerate through all Worksheets including their name.
Throw it all into a dictionary for name-based lookup if you prefer that:
IDictionary<string, WorkSheet> wsDict = GetNamedWorksheets(myWorkbookPart)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
...or if you just want one specific sheet by name:
public static Sheet GetSheetFromName(WorkbookPart workbookPart, string sheetName)
{
return workbookPart.Workbook.Sheets.Elements<Sheet>()
.FirstOrDefault(s => s.Name.HasValue && s.Name.Value == sheetName);
}
(Then call GetWorkSheetFromSheet to get the corresponding Worksheet.)
The sheet names are stored in the WorkbookPart in a Sheets element which has children of element Sheet which corresponds to each worksheet in the Excel file. All you have to do is grab the correct index out of that Sheets element and that will be the Sheet you are on in your loop. I added a snippet of code below to do what you want.
int sheetIndex = 0;
foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
{
Worksheet worksheet = worksheetpart.Worksheet;
// Grab the sheet name each time through your loop
string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(sheetIndex).Name;
foreach (SheetData sheetData in worksheet.Elements<SheetData>())
{
...
}
sheetIndex++;
}
Using spreadsheetDocument As SpreadsheetDocument = spreadsheetDocument.Open("D:\Libro1.xlsx", True)
Dim workbookPart As WorkbookPart = spreadsheetDocument.WorkbookPart
workbookPart.Workbook.Descendants(Of Sheet)()
Dim worksheetPart As WorksheetPart = workbookPart.WorksheetParts.Last
Dim text As String
For Each Sheet As Sheet In spreadsheetDocument.WorkbookPart.Workbook.Sheets
Dim sName As String = Sheet.Name
Dim sID As String = Sheet.Id
Dim part As WorksheetPart = workbookPart.GetPartById(sID)
Dim actualSheet As Worksheet = part.Worksheet
Dim sheetData As SheetData = part.Worksheet.Elements(Of SheetData)().First
For Each r As Row In sheetData.Elements(Of Row)()
For Each c As Cell In r.Elements(Of Cell)()
text = c.CellValue.Text
Console.Write(text & " ")
Next
Next
Next
End Using
Console.Read()
worksheet.GetAttribute("name","").Value