I have a formated template stored in the Database.
after building and opening the Excel the cell has the format but its not formated like it should.
example: the field looks in the template like this. 1234.56$ but know it is looking like this 1234.56. so the $ is missing.
second example. 12% its looking like but know its looking like this 11.9999999997%
The value I put in are exact values. like 1234.56 and 11.9999999997% so if i put them manually in the generatet excle it worsk with the formating but not during the creating phase.
does anyone have some ideas?
My insert statment
public static void InsertRows(List<ExcelRow> rowDefinitions, Stream template, string sheetName)
{
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(template, true))
{
// tell Excel to recalculate formulas next time it opens the doc
doc.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
doc.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
foreach (var rd in rowDefinitions)
{
// first get the context (WS + SheetData)
var ws = GetWorksheetPart(doc.WorkbookPart, sheetName);
var sheetData = ws.Worksheet.Descendants<SheetData>().First();
var nr = CreateRow((uint)rd.RowIndex, sheetData);
foreach (var cd in rd.Cells)
{
var c = EnsureCell(nr, cd.ColumnName);
SetCellValue(cd.CellText, c, doc.WorkbookPart.SharedStringTablePart);
}
}
doc.WorkbookPart.Workbook.Save();
}
}
Related
I need to get the area of the merged cell, the line number on which the area ends in Excel using only DocumentFormat.OpenXml or ClosedXML, how to do this for each cell?
Using ClosedXML, this could be done with:
var ws = workbook.Worksheet("Sheet 1");
var cell = ws.Cell("A2");
var mergedRange = cell.MergedRange();
var lastCell = mergedRange.LastCell();
// or
var lastCellAddress = mergedRange.RangeAddress.LastAddress;
I found this to be a little clunky but I believe this to be the correct approach.
private static void GetMergedCells()
{
var fileName = $"c:\\temp\\Data.xlsm";
// Open the document.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, false))
{
// Get the WorkbookPart object.
var workbookPart = document.WorkbookPart;
// Get the first worksheet in the document. You can change this as need be.
var worksheet = workbookPart.Workbook.Descendants<Sheet>().FirstOrDefault();
// Retrieve the WorksheetPart using the Part ID from the previous "Sheet" object.
var worksheetPart = (WorksheetPart)workbookPart.GetPartById(worksheet.Id);
// Retrieve the MergeCells element, this will contain all MergeCell elements.
var mergeCellsList = worksheetPart.Worksheet.Elements<MergeCells>();
// Now loop through and spit out each range reference for the merged cells.
// You'll need to process the range either as a string or turn it into another
// object that gives you the end row.
foreach (var mergeCells in mergeCellsList)
{
foreach (MergeCell mergeCell in mergeCells)
{
Console.WriteLine(mergeCell.Reference);
}
}
}
}
If you couldn't already tell, this is using DocumentFormat.OpenXml.Spreadsheet
I have a code to read Excel file into data-table using OpenXml, it works fine but however, when I read a string like this "7.5" it gives me "7.4999999" in float format why is it so?
I just want to read it as it is i.e in the string. so this should "7.5" should be 7.5 only not 7.499 or any other thing.
any help would be appreciated.
here is my code to read Excel in datatable
private static string ConvertFileToDataTable(Stream fileStream, DataTable framworkDatatable, List<FileErrorModel> fileErrorModels)
{
string frameworkName;
InitializeDataTable(framworkDatatable);
using (var document = SpreadsheetDocument.Open(fileStream, true))
{
var workbookPart = document.WorkbookPart;
var mysheet = (Sheet)document.WorkbookPart.Workbook.Sheets.ChildElements.GetItem(0);
frameworkName = mysheet.Name;
var worksheet = ((WorksheetPart)workbookPart.GetPartById(mysheet.Id)).Worksheet;
var sheetData = (SheetData)worksheet.ChildElements.GetItem(4);
foreach (var row in sheetData.Descendants<Row>())
{
if (row == null) continue;
if (row.RowIndex.Value == Constants.ColumnNameRowIndex)
{
if (row.Descendants<Cell>().Count() > Constants.ValidColumnCount || row.Descendants<Cell>().Count() < Constants.ValidColumnCount)
{
var error = new FileErrorModel
{
ErrorText = ValidationMessages.IncompatibleCoulmns,
RowNumber = null,
ColumnNumber = null
};
fileErrorModels.Add(error);
}
}
if (row.RowIndex.Value == Constants.TableNameRowIndex ||
row.RowIndex.Value == Constants.ColumnNameRowIndex) continue;
framworkDatatable.Rows.Add();
var i = 0;
foreach (var cell in row.Descendants<Cell>())
{
framworkDatatable.Rows[framworkDatatable.Rows.Count - 1][i] = GetCellValue(document, cell);
i++;
}
}
}
return frameworkName;
}
this is how I initialize Datatable
private static void InitializeDataTable(DataTable framworkDatatable)
{
framworkDatatable.Columns.AddRange(new[] {
new DataColumn(ImportSheetColumnNames.ControlFamilyIdentifier, typeof(string)),
new DataColumn(ImportSheetColumnNames.ControlFamilyShortName, typeof(string)),
new DataColumn(ImportSheetColumnNames.ControlFamilyDescription,typeof(string)),
new DataColumn(ImportSheetColumnNames.ControlIdentifier,typeof(string)),
new DataColumn(ImportSheetColumnNames.ControlType,typeof(string)),
new DataColumn(ImportSheetColumnNames.ControlDescription,typeof(string)),
new DataColumn(ImportSheetColumnNames.TestProcedure,typeof(string)),
new DataColumn(ImportSheetColumnNames.BestPractice,typeof(string)),
new DataColumn(ImportSheetColumnNames.Help,typeof(string)),
new DataColumn(ImportSheetColumnNames.ActionPlan,typeof(string)),
new DataColumn(ImportSheetColumnNames.Question,typeof(string))
});
}
ignore "ImportSheetColumnNames", they are just constants column names
In short: (binary) floating point numbers cannot exactly represent the decimal 7.5. No reason to believe that something is wrong with your code. More details e.g. here: Why can't decimal numbers be represented exactly in binary?
What do you see if you open the sheet in Excel (not in the cell, but in the formula bar)?
If I create a new Excel spreadsheet and enter the following values in to the corresponding cells:
A1 = 7.5
A2 = 7.4999999
A3 = 7.49 (but formatted to have a single decimal point showing)
I see all three values as 7.5 in the sheet, but I see those values in the formula bar.
Now, if I look in the file (using the Open XML Productivity Tool - downloadable from Microsoft.com and essential for any Open XML work), I see the following saved in the file,under
filename
/xl/workbook.xml
/xl/worksheets/sheet1.xml
x:worksheet
x:sheetData:
:
<x:sheetData xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<x:row r="1" spans="1:1" x14ac:dyDescent="0.25" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
<x:c r="A1">
<x:v>7.5</x:v>
</x:c>
</x:row>
<x:row r="2" spans="1:1" x14ac:dyDescent="0.25" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
<x:c r="A2">
<x:v>7.4999998999999997</x:v>
</x:c>
</x:row>
<x:row r="3" spans="1:1" x14ac:dyDescent="0.25" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
<x:c r="A3" s="1">
<x:v>7.49</x:v>
</x:c>
</x:row>
</x:sheetData>
That's what's in the file (the <x:v/> element represents "value"). Excel doesn't store the "string in the cell", it stores the value, and it also stores everything in the environment that describes how to display things to the user. A value of 7.4999999 is pretty much 7.5, and is shown as 7.5 in Excel, but it's stored as 7.4999999. The 7.5 is not stored, and there's no way to recreate it unless you try to recreate all of Excel's display rules.
I have a link and i want to apply webscraping through which i can get the info of table and and then export this table into excel.Please suggest
HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
var myTable = doc.DocumentNode
.Descendants("table")
.Where(t => t.Attributes["id"].Value == someTableId)
.FirstOrDefault();
if (myTable != null)
{
///further parsing here
}
the code i m gonna use is above mentioned.As I am beginner so anyone ca tell me what to do
You may just continue iterating through table rows and cells:
if (myTable != null)
{
int iRow=0;
var tableRows = myTable
.Descendants("tr");
foreach (var tableRow in tableRows)
{
var rowCells = tableRow
.Descendants("td");
int iColumn=0;
foreach (var cell in rowCells)
{
//Save to Excel code
//Perform any checks here to ensure youre getting a valid value from the cell contents
//Excel.Cell[iRow,iColumn++]=cell.InnerText;
}
iRow++;
}
}
}
You may use any third party tool to save the values to Excel, like NPOI for binary format (up to Excel 2003) or ClosedXML if you want to use OpenXML format (Excel 2007 and above).
I am using OpenXML Spreadsheet in order to generate .xlsx file with a given template and a variable dictionary. But I have a question when updating the value in SharedStringTable since the InnerText of the SharedStringItem is read only.
My template excel file is a file with .xlsx. The variables I need to replace has prefix "$$$". For example, "$$$abc", then in the dictionary I may have <"abc", "Payson"> pair (if the dictionary does not contain the key "abc", just leave the "$$$abc" there.
What I have done is something like this
private void UpdateValue(WorkbookPart wbPart, Dictionary<string, string> dictionary)
{
var stringTablePart = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
if (stringTablePart == null)
{
stringTablePart = wbPart.AddNewPart<SharedStringTablePart>();
}
var stringTable = stringTablePart.SharedStringTable;
if (stringTable == null)
{
stringTable = new SharedStringTable();
}
//iterate through all the items in the SharedStingTable
// if there is any text starts with $$$, find out the name of the string
// look for the string in the dictionary
// replace it if found, or keep it if not.
foreach (SharedStringItem item in stringTable.Elements<SharedStringItem>())
{
if (item.InnerText.StartsWith("$$$"))
{
string variableName = item.InnerText.Substring(3);
if (!string.IsNullOrEmpty(variableName) && dictionary.containsKey(variableName))
{
// The problem is here since InnerText is read only.
item.InnerText = dictionary[variableName];
}
}
}
}
The document is here http://msdn.microsoft.com/en-us/library/documentformat.openxml.openxmlcompositeelement.innertext(v=office.14).aspx
Even the document mentioned that innertext can be set, however, there is no set accessor.
Does anyone know how to set the InnterText. Since I may have many cells with the same variable name "$$$abc", and I would like to replace all of them with "Payson".
It appears that you need to replace the InnerXml, then save the SharedStringTable. See Set text value using SharedStringTable with xml sdk in .net.
I've tried to edit the Text and it works.
Text newText = new Text();
newText.Text = dictionary[variableName];
item.Text = newText;
stringTable.Save();
I am trying to modify exisiting excel worksheet. Precisely I'd like to add a few rows to a table that exists in the worksheet (created using format as a table). I tried
var table = sheet.Tables["PositionsTable"];
but the 'table' thus created is only a meta-data of the actual table, and I can not add rows to it. If I try
sheet.Cells[table.Address.Address.ToString()].LoadFromCollection(positions);
Then I don't get the formatting of the table.
Anyone knows how I add rows to the table! Thanks
We use something like the following at our company. Basically the idea is that for each record you want to output you create an object array of the data to load into excel and then call LoadFromArrays.
using (var excelPkg = new ExcelPackage())
{
var name = "Sheet1";
//You will probably pass the columns to output into this function
var headerArray = new string[] { "Column1", "Column2" };
var data = positions
.Select(i => headerArray.Select(h => GetValue(i, h)).ToArray());
var ws = excelPkg.Workbook.Worksheets.Add(name);
ws.Cells["A1"].LoadFromArrays(
((object[])headerArray).ToSingleItemEnumerable().Union(data));
ws.Row(1).Style.Font.Bold = true; //set header to bold
excelPkg.SaveAs(stream, "password");
}
private static object GetValue(Position item, string field)
{
//Your logic goes here
return null;
}
public static IEnumerable<T> ToSingleItemEnumerable<T>(this T o)
{
yield return o;
}