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

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();

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

Excel sheet comments in C#

I have a list that is filtered from SQL and an excel sheet that stocks them. In that list I have a column named A.
If that column exists, a comment should appear at the header of that column.
Its position might change, so I can't use this:
int commentIndex = worksheet.Comments.Add("F5");
Comment comment = worksheet.Comments[commentIndex];
comment.Note = "Hello Aspose!";
Here is my column
if (dt.Columns[i].ColumnName==A)
{
// code to be filled
}
dt=data table that has the columns from SQL.
Here is I did it and solved the issue:
int commentIndex = ws.Comments.Add(0, i);
Aspose.Cells.Comment comment = ws.Comments[commentIndex];
comment.Note = "Blalala";

Insert a row in Excel using EPPlus

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.

duplicate datatable column names

I have a datable with column names I0,I1,I2 etc. However these aren't actual column names. I store the column names in another table.
I then have a loop to map the actual column names as follows:
for (int i = 0; i < dt_key.Rows.Count; i++)
{
dt_data.Columns[i].ColumnName = dt_key.Rows[i][0].ToString();
}
I get the following errors:
A column named 'Pressure' already belongs to this DataTable.
and
A column named 'Size' already belongs to this DataTable.
Ultimately I am trying to write this to a xml file:
dt_data.WriteXml(filename);
This works but I end up the column names I0..I22
There are similar questions to this, but they are trying to make datatables with duplicate columns names. I am just trying to print out a table with duplicate column names. What is a good method to do this?
Edit:
I can do the following:
for (int i = 0; i < dt_key.Rows.Count; i++)
{
dt_data.Columns[i].ColumnName = dt_key.Rows[i][0].ToString() + " " + dt_data.Columns[i].ColumnName;
}
I think the short answer to your question is that there is no way to have duplicate column names using a DataTable. What exactly are you using this XML for? There are lots of alternate ways to generate XML that give you a lot more fine grained control.
You could manually create your file using an XmlWriter object. This method creates an XmlWriter that writes to a file.
for (int i = 0; i < dt_key.Rows.Count; i++)
{
if(dt_data.Columns.Contains(dt_key.Rows[i][0].ToString()))
dt_data.Columns[i].ColumnName = dt_key.Rows[i][0].ToString() + " " ;
else
dt_data.Columns[i].ColumnName = dt_key.Rows[i][0].ToString();
}
However adding a same column name is a bad practise,still you could achieve that by adding a space at the end of column name to make it unique.
Maybe you can make use of the DataColumn.Caption property to store the column display name:
https://msdn.microsoft.com/en-us/library/system.data.datacolumn.caption(v=vs.110).aspx
You can not duplicate Column names by using Data Table.Consider this and find another solution .
If you try to duplicate Column name then it will show an error like that you mentioned.
A column named 'Pressure' already belongs to this Data Table .
In here you try to duplicate Column name 'Pressure' that's why that error comes.

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

Categories