I am trying to get the index of the last used row in a spreadsheet. I've found that in excel it could be done like that:
int lastUsedRow = worksheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell,
Type.Missing).Row;
But this doesn't seem to work with GemBox. The idea is that I have a template excel file that I want to fill with more information and therefore need the last row, so that I can continue on the next one.
Hi you can just use ExcelFile.Rows.Count property.
Gets the number of currently allocated elements (dynamically changes when worksheet is modified)
Try the following:
int lastUsedRow = worksheet.Rows.Count - 1;
Also regarding the shahkalpesh suggestion, yes you can also achieve your task with that approach as well, here is how:
var usedRange = worksheet.GetUsedCellRange(true);
int lastUsedRow = usedRange.LastRowIndex;
Note: I haven't used Gembox. My answer is based on searching in the documentation.
GetUsedCellRange returns a CellRange, which has a property named LastRowIndex.
Does this work the same way as Excel?
Related
I am trying to build a data import tool that accepts an EXCEL file from the user and parses the data from the file to import data into my application.
I am running across a strange issue with DeleteRow that I cannot seem to find any information online, although it seems like someone would have come across this issue before. If this is a duplicate question, I apologize, however I could not find anything related to my issue after searching the web, except for this one which still isn't solving my problem.
So the issue:
I use the following code to attempt to "remove" any row that has blank data through ExcelPackage.
for (int rowNum = 1; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var rowCells = from cell in ws.Cells
where (cell.Start.Row == rowNum)
select cell;
if (rowCells.Any(cell => cell.Value != null))
{
nonEmptyRowsInFile += 1;
continue;
}
else ws.DeleteRow(rowNum);
//Update: ws.DeleteRow(rowNum, 1, true) also does not affect dimension
}
Stepping through that code, I can see that the DeleteRow is indeed getting called for the proper row numbers, but the issue is when I go to set the "total rows in file" count on the returned result object:
parseResult.RowsFoundInFile = (ws.Dimension.End.Row);
ws.Dimension.End.Row will still return the original row count even after the calls to DeleteRow.
My question is...do I have to "save" the worksheet or call something in order for the worksheet to realize that those rows have been removed? What is the point of calling "DeleteRow" if the row still "exists"? Any insight on this would be greatly appreciated...
Thanks
I think I figured out the problem. This is yet again another closure issue in C#. The problem is that the reference to "ws" is still the same reference from before the DeleteRow call.
In order to get the "updated" dimension, you have to redeclare the worksheet, for example:
ws = excelPackage.Workbook.Worksheets.First();
Once you get a new reference to the worksheet, it will have the updated dimensions, including any removed/added rows/columns.
Hopefully this helps someone.
How do I set the source data of an excel interop chart to several entire rows?
I have a .csv file that is created by my program to display some results that are produced. For the sake of simplicity let's say these results and chart are displayed like this: (which is exactly how I want it to be)
Now the problem I am having is that the number of people is variable. So I really need to access the entire rows data.
Right now, I am doing this:
var range = worksheet.get_range("A1","D3");
xlExcel.ActiveChart.SetSourceData(range);
and this works great if you only have three Persons, but I need to access the entire row of data.
So to restate my question, how can I set the source data of my chart to several entire rows?
I tried looking here but couldn't seem to make that work with rows instead of columns.
var range = worksheet.get_range("A1").CurrentRegion;
xlExcel.ActiveChart.SetSourceData(range);
EDIT: I am assuming that the cells in the data region won't be blank.
To test this,
1) place cursor on cell A1
2) press F5
3) click on "Special"
4) choose "Current Region" as option
5) click "OK"
This will select the cells surrounding A1 which are filled, which I believe is what you are looking for.
The translation of that in VBA code points to CurrentRegion property. I think, that should work.
Check Out the option Range.EntireRow I'm not 100% on how to expand that to a single range containing 3 entire rows, but it shouldn't be that difficult to accomplish.
Another thing you can do is scan to get the actual maximum column index you need (this is assuming that there are guaranteed to be no gaps in the names), then use that index as you declare your range.
Add Code
int c = 2;//column b
while(true)
{
if (String.IsNullOrEmpty(worksheet.GetRange(1,c).Value2))
{
c--;
break;
}
c++;
}
Take a column from A to D that you're sure has no empty cells.
Do some loop to find the first empty one in that column and it will be one after the last.
Range Cell = SHeet.Range["A1"]; //or another column you're sure there's no empty data
int LineOffset = 0;
while (Cell.Offset[LineOffset, 0].Value != "") //maybe you should cast the left side to string, not sure.
{
LineOffset++;
}
int LastLine = LineOffset - 1;
Then you can get Range[Sheet.Cells[1,1], Sheet.Cells[LastLine, 4]]
Out of the box here, but why not transpose the data? Three columns for Name, Height, Weight. Convert this from an ordinary range to a Table.
When any formula, including a chart's SERIES formula references a column of a table, it always references that column, no matter how long the table gets. Add another person (another row) and the chart displays the data with the added person. Remove a few people, and the chart adjusts without leaving blanks at the end.
This is illustrated in my tutorial, Easy Dynamic Charts Using Lists or Tables.
I'm copying and inserting rows in an Excel sheet, like so:
while (rowsToAdd > 0)
{
// copy the existing row
insertionCell.EntireRow.Copy(Type.Missing);
// location of the new row
Range newRow = insertionCell.EntireRow.get_Offset(1, 0).EntireRow;
// insert the new row
newRow.Insert(XlInsertShiftDirection.xlShiftDown, Type.Missing);
rowsToAdd--;
}
The problem I have is that sometimes, I'm left with a selection marquee around the row I originally copied.
Is there a way I can cancel the selection marquee (the way you'd normally do it with the Escape key?)
In VBA it's Application.CutCopyMode = False
Adding
myExcelApplication.CutCopyMode = XlCutCopyMode.xlCopy;
seems to do the trick, though the documentation does not explain it very well, and seems to be wrong, since bools are mentioned.
I know it's pretty old, but as I had some issue copying one row to another, and as I got the proper solution on my case, I'm now glad to share it, just in case it could fix your problem (thinking about copy rows instead of inserting one).
Here is the solution:
Microsoft.Office.Interop.Excel.Range xlSourceRow;
Microsoft.Office.Interop.Excel.Range xlNewRow;
xlSourceRow.Copy(xlNewRow);
Using this solution prevents from getting any selection in your MS-Excel file.
I know how to make new columns, I know how to add a header to that column, but I am a little unsure about adding values programatically.
I am going to be looping through rows and then adding values to the new columns that I create based on some functions I perform on the Data Set. I thought a good way to start would be to just pick a row and loop through it and print out all the information in that row. If I could figure out how to do that, I could probably right all the code necessary to loop through appropriate rows and get the data I need. But I am a little confused about how to use this Range object...
Range rng = (Range)wkSheet.get_Range("A1", Type.Missing);
Lets say I wanted to print out all the Range Row information to a console? How would I loop through that Range and print out that information? Even change the information in the Range. I know how to change the value of a single row, the header, but changing multiple rows is alluding me.
object[,] values = (object[,])rng.Value;
Probably duplicate reading-from-excel
I´m trying to set a cell of type "System.Int32" in a DataSet by code, and my try looks like this:
int aCarID = 5; // as an example...
// points out the row that I want to manipulate - I guess this is what doesn´t work???
int insertIndex = myDataSet.tableCars.Rows.Count
myDataSet.tableCars.Rows[insertIndex]["CarID"] = aCarID;
What happens is: I get an exception of "System.IndexOutOfRangeException".
You´re allowed to say that I´m stupid as long as you provide an answer...
UPDATE!
Yes, I´m trying to create a new row, that´s true - that´s why I´m not using "-1".
So what´s the syntax to create a new row?
If I use tableCars.Rows.Add(...) I need to supply a "DataRow Row" to the Add-function, and I don´t have one to provide - yet! (Catch 22)
NEW UPDATE!
Ooops, found it - "NewRow()" :-)
You do realize that indices start with zero in C#? That means if your table has 3 rows, you're trying to access the 4th row because insertIndex = 3.
Try insertIndex - 1.
Edit: Since you're trying to add a new row and already found out how to do so, also don't forget to save those changes to the database (I assume that's what you want to do). The most simple way is to set the UpdateCommand-property of the DataAdapter you used to fill the DataSet (or actually the DataTable in the DataSet).
You can also have the update commands generated, using a subclass of the DbCommandBuilder.
This is a classic off-by-one: valid rows are at indices 0... Rows.Count -1
If you want to make a new row, call tableCars.AddNew() first.
From MSDN:
An IndexOutOfRangeException exception is thrown when an attempt is made to access an element of an array or collection with an index that is outside the bounds of the array or less than zero.
so the problem is when you use a wrong index as Christian said.
try to create new row first, because you want to access row which doesn't exists, or you have to insert your information into row indexed (insertIndex - 1).
Datarow indexes first position is 0, as in arrays.
You're using a strong-typed dataset, but your insert code is actually for a non-strongly typed dataset.
The following code will work for you (and is much easier!)
var insertRow = myDataSet.tableCars.NewtableCarsRow();
insertRow.CarID = aCarID;
myDataSet.AcceptChanges();
That's it!
NOTE: this code works from .NET version 3.5 onwards. For prior versions, replace the var keyword with tableCarsRow (I'm assuming that you didn't customize the default name for the datarow in the DataSet designer).