My task is parse an excel file and converted it to web table.
To achieve that objective, I need the column numbers, width of each column, row numbers, and each cell and cell property within the row.
So far, I can get the rows, the cells, the cell property such as border,font, and so on. But I can't get the column width.
When I open the excel file and get columns by following code
Columns columns = sheet.Descendants<Columns>().FirstOrDefault()
But, sometimes I can get it, sometimes the value is null.
I read the excel file by openxml tools. The following code is not always there.
Columns columns1 = new Columns();
Column column1 = new Column(){ Min = (UInt32Value)7U, Max = (UInt32Value)7U, Width = 39.6328125D, CustomWidth = true };
columns1.Append(column1);
If you open an empty excel file and do not change column width, then you save it. The code is not there.
So my question is how can I get the column width?
A column width can have either the default width or custom width. As you state, the custom width can be read from Column.Width property. If the default column width is set, it can be read from SheetFormatProperties Class. However, if DefaultColumnWidth property is set to null, The default column width is 8.43 characters.
To get the DefaultColumnWidth :
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(filePath, true))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
var sheetFormatProps = workSheet.SheetFormatProperties;
var defaultColWidth = sheetFormatProps.DefaultColumnWidth;
if (defaultColWidth == null)
{
defaultColWidth = 8.43;
}
}
Related
This is what I want to have
Find the location of the value "arriere" in an Excel sheet and get the value next to it.
column I row 35 = "arriere"
column J row 35 = 1456.00
Right now I'm using the following code :
using (var package = new ExcelPackage(f.FullName))
{
var worksheet = package.Workbook.Worksheets[0];
var montantArriere = from cell in worksheet.Cells["G:L"]
where cell.Value?.ToString() == "Total Arriéré"
select worksheet.Cells[cell.Start.Row, 10].Value;
}
The code works but if the value "arriere" change of column I won't be able to find the value next to it since the cell.Start.Row is set by 10 from the start.
Is there any way to have the value next to "arriere" more dynamically?
You can use Offset on cell to get a cell based on the offset you choose. (row, column)
using (var package = new ExcelPackage(f.FullName))
{
var worksheet = package.Workbook.Worksheets[0];
var montantArriere = from cell in worksheet.Cells["G:L"]
where cell.Value?.ToString() == "Total Arriéré"
select cell.Offset(0,1).Value;
}
I have excel template with empty one-column table. I need to populate it with some string values (this is needed for setting lookups using data validation, but I guess it doesn't really matter)
I came up to getting Table object and I assume I should use Append method
var workBookPart = doc.WorkbookPart;
var lookupsSheet = (Sheet)workBookPart.Workbook.Sheets.FirstOrDefault(x => (x is Sheet && ((Sheet)x).Name == "Lookups"));
var worksheetPart = (WorksheetPart)workBookPart.GetPartById(lookupsSheet.Id);
var table = worksheetPart.TableDefinitionParts.FirstOrDefault(x => x.Table.DisplayName == "ValuesTable")?.Table;
Can someone enlighten about the correct way of adding rows to such table. Thanks!
I would suggest you use the ClosedXML library to set the values of cells in your worksheet. By using ClosedXML, you will be able to populate the cells you want in the following fashion:
var workbook = new XLWorkbook();
var ws = workbook.Worksheets.Add("Demo");
// Set the values for the cells
ws.Cell(1, 1).Value = "Value";
ws.Cell(2, 1).Value = 1;
ws.Cell(3, 1).Value = 2;
ws.Cell(4, 1).Value = 3;
ws.Cell(5, 1).Value = true;
Note that you can set the value of a cell to a string, an integer, and a boolean without doing any explicit casting. You can set the value of a cell without doing any explicit casting to other types as well, as it is explained in the following link: Cell Values.
For more information regarding the ClosedXML library please refer to the documentation.
As a side note, I was really eager to use Open XML to manipulate Excel spreadsheets but I found ClosedXML way easier to use.
I need to export a data set:
DataSet ds = new DataSet("tabless");
ds.Tables.Add(table01);
ds.Tables.Add(table02);
ds.Tables.Add(table03);
it contains 3 data table, each one of them is:
table01.Columns.Add("Branch",typeof(string));
table01.Columns.Add("Today", typeof(double));
table01.Columns.Add("MTD",typeof(double));
table01.Columns.Add("LM",typeof(double));
table01.Columns.Add("Differ",typeof(double),"LM-MTD");
table01.Columns.Add("YTD",typeof(double));
So I need to export them to an excel sheet with number format and comma separator.
Like when value = -200000 will be (200,000) with red color and value 300000 will be 300,000 and apply this to each table in the work sheet.
For more info check the below photo:
Screenshot http://postimg.org/image/lj55lz6ib/
You could use NumberingFormat
//Create a NumberingFormat
NumberingFormat numForm2decim = new NumberingFormat();
numForm2decim.NumberFormatId = 1u;
numForm2decim.FormatCode = StringValue.FromString("0.00");
//Use it in a CellFormat
CellFormat cellformatNumber2Decim = new CellFormat();
cellformatNumber2Decim.NumberFormatId = numForm2decim.NumberFormatId;
cellformatNumber2Decim.ApplyNumberFormat = true;
//And apply the cellFormat to your Cell throw the StyleIndex Property
I created an excel file dynamicly using openXML. Inside this sheet there are multiple sheets. Inside each sheet there can be rows that are write-protected.
I use an excel file as template. In this template there are "normal" rows which allow editing and a row that does not. I grab the row and copy it to the places where I do not want the user to be able to edit the contents:
private Row CloneRow(Row sourceRow, uint index, bool? hidden = null)
{
var targetRow = (Row) sourceRow.CloneNode(true);
if (hidden.HasValue)
{
targetRow.Hidden = hidden;
}
foreach (Cell cell in targetRow.Elements<Cell>())
{
// Update the references for reserved cells.
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(targetRow.RowIndex.Value.ToString(), index.ToString()));
cell.CellFormula = null;
}
// Update the row index.
targetRow.RowIndex = new UInt32Value(index);
return targetRow;
}
the parameter sourceRow is read from the template:
List<Row> rows = sheet.ChildElements.OfType<Row>().ToList();
rowChangeAllowed=rows.FirstOrDefault(rw=>rw.RowIndex==3);
rowNotChangeAllowed=rows.FirstOrDefault(rw=>rw.RowIndex==4);
Everything works as expected. But when I open the file in Excel, rows that should be proteced on ANY sheet are protected on ALL sheets.
Example:
Sheet 1: Row 4+5 should be protected
Sheet 2: Row 7 should be protected.
Now on sheet 1 rows 4,5 and 7 are protected
When I switch to the second sheet, suddenly everything works as needed: On Sheet 1, row 4+5 are still protected, but row 7 is not.
Because the behaviour is only wrong directly after opening the file, but is correct when I switch between the sheets: Is there an additional command I have to call to "refresh" the file after creating?
Additional Issue:
When I change a cell in sheet 1, it also is automaticly changed in sheet 2 (again: until I swap the sheets once manually)
The problem was having to many views in the sheet. The following code solved the issue:
//There can only be one sheet that has focus
SheetViews views = worksheetPart.Worksheet.GetFirstChild<SheetViews>();
if (views != null)
{
views.Remove();
worksheetPart.Worksheet.Save();
}
(got it from http://blogs.msdn.com/b/brian_jones/archive/2009/02/19/how-to-copy-a-worksheet-within-a-workbook.aspx)
I have a predefined Excel workbook with all sheets in place and I need to write content to it. I succesfully write to cells.
The problem is in a particular worksheet that i need to add three columns to it. In the code bellow, first i'm grabbing the Worksheet and then i proceed to add columns. This code runs fine, i mean, no exception is thrown, but then I get an error when I try to open the Excel file, stating that there are some content that cannot be read and all the content of this particular worksheet is cleared.
I know that the problem is with this operation because if I comment out those lines that add columns, the workbook opens just fine with all the cells values I write from code in place.
This is the relevant code, for testing purpose I'm trying to add 3 columns:
using (SpreadsheetDocument document = SpreadsheetDocument.Open(outputPath, true)){
Sheet sheet2 = document.WorkbookPart.Workbook.Descendants<Sheet>().Single( s => s.Name == "Miscellaneous Credit" );
Worksheet workSheet2 = ( (WorksheetPart)document.WorkbookPart.GetPartById( sheet2.Id ) ).Worksheet;
Columns cs = new Columns();
for ( var y = 1; y <= 3; y++ ) {
Column c = new Column()
{
Min = (UInt32Value)1U,
Max = (UInt32Value)1U,
Width = 44.33203125D,
CustomWidth = true
};
cs.Append( c );
}
workSheet2.Append( cs );
}
EDIT : As per Chris's explanation about columns's concept
using (SpreadsheetDocument document = SpreadsheetDocument.Open(outputPath, true)){
Sheet sheet2 = document.WorkbookPart.Workbook.Descendants<Sheet>().Single( s => s.Name == "Miscellaneous Credit" );
Worksheet workSheet2 = ( (WorksheetPart)document.WorkbookPart.GetPartById( sheet2.Id ) ).Worksheet;
// Check if the column collection exists
Columns cs = workSheet2.Elements<Columns>().FirstOrDefault();
if ( ( cs == null ) ) {
// If Columns appended to worksheet after sheetdata Excel will throw an error.
SheetData sd = workSheet2.Elements<SheetData>().FirstOrDefault();
if ( ( sd != null ) ) {
cs = workSheet2.InsertBefore( new Columns(), sd );
}
else {
cs = new Columns();
workSheet2.Append( cs );
}
}
//create a column object to define the width of columns 1 to 3
Column c = new Column
{
Min = (UInt32Value)1U,
Max = (UInt32Value)3U,
Width = 44.33203125,
CustomWidth = true
};
cs.Append( c );
}
This first part of answer deals about how to set columns width (based on the initial sample code, I was thinking that you wanted only define the width of the columns).
First, it seems you misunderstood what are Min and Max properties of the Column object. They represent respectively First and Last column affected by this 'column info' record. So if you have a set of contiguous columns with the same width, you can set that width using one Column class. In your snippet you define 3 times the width of the same column (Index 1).
Then, you presume Columns collection doesn't exist yet...
And finally, the main point is that if the Columns collection is appended after SheetData, Excel will throw error.
Final code that work for me (Open XML SDK 2.0)
using (SpreadsheetDocument document = SpreadsheetDocument.Open(outputPath, true)) {
Sheet sheet2 = document.WorkbookPart.Workbook.Descendants<Sheet>().Single(s => s.Name == "Your sheet name");
Worksheet workSheet2 = ((WorksheetPart)document.WorkbookPart.GetPartById(sheet2.Id)).Worksheet;
// Check if the column collection exists
Columns cs = workSheet2.Elements<Columns>().FirstOrDefault();
if ((cs == null)) {
// If Columns appended to worksheet after sheetdata Excel will throw an error.
SheetData sd = workSheet2.Elements<SheetData>().FirstOrDefault();
if ((sd != null)) {
cs = workSheet2.InsertBefore(new Columns(), sd);
} else {
cs = new Columns();
workSheet2.Append(cs);
}
}
//create a column object to define the width of columns 1 to 3
Column c = new Column {
Min = (UInt32Value)1U,
Max = (UInt32Value)3U,
Width = 44.33203125,
CustomWidth = true
};
cs.Append(c);
}
I'm still confused on how to perform column insert. Says I have
columns A, B and C, I want to insert three columns between B and C,
ending up with columns A,B,C,D,E,F. How can i achieve it?
The Columns object in OpenXml SDK is here to store styles and width informations for the columns. Inserting a Column in the collection won't "insert" a column in the sheet.
"Inserting" a column like you mean is a very large and complex task with OpenXmlSDK.
From my understanding of the problem, it means you will have to find all cells and shift them by changing their reference (ex. a cell with ref "B1" would become "F1" after inserting 3 columns, etc ...). And it means you will have to change a lot of other things (reference of cell in formulas for example).
This kind of task could be easily done with Office.Interop or probably with libraries like EEPlus or ClosedXml.