Insert Excel Rows/Columns with ExcelDNA or NetOffice - c#

I am using ExcelDNA to set and get cell values. I can get and set cell values
var ref = ExcelReference(2, 2);
var val = ref.GetValue();
ref.SetValue(42);
is there a way to insert an entire row or column by moving entries to the right or down? I want the same behavior as when the user right clicks the column and all the entries are shifted to the right. Solution can use NetOffice if necessary.

I'd recommend using the COM object model for this, and the code will be similar to VBA for the same task.
You get hold of the root Application object with a call to ExcelDnaUtil.Application. The resulting object will be of type Microsoft.Office.Interop.Excel.Application and can be used to select the row of column, then call app.Selection.Insert() to insert the new row or column.
It should also be possible using the C API, but that is unlikely to be easier or faster.

I would like to add that NetOffice does not support the EntireRow and EntireColumn methods of the Range object, which would be useful for inserting or deleting full rows. As a workaround, one can replace this for rows by addressing full rows by Range(rowNoStart + ":" + rowNoEnd).
For columns, one can write Range(GetExcelColumnName(colStart) + ":" + GetExcelColumnName(colEnd)), where GetExcelColumnName is a function from this former SO post.

Related

How to put a table from excel into a scrollable list of items in C#?

I have a table in an excel sheet with two columns and about 400 rows. Very simple. All I want is to now put that data into a scrollable list on a form in winforms.
I've been at this for awhile and have become frustrated. Everything I find is showing me how to add an item to a listview one by one, I assume there's an easier way.
This list is static, it will never need to change. How would I do this?
Holding data in source code is generally not a good idea. I think, there is no such thing as 'never changes'. I understand you don't want to implement the Excel-Stuff, but you may put the data in a Json, whatever... and use it as resource.
Anyway, you can use Excel functions to prepare your data for copy-paste.
Sorry for the german Excel, I think the english name of the function would be 'Concatenate'
then you copy the cells and paste it in your Code (assuming your variable is called 'list' and it's instantinated already...)
don't forget about the extra parenthesis in the excel function for strings!
Try this:
Type ExcelType = Type.GetTypeFromProgID("Excel.Application");
dynamic Excel = Activator.CreateInstance(ExcelType);
int Index = 1;
Excel.WorkBooks.Open("Your workbooks file location here...");
do
{
listBox1.Items.Add(Excel.Range("A" + Index).Value);
Index += 1;
} while (Excel.Range("A" + Index).Value != null);
Excel.Quit();
Excel = null;

How to determine header row while using ClosedXML

i have a small winforms application im working on and using ClosedXML to handle our excel files. Im trying to build the read logic in a way that no matter what row the headers are on, i can find that row and work with the data below that. Because our reports come from our enterprise reporting system, the files are not always the same in where they start with the data because the exports from our system appends the report filters and selections to the top x rows then below that it starts the data dump. So right now that only way i can get it to work is if i manually remove all those rows at the top and make the header row the first row.
Im looking for some assistance in how i can find the "header" row based on column names or any other method. I have already looked thru their wiki https://github.com/ClosedXML/ClosedXML/wiki but that only has mention of working with printing headers and footers..
Here is where i believe i need to focus my work, but unclear where to start:
// Look for the first row used
var firstRowUsed = ws.FirstRowUsed(); //{'Precision Calculator D'!A1:XFD1}
//var firstRowUsed = "'Precision Calculator D'!A9:XFD9";
// Narrow down the row so that it only includes the used part
var udasRow = firstRowUsed.RowUsed(); //{'Precision Calculator D'!A10:A10}
//var udasRow = "'Precision Calculator D'!A10:A10}";
// Move to the next row (it now has the titles)
udasRow = udasRow.RowBelow();
There are reports ive tried that have the header starting on row 5 and others that start on row 7 and so on, so there is no actual row that they will alays be on, so need to find a way to determine it automatically. is there anyway to determine the row that the column names are in? The columns will always be in the same order, so those i have determined.
So ran across this in a mention of closedXML and it def may help get me where i need to be, but unclear how to implement
var foundMonth = ws.Search("Month", System.Globalization.CompareOptions.OrdinalIgnoreCase);
Since it returns a IEnumerable there is a chance that there may be more than one cell with the value "Month" and in my file that im testing with, there is 2 rows that contain the word and not sure how i can determine in this case that i want the last cell it found if there are multiple.
Addressed the concern about the multiple cells returned, and can now determine which row the headers are on with the following:
var foundMonth = ws.Search("Month", System.Globalization.CompareOptions.OrdinalIgnoreCase);
var monthRow = foundMonth.Last().Address.ToString();
Still unclear how to implement this into the original code post above, so that the firstRowUsed is reflected correctly in this case would be A11:XFD11
After exhausting search of ClosedXML and reading thru a number of other questions, i was able to find a solution. Below is the code that will help set the used range based on my current data structure within the file..
var foundMonth = ws.Search("Month", System.Globalization.CompareOptions.OrdinalIgnoreCase);
var monthRow = foundMonth.Last().Address; // A11
var lastcell = ws.LastCellUsed().Address; // BC3950
var rangeUsed = ws.Range(monthRow, lastcell);
Since i have no idea where my header row will be from file to file, im searching for my column header name in column A, since all the usable data is mostly numbers i can safely assume that in column A, the last found instance of the word "Month" is my header row.
With that and the last cell used i am able to determine my data range as seen above. Although i still need to figure out how to replace my firstRowUsed logic to work the same way, this is a step closer to a final solution. Ill post back my findings on that one before i mark this question answered.
var firstRowUsed = ws.Range(monthRow, lastcell).FirstRowUsed();
This line provides you the same as this line below
var firstRowUsed = ws.FirstRowUsed();
I tried this logic with 3 different files, each one having more and less data and also having the header row on different rows. and works like a charm

How to create a formula (SUBTOTAL) below table

I use EPPLus to create a excel file with a table in it. Creating the excel file and table works like a charm using:
worksheet.Cells["A1"].LoadFromCollection(rows, true, TableStyles.Light12);
...but when I try to insert a formula into the first empty row after the table, the file becomes corrupt and cannot be opened normally. Excel can recover it though, but the formula I added will never show.
I use this line of code to create the formula:
worksheet.Cells["E"+(rows.Count+2).ToString()].Formula = "=SUBTOTAL(109;[TurnoverCurrent])";
I've already confirm that I'm targeting the correct cell, and if I insert just a string instead of a formula, it works. Hence my feeling is that I don't use the formula method correctly. How can I achieve a SUBTOTAL formula after my table?
Three things jump out at me, and I'm not sure if either/or will fix it, but I thought to offer.
1) your subtotal formula is right next to the table -- when Excel renders the XML, is it trying to add this to the table and freaking out because it's self-referencing? In other words, the subtotal formula is now part of the table and adding itself?
2) when I use subtotal, I use a comma, not a semicolon
3) you didn't reference the table name in your formula. Since this is a brand new sheet, it's safe to try Table1. If you really want to be fancy, you can look up the table and find its name. I wouldn't.
So maybe add one row, prefix the table name and change your semicolon to a comma?
worksheet.Cells[rows.Count + 3, 5].Formula = "=SUBTOTAL(109,Table1[TurnoverCurrent])";
Change
worksheet.Cells["E" + (rows.Count+2).ToString()].Formula = "=SUBTOTAL(109;[TurnoverCurrent])";
to
worksheet.Cells["E" + (rows.Count+2).ToString()].Formula = "SUBTOTAL(109;[TurnoverCurrent])";
Do nut use the "="

multiple autofilters for the same EPPlus worksheet

Im creating an xlsx-file using EPPlus and want to have autofilters for all headers. I set the autofilters like this:
worksheet.Cells["A3:G" + (3 + data.Count).ToString()].AutoFilter = true;
This works smooth for just one table of content. However, I have two separate tables in the same worksheet, and when trying to set autofilters for the second one, the autofilters for the first one disappears.
Any known workarounds for this, or other suggestions?
This is a very good question!
A traditional limitation of AutoFilter is that it can be applied only once on a worksheet.
Starting with Excel 2007 you can create several Tables on a worksheet...........each with filtering capabilities.
See Pieterse's Article

How to Delete a Column Programmatically?

How does one delete a column (or multiple columns) in Excel?
eg. How to delete column C and shift the rest left?
Here is the solution to make it clearer (thanks to Leniel for the link)
Excel.Range range = (Excel.Range)sheet.get_Range("C1", Missing.Value);
range.EntireColumn.Delete(Missing.Value);
System.Runtime.InteropServices.Marshal.ReleaseComObject(range);
This was the first result I hit and deleting a column in Excel doesn't need as much code as the current answers suggest. In fact (assuming you have a Worksheet object already, listed below as mySheet) all that is needed for the original question is:
mySheet.Columns["C"].Delete();
If you want to delete multiple columns then:
mySheet.Columns["C:D"].Delete();
You can specify a variable in the Delete method (see https://learn.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel.xldeleteshiftdirection?view=excel-pia) i.e. mySheet.Columns["C"].Delete(xlShiftToLeft)but there's no need as the Delete method is smart enough to realise that the Range you are selecting is a single column, so will do this automatically.
You can also uses a numeric value to designate the column i.e. mySheet.Columns[2].Delete()
Here you find how to do it:
http://bytes.com/topic/c-sharp/answers/258110-how-do-you-delete-excel-column
http://quicktestprofessional.wordpress.com/2008/02/14/delete-columns-from-xl-sheet/

Categories