Insert a row in Excel using EPPlus - c#

I do not see a way to insert a row in an existing excel file using EPPlus. I am aware of the InsertRow function but this does not insert the row at the end - similar to the insert statement in sql. If this is not possible, how would I find the last used row in an excel file.

Use following code
worksheet.Dimension.End.Row
This should give last used row info.

Here is a method that finds the last cell in a table in an Excel worksheet using EPPlus.
private ExcelRange GetLastContiguousCell(ExcelRange beginCell)
{
var worksheet = beginCell.Worksheet;
var beginCellAddress = new ExcelCellAddress(beginCell.Start.Row, beginCell.Start.Column);
var lastCellAddress = worksheet.Dimension.End;
var bottomCell = worksheet.Cells[beginCellAddress.Row, beginCellAddress.Column, lastCellAddress.Row, beginCellAddress.Column]
.First(cell => cell.Offset(1, 0).Value == null);
var rightCell = worksheet.Cells[beginCellAddress.Row, beginCellAddress.Column, beginCellAddress.Row, lastCellAddress.Column]
.First(cell => cell.Offset(0, 1).Value == null);
return worksheet.Cells[bottomCell.Start.Row, rightCell.Start.Column];
}
An important note, however, is that this assumes there are no gaps in the first row and first column. I use this method for situations where the first row is for column headings (which can't be null) and the first column is a primary Id column (which also can't be null). If your situation differs from this, you will have to adapt the method, but hopefully it will still help.
Edit
It just occurred to me that you might just be able to use worksheet.Dimension.End without all the other code. I use this more complicated method because I sometimes put other information besides the table in my worksheet, and I don't want that to be included in the calculation.

Related

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

Is there a method for clearing a sheet of data in one call

I'm bringing in data from another application, but I don't know what's new and what's updated, so I'm clearing the sheet (row by row - which is time intensive) and re-adding rows. I'd like to know if there is a way to clear the sheet with one command so that I can re-add my rows.
Keep in mind, My code is in C#. I do not know cURL or how to use it.
There is no single command to delete all rows. However, you can specify a list of rows to delete.
Here is some sample code.
var ss = new SmartsheetBuilder().SetAccessToken(accessToken).Build();
var allColumns = ss.SheetResources.ColumnResources.ListColumns(sheetId, null, null);
long primaryColumnId = (long) allColumns.Data.First(c => c.Primary == true).Id;
var columnsToRead = new long[] { primaryColumnId };
Sheet sheet = ss.SheetResources.GetSheet(sheetId, null, null, null, null, columnsToRead, null, null);
var rowsToDelete = sheet.Rows.Select(r => (long) r.Id);
ss.SheetResources.RowResources.DeleteRows(sheetId, rowsToDelete, true);
Notes:
To avoid reading the entire sheet, this sample determines the id for the primary column and only reads that single column.
If your sheet is huge and you get timeout errors on the delete, you will need to chunk the deletes. (Why? Deleting a row can trigger cascading changes due to cell links, formulas, etc.) If you encounter problems, please let me know and I can update the sample.

How to delete table in Excel but preserve comments using VSTO?

I'm using the snippet below to convert an Excel range with data into a table. In some cases, I need to delete the table, but preserve comments in the cells. Is there a way I can achieve that? Also, is there a way to toggle the headers on/off? I tried the different options under XlListObjectHasHeaders: Microsoft.Office.Interop.Excel.XlYesNoGuess. but those didn't work. thanks for your help.
finalRange.Worksheet.ListObjects.AddEx(
SourceType: Microsoft.Office.Interop.Excel.XlListObjectSourceType.xlSrcRange,
Source: finalRange,
XlListObjectHasHeaders: Microsoft.Office.Interop.Excel.XlYesNoGuess.xlYes);
I delete the table structure like this -
finalRange.Worksheet.ListObjects.Item[1].Delete();
EDIT (solution for multiple tables):
foreach (var table in sheet.ListObjects)
{
Microsoft.Office.Interop.Excel.ListObject tempObj = (Microsoft.Office.Interop.Excel.ListObject)table;
Microsoft.Office.Interop.Excel.Range tempRange = tempObj.Range;
tempRange.ClearContents();
}
These both presuppose your Table variable is lo:
Excel.ListObject lo = ws.ListObjects["Table1"];
To hide the header row in an Excel table:
lo.ShowHeaders = false;
To Remove the table but retain the comments, use the range.Clear method instead of the table.Delete.
Excel.Range tableRange = lo.Range;
tableRange.ClearContents();

How do you get the name of the first page of an excel workbook?

Suppose you don't know the name of the first worksheet in an excel workbook. And you want to find a way to read from the first page. This snippet sometimes works, but not always. Is it just me? Or is there a no brainer way to do this?
MyConnection = new System.Data.OleDb.OleDbConnection("provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + inputFile + "';Extended Properties=Excel 8.0;");
String[] excelSheets = new String[tbl.Rows.Count];
int i = 0;
foreach (DataRow row in tbl.Rows)
{
excelSheets[i] = row["TABLE_NAME"].ToString();
i++;
}
string pageName = excelSheets[0];
OleDbDataAdapter myAdapter = new System.Data.OleDb.OleDbDataAdapter("SELECT * FROM [" + pageName + "]", MyConnection);
Note: I am looking for the name of the first worksheet.
If you have Office installed on the machine, why not just use Visual Studio Tools for Office (VSTO). Here is essentially the code to get the worksheet:
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook workbook = app.Workbooks.Open(fileName,otherarguments);
Microsoft.Office.Interop.Excel.Worksheet worksheet = workbook.Worksheets[1] as Microsoft.Office.Interop.Excel.Worksheet;
Your code seems to be missing the defintion of tbl. I assume it is something like
DataTable tbl = MyConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
If so, you will probably get the sheetnames but in the wrong order.
I could not find a proper solution for this issue, so I approached it from another point of view. I decided to look for sheets that actual had information on it. You can probably do this by looking at the rows, but the method I used was to look at the columns from the schema information. (This obviously will fail in your used sheet only has one column as unused sheets also have one column), but it worked in my case, and I also used it to check I had the expected number of columns (in my case nine)
This uses the GetOleDbSchemaTable(OleDbSchemaGuid.Columns, null) method to return the column information.
The code is probably irrelevant/trival, and as I happened to be learning LINQ when I came across this issue, so I wrote it in LINQ style
It does require a small class called LinqList which you can get here
DataTable columnDetails = objConn.GetOleDbSchemaTable(
System.Data.OleDb.OleDbSchemaGuid.Columns, null);
LinqList<DataRow> rows = new LinqList<DataRow>(columnDetails.Rows);
var query= (from r in rows
group r by r["Table_Name"] into results
select new { results.Key , count=results.Count() }
);
var activeSheets = (from sheet in query
where sheet.count == 9
select sheet.Key
).ToList();
if (activeSheets.Count != 1)
... display error
This is the same as this other question First sheet Excel
I think that the order of the returned table gets messed up. We would need to find a way to get the order of the tabs. For now if you check your code, sometime the first sheet is index 0. But it can be returned in any order. I have tried deleting the other sheets and with only one you get the right name. But that wouldn't be pratical.
edit : after some research, it could be the tabs are returned in order of names Using Excel OleDb to get sheet names IN SHEET ORDER
see link
SpreadsheetGear for .NET will let you load a workbook and get the names of sheets (with IWorkbook.Worksheets[sheetIndex].Name) and get the raw data or formatted text of each cell (it does more but that's probably what you are looking for if you are currently using OleDB).
You can download a free trial here.
Disclaimer: I own SpreadsheetGear LLC

Excel DateTime being returned as DBNull

I have some Excel file reading code that uses the OLEDB (Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;) which works well but I keep encountering an issue whereby certain dates are returned as DBNull.
In the original XLS document, the format of dates that work (en-GB locale) are:
"02/04/2009 17:00:00" // returned as a System.DateTime
And the following style fails:
"08/Jan/09 11:24 AM" // returned as DBNull
Excel knows they're both dates (although I can't force them to style correctly) as the following correctly shows a date:
=DATE(YEAR(c),MONTH(c),DAY(c)) // where c = cell reference.
Is there a way, without altering the auto-generated original, to get the data?
EDIT for reference, here is my read-data method (assuming a dbAdapter is set up already -- note the DBNull doesn't come from the catch which isn't fired at all):
public List<List<string>> GetData(string tableName, int maxColumns)
{
List<List<string>> rows = new List<List<string>>();
DataSet ExcelDataSet = new DataSet();
dbCommand.CommandText = #"SELECT * FROM [" + tableName + "]";
dbAdapter.Fill(ExcelDataSet);
DataTable table = ExcelDataSet.Tables[0];
foreach (DataRow row in table.Rows)
{
List<string> data = new List<string>();
for (int column = 0; column < maxColumns; column++)
{
try
{
data.Add(row[column].ToString());
}
catch (Exception)
{
data.Add(null);
}
}
// Stop processing at first blank row
if ( string.IsNullOrEmpty(data[0]) ) break;
rows.Add(data);
}
return rows;
}
I don't know if this will be helpful or not, but I have run into issues with Excel OLEDB code returning NULLs where I expected data and it almost always came back to a data type inference issue. Excel determines the datatype of a column based on the first x rows of data (I think x=10, could be wrong). I know you don't want to alter the file, but it might be worth trying to put the problem date style in the first 10 rows and see if it alters the behavior of your application.
Obviously if it does fix it, then that doesn't solve your problem. The only fixes in that case that I know of are to alter the file (put something in the first 10 rows that forces it to use the correct datatype). Sorry I can't offer a better solution, but hopefully at least I am helping you figure out what's causing your issue.

Categories