Updating a cell value breaks row style. NPOI, C# - c#

Good evening, recently i was trying to update cell's value in .xls file, using NPOI library(C#), but, when i do that with cell.SetCellValue("anyvalue");,
I am able to see the changes only in some cells. Other cell are just empty.
Tried to save cell's style and re-write it using cell.CellStyle, but still the same.
Generally speaking, i get only half of the values that have to be filled in places.
Using that code, where nameAndValues[0] contains cell name, and nameAndValues[1] contains its value.
using (FileStream rstr = new FileStream(currentPath + $"/{excelName}", FileMode.Open, FileAccess.Read))
{
var workbook = new HSSFWorkbook(rstr);
var sheet = workbook.GetSheetAt(0);
using (FileStream wstr = new FileStream(currentPath + $"/{excelName}", FileMode.Open, FileAccess.Write))
{
for (int i = 0; i < values.Count; i++)
{
var cr = new CellReference(namesAndValue[i, 0]);
var row = sheet.CreateRow(cr.Row);
var cell = row.CreateCell(cr.Col);
cell.SetCellValue(namesAndValue[i, 1]);
}
workbook.Write(wstr);
wstr.Close();
}
rstr.Close();
}

When you call sheet.CreateRow(0), the first row of the sheet will be wiped out and an empty row will be inserted with no style. The same goes with row.CreateCell().
So you are calling CreateRow over and over again, making only the last value of the row survive.
I think this might be the problem.

Related

How to get the address of a cell

I'm trying to find the address of a cell in a xlsx file using c#, but i can't find a right solution for it.
using IronXL;
var workbook = IronXL.WorkBook.Load("email list.xlsx");
var sheet = workbook.WorkSheets.First();
var cells = sheet["A1:C494"];
foreach(var cell in cells)
{
Console.WriteLine(cell.Value);
//print cell adress
}
Thank you for your time

Writing a excel using closed XML - adding custom column names

I need to write a list of objects to excel sheet as a table, in which each column represents object attributes or values. To the below method, Im passing column names in a separate List and data objects in a List, I managed to get the data displayed like below, but still I could not get the columns to display properly.
I referred the below documentation, but I could not find a way to get the titles display properly.
https://github.com/closedxml/closedxml/wiki/Inserting-Tables
Code
public string CreateExcelFile<T>(IEnumerable<T> list, string sheetName, string headerTitle, List<string> titles, string fileName, string savedPath)
{
var wb = new XLWorkbook();
var ws = wb.Worksheets.Add(sheetName);
ws.Cell(1, 1).Value = headerTitle; // sets excel sheet header
var rangeTitle = ws.Range(3, 1, 3, titles.Count); // range for row 3, column 1 to row 3, column titles.Count
rangeTitle.AddToNamed("Titles");
// Need to add columns names with in rangeTitle
//rangeTitle.InsertData(titles);
// write data from row 4 onwards
if (list != null && list.Any())
{
ws.Cell(4, 1).InsertData(list);
}
else
{
ws.Cell(4, 1).Value = "No data to show";
}
// styles
var titlesStyle = wb.Style;
titlesStyle.Font.Bold = true;
titlesStyle.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
titlesStyle.Fill.BackgroundColor = XLColor.Amber;
// style titles row
wb.NamedRanges.NamedRange("Titles").Ranges.Style = titlesStyle;
ws.Columns().AdjustToContents();
var filePath = savedPath + string.Format("{0}.xlsx", fileName);
wb.SaveAs(filePath);
return filePath;
}
Output excel
Output Im trying to get - I need to get values stored in titles in the Yellow highlighted row.
Can anyone help?
You could use InsertTable. The data is inserted as an Excel Table:
ws.Cell(1, 1).InsertTable(list.AsEnumerable());
I managed to get the columns to display by doing below.
// Need to add columns names with in rangeTitle
for (int i = 0; i < titles.Count; i++)
{
var columnNumber = i + 1;
ws.Cell(3, columnNumber).Value = titles[i];
}
This works for now. But, I wonder is there a better way to doing things (without manually assigning column names like above).
ws.Cell(3, 1).Value = new [] { titles };
If you set Value to an array, ClosedXML will write each object in the array to its own row, with one property of the object per column. (See https://github.com/ClosedXML/ClosedXML/wiki/Copying-IEnumerable-Collections)
In this case, the array we're passing in has only one object – an array of titles. That inner array gets written to the target row, and each item in the inner array gets written to a column in that row.

Export on .xlsxm file

In my WinApp I export data to a specific tab of an Excel spreadsheet, in which there are macros (file extension .xlxm).
In this workbook, the data is always inserted from the same cell when it is empty, but it can happen that has already had the previous entries, so you have to retrieve the first available blank cell on the new line after the one that has already had the data inserted. The sequence of entries in the cells is similar to the following: in cells A1: A3, and then such as A10: A15, dropping the cells A4 to A9 because they are cells with formulas. I would to add that I must to control every cell of workbook for to fill in data from winApp. I hope to be able to explain the scenario.
You could use a library such as EPPlus from NuGet to achieve this. Something like this would do the trick.
static void Main(string[] args)
{
List<string> ExampleData = new List<string> { "my", "intestesting", "data" };
using (ExcelPackage package = new ExcelPackage(new FileInfo(#"C:\Temp\example.xlsm")))
{
ExcelWorksheet ws = package.Workbook.Worksheets["MySheet"];
int lastRowIndex = ws.Dimension.End.Row;
int idx = lastRowIndex + 1;
foreach (var datum in ExampleData)
{
ws.Cells[idx, 1].Value = datum;
idx++;
}
package.Save();
}
}

open xml sdk excel formula recalculate after cell removal

I have a formula cell C4 that needs to recalculate after I enter a value in another cell C2. but the C4 keeps getting cached and keeps returning old cached value.
I have asked this question multiple times on SO but I am not getting any help. I am trying every thing that I can. Here is what I found on msdn site.
With the methods from the previous code listing in place, generating
the report is now a process of getting the portfolio data and
repeatedly calling UpdateValue to create the report. Indeed, if you
add the necessary code to do this, things seem to work fine except for
one problem - any cell that contains a formula that refers to a cell
whose value was changed via Open XML manipulation does not show the
correct result. This is because Excel caches the result of a formula
within the cell. Because Excel thinks it has the correct value cached,
it does not recalculate the cell. Even if you have auto calculation
turned on or if you press F9 to force a manual recalculation, Excel
does not recalculate the cell. The solution to this is to remove the
cached value from these cells so that Excel recalculates the value as
soon as the file is opened in Excel. Add the RemoveCellValue method
shown in the following example to the PortfolioReport class to provide
this functionality.
Based on above MSDN explanation. I have tried putting the removing the code before I update the cell. After I update the cell. Before I read the formula cell, after I read the formula cell but I keep getting the following error after I read the formula cell.
System.NullReferenceException: Object reference not set to an instance
of an object.
Here is my code...
string filename = Server.MapPath("/") + "MyExcelData.xlsx";
using (SpreadsheetDocument document = SpreadsheetDocument.Open(filename, true))
{
Sheet sheet = document.WorkbookPart.Workbook.Descendants<Sheet>().SingleOrDefault(s => s.Name == "myRange1");
if (sheet == null)
{
throw new ArgumentException(
String.Format("No sheet named {0} found in spreadsheet {1}", "myRange1", filename), "sheetName");
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheet.Id);
Worksheet ws = worksheetPart.Worksheet; // ((WorksheetPart)(worksheetPart.GetPartById(sheet.Id))).Worksheet;
Cell cell = InsertCellInWorksheet(ws, "C4");
// If there is a cell value, remove it to force a recalculation
// on this cell.
if (cell.CellValue != null)
{
cell.CellValue.Remove();
}
// Save the worksheet.
ws.Save();
document.Close();
}
// getting 2 numbers in excel sheet, saving, and closing it.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(filename, true))
{
Sheet sheet = document.WorkbookPart.Workbook.Descendants<Sheet>().SingleOrDefault(s => s.Name == "myRange1");
if (sheet == null)
{
throw new ArgumentException(
String.Format("No sheet named {0} found in spreadsheet {1}", "myRange1", filename), "sheetName");
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheet.Id);
int rowIndex = int.Parse("C3".Substring(1));
Row row = worksheetPart.Worksheet.GetFirstChild<SheetData>().
Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
Cell cell3 = row.Elements<Cell>().FirstOrDefault(c => "C3".Equals(c.CellReference.Value));
if (cell3 != null)
{
cell3.CellValue = new CellValue("16");
cell3.DataType = new DocumentFormat.OpenXml.EnumValue<CellValues>(CellValues.Number);
}
worksheetPart.Worksheet.Save();
document.Close();
}
// getting the result out of excel.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(filename, false))
{
document.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
document.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
Sheet sheet = document.WorkbookPart.Workbook.Descendants<Sheet>().SingleOrDefault(s => s.Name == "myRange1");
if (sheet == null)
{
throw new ArgumentException(
String.Format("No sheet named {0} found in spreadsheet {1}", "myRange1", filename), "sheetName");
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheet.Id);
int rowIndex = int.Parse("C4".Substring(1));
Row row = worksheetPart.Worksheet.GetFirstChild<SheetData>().
Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
Cell cell = row.Elements<Cell>().FirstOrDefault(c => "C4".Equals(c.CellReference.Value));
d.Average = Convert.ToDouble(cell.CellValue.InnerText);
}
The problem seems to be that you are directly modifying an Excel data file without Excel being open. Since Excel can only track formula dependencies when its open it does not know that it needs to recalculate when you change data without Excel knowing that you have done so.
3 possible solutions are:
1) remove the calculation chain part from the file (not tested)
2) after making the changes to the file use interop/automation to open Excel and request a full calculation (or full calculation with dependency rebuild if you are also altering/creating formulas)
3) set the fullcalculationonload property to true : this should cause Excel to do a full calculation when it opens the file
I think u have deleted the cellValue of C4 ,, first u have to create the cellValue then u can perform any operation on it .

Read Excel Cell Format

I'm working on this program that will read the data in excel file and put it into our database. The program is written in Visual Studio 2010 using C#, and I'm using the NPOI library.
In the past, I was able to read the spreadsheet row by row and cell by cell to get the data, but the new format of the excel file will not allow me to do this easily. (The excel is given by another user, so I can't really make big changes to it).
There are several "tables" in one sheet (using borders and headers for each column name), and I will need to get data mainly from the tables but sometimes outside the tables too.
I was wondering if I were to read the spreadsheet row by row (which is what I'm a bit for familiar with), is there a way I can tell that I have reached a table? Is there a way I can read the "format" of the cell?
What I mean is, for example, "this cell has borders around it so starting this row is a table." or "the text in this cell is bold, so this row is the header row for this new table."
In the past I was only able to read the "text" for the spreadsheet and not the format/style. I've been searching on the internet and I can only find how to set the style for output excel but not how to read the format from input.
Any help is appreciated, thanks!
It would be better to have the various tables in your source workbook defined as named ranges with known names. Then you can get the associated area like this -
using System.IO;
using System.Windows;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
// ...
using (var file = new FileStream(workbookLocation, FileMode.Open, FileAccess.Read))
{
var workbook = new XSSFWorkbook(file);
var nameInfo = workbook.GetName("TheTable");
var tableRange = nameInfo.RefersToFormula;
// Do stuff with the table
}
If you have no control over the source spreadsheet and cannot define the tables as named ranges, you can read the cell formats as you suggest. Here is an example of reading the TopBorder style -
using (var file = new FileStream(workbookLocation, FileMode.Open, FileAccess.Read))
{
var workbook = new XSSFWorkbook(file);
var sheet = workbook.GetSheetAt(0);
for (int rowNo = 0; rowNo <= sheet.LastRowNum; rowNo++)
{
var row = sheet.GetRow(rowNo);
if (row == null) // null is when the row only contains empty cells
continue;
for (int cellNo = 0; cellNo <= row.LastCellNum; cellNo++)
{
var cell = row.GetCell(cellNo);
if (cell == null) // null is when the cell is empty
continue;
var topBorderStyle = cell.CellStyle.BorderTop;
if (topBorderStyle != BorderStyle.None)
{
MessageBox.Show(string.Format("Cell row: {0} column: {1} has TopBorder: {2}", cell.Row.RowNum, cell.ColumnIndex, topBorderStyle));
}
}
}
}

Categories

Resources