How do I get closedxml.excel to recognize merged cells? - c#

I'm trying to make a template excel file and I need to put data at various parts of the file. I have 2 fields where the data I'm importing is from a list so in the cell I do something like this:
{Item.Name}
and I of course name the range of cells that will be populated by this list. I have run into an issue where only the first record in my list will be of the correct format/ cell merge. Every record after the first completely breaks down all of my merged cells so my formatting is not good. Any ideas of how to get closedxml.excel to recognize there are merged cells?

I don't know if there is a way to get only the merged cells, but you can check if a cell is merged:
using (var excelFileStream = new FileStream("excelfile.xlsx", FileMode.Open, FileAccess.Read))
{
using IXLWorkbook workbook = new XLWorkbook(excelFileStream);
IXLWorksheet worksheet = workbook.Worksheets.Worksheet(1);
IXLCell cell = worksheet.Cell(row: 1, column: 1);
IXLRangeAddress range = cell.MergedRange().RangeAddress;
if (range.ColumnSpan > 1 || range.RowSpan > 1)
{
//merged cell
}
else
{
//non-merged cell
}
}

Related

How to load unique items of a specific excel column to a combo box using c#

I'm new to EPPlus excel library. Recently I've been trying to figure out a efficient way to load all unique items from the first column of a excel file (apart from the very first cell as it is the title) to a combo box on form load. I've tried below but it's not working (its showing the text System.Linq.Enumerable+d__64`1[System.Char] in the combo box) & also I want to dynamically find the last filled row number of the first column and not just manually add it in the code like I did i.e. 10:
var package = new ExcelPackage(new FileInfo(#"C:\Users\Tamal Banerjee\Desktop\sample.xlsx"));
ExcelWorksheet workSheet = package.Workbook.Worksheets[0];
var start = workSheet.Dimension.Start;
for (int row = start.Row; row <= 10; row++)
{
{
if (!string.IsNullOrEmpty(workSheet.Cells[row, 1].Text.ToString()))
{
comboBox1.Items.Add(workSheet.Cells[row, 1].Text.Distinct().ToString());
}
}
}
Can someone help?

How do you append data to an existing Excel file?

How do I append data to an already existing Excel file.
Let's say there can be a variable amount of rows already written to a file and I need to get the next row to write on.
I was thinking check for 2 blank rows and then write on the 2nd row or something like that.
How would I do this? Is there a way in EPPlus to open an Excel file and find the last line or something?
The Worksheet.Dimension should get you what you need. So if you have a sheet like this:
You can does this:
using (var package = new ExcelPackage(excelFile))
{
var ws = package.Workbook.Worksheets.First();
var lastRow = ws.Dimension.End.Row;
var lastColumn = ws.Dimension.End.Column;
Console.WriteLine($"Last Row: {lastRow}");
Console.WriteLine($"Last Column: {lastColumn}");
}
Which gives in console:
Last Row: 9
Last Column: 6

Find the last cell that contains data in column

I have an excel file that contains the names of the columns in the first row.
How can I find the number of the last non-empty column in the first row?
I use the library ClosedXML.Excel;
Try this:
var workbook = new XLWorkbook(fileName);
int col = workbook.Worksheet(1)
.Row(1)
.LastCellUsed()
.Address
.ColumnNumber;

How can i get actual used range for modified excels using Epplus?

I am reading data from excel to datable using EPPlus.
After reading an excel sheet with 10 rows of record, I modified the excel sheet by removing existing data and kept data for only one row.
But when I am reading the modified excel it still reading 10 rows (1 with value and remaining as null fields) to data table.
How can limit this?
I am using following code for reading Excel.
using (var pck = new OfficeOpenXml.ExcelPackage())
{
using (var stream = File.OpenRead(FilePath))
{
pck.Load(stream);
}
var ws = pck.Workbook.Worksheets.First();
bool hasHeader = true; // adjust it accordingly(this is a simple approach)
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
DSClientTransmittal.Tables[0].Columns.Add(hasHeader ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = hasHeader ? 2 : 1;
for (var rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
//var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
var wsRow = ws.Cells[rowNum, 1, rowNum, DSClientTransmittal.Tables[0].Columns.Count];
var row = DSClientTransmittal.Tables[0].NewRow();
foreach (var cell in wsRow)
{
try
{
object cellValue = cell.Value;
//row[cell.Start.Column - 1] = cell.Text;
row[cell.Start.Column - 1] = cellValue.ToString().Trim();
//cell.Style.Numberformat.Format = "#";
//row[cell.Start.Column - 1] = cell.Text;
}
catch (Exception ex) { }
}
DSClientTransmittal.Tables[0].Rows.Add(row);
}
pck.Dispose();
}
When I was using Interop excel to read excel, same issue was overcame by
clearformat() method like
ws.Columns.ClearFormats();
xlColCount = ws.UsedRange.Columns.Count;
Is there any equivalent for this in Epplus open xml?
How can I get actual used range for modified excels?
There is no built-in way of indicating that a row shouldn't be accounted for when only deleting data in some cells.
Dimension is as close as you can get, but rows are included in the Dimension if any column contains data or if any row above or below contains data.
You could however try to find out if you should skip a row in the for loop.
For example if you always delete data in the first 4 columns only, then you could try:
if(!ws.Cells[rowNum, 1, rowNum, 4].All(c => c.Value == null))
{
//Continue adding the row to the table
}
The description isn't indicating the criteria for skipping a row, but you get the idea.
To start with, I am not a C# programmer, but I think I have a solution that works using an Excel VBA script. You may be able to run this Excel VBA code with C, or get insight in how to accomplish the same thing with C+.
The problem you are having is related to the way Excel handles the working size of a worksheet. If you enter data in the 1 millionth row and then delete that cell, Excel still shows the worksheet as having 1 million rows.
I tested out this Excel VBA code and it successfully deleted all rows that were completely empty, and then reset the worksheet size.
Sub DelEmptyRowsResizeWorksheet()
Dim i As Long, iLimit As Long
iLimit = ActiveSheet.UsedRange.Rows.Count
For i = iLimit To 1 Step -1
If Application.CountA(Cells(i, 1).EntireRow) = 0 Then
Cells(i, 1).EntireRow.Delete
End If
Next i
iLimit = ActiveSheet.UsedRange.Rows.Count ' resize the worksheet based on the last row with data
End Sub
To do this manually without a script, first delete all empty rows at the bottom (or columns on the right side) of a worksheet, save it, then close and reopen the workbook. I found that this also resets the Excel workbook size.

Reading from Excel File using ClosedXML

My Excel file is not in tabular data. I am trying to read from an excel file.
I have sections within my excel file that are tabular.
I need to loop through rows 3 to 20 which are tabular and read the data.
Here is party of my code:
string fileName = "C:\\Folder1\\Prev.xlsx";
var workbook = new XLWorkbook(fileName);
var ws1 = workbook.Worksheet(1);
How do I loop through rows 3 to 20 and read columns 3,4, 6, 7, 8?
Also if a row is empty, how do I determine that so I can skip over it without reading that each column has a value for a given row.
To access a row:
var row = ws1.Row(3);
To check if the row is empty:
bool empty = row.IsEmpty();
To access a cell (column) in a row:
var cell = row.Cell(3);
To get the value from a cell:
object value = cell.Value;
// or
string value = cell.GetValue<string>();
For more information see the documentation.
Here's my jam.
var rows = worksheet.RangeUsed().RowsUsed().Skip(1); // Skip header row
foreach (var row in rows)
{
var rowNumber = row.RowNumber();
// Process the row
}
If you just use .RowsUsed(), your range will contain a huge number of columns. Way more than are actually filled in!
So use .RangeUsed() first to limit the range. This will help you process the file faster!
You can also use .Skip(1) to skip over the column header row (if you have one).
I'm not sure if this solution will solve OP's problem but I prefer using RowsUsed method. It can be used to get the list of only those rows which are non-empty or has been edited by the user. This way I can avoid making emptiness check while processing each row.
Below code snippet can process 3rd to 20th row numbers out of all the non-empty rows. I've filtered the empty rows before starting the foreach loop. Please bear in mind that filtering the non-empty rows before starting to process the rows can affect the total count of rows which will get processed. So you need to be careful while applying any logic which is based on the total number of rows processed inside foreach loop.
string fileName = "C:\\Folder1\\Prev.xlsx";
using (var excelWorkbook = new XLWorkbook(fileName))
{
var nonEmptyDataRows = excelWorkbook.Worksheet(1).RowsUsed();
foreach (var dataRow in nonEmptyDataRows)
{
//for row number check
if(dataRow.RowNumber() >=3 && dataRow.RowNumber() <= 20)
{
//to get column # 3's data
var cell = dataRow.Cell(3).Value;
}
}
}
RowsUsed method is helpful in commonly faced problems which require processing the rows of an excel sheet.
It works easily
XLWorkbook workbook = new XLWorkbook(FilePath);
var rowCount = workbook.Worksheet(1).LastRowUsed().RowNumber();
var columnCount = workbook.Worksheet(1).LastColumnUsed().ColumnNumber();
int column = 1;
int row = 1;
List<string> ll = new List<string>();
while (row <= rowCount)
{
while (column <= columnCount)
{
string title = workbook.Worksheets.Worksheet(1).Cell(row, column).GetString();
ll.Add(title);
column++;
}
row++;
column = 1;
}

Categories