OutOfBounds Exception on iOSMobileTable.RowCellData

I’m getting an out of bounds exception from RowCellData(row) when looping through the rows of a table, always on the last row. RowCount returns the correct number of rows and the loop is from 0 to RowCount-1, so I shouldn’t be getting the error.

Can you show us more code…

1 Like

Just out of curiosity… are you putting RowCount-1 into a variable and then using that variable in a loop? If so, you could be encountering a race condition where a row is being removed before the loop completes.

I am putting RowCount-1 into a variable, but i am not adding/removing/altering the rows in any way.

In the loop I am checking to see if the row is selected (using the checkmark accessory) and if so then retrieving the RowCellData Tag to do something with that. This all takes place in the ToollbarButtonPressed event, and once the loop finishes, then it calls the Close method.

It all works as it should unless the last row (regardless of how many rows are in the table) is selected, then I get the exception on that row.

Is “row” the variable you’re setting with “RowCount-1” or is row from somewhere else?

Curious… could this happen if you pressed the toolbar button twice?

I don’t see how. I’ve put breakpoints in the event and it only executes once and then the screen closes. It doesn’t make sense to me.

@Mark_Sweeney I think I call the actual variable something else, but I’ve stepped through the loop in the debugger and the correct values are being used for the row parameter.

I have been messing with the code trying to isolate what is actually going wrong, and I still don’t have a handle on it. Here’s the current code in the ToolbarButtonPressed event:

Var i As Integer
Var lastRowIndex As Integer = WorkItemDefList.RowCount - 1
Var selected As Boolean
Var theCell As MobileTableCellData
Var theDef As WorkItemDef
Var theItem As WorkItem

For i = 0 To lastRowIndex
  theCell = WorkItemDefList.RowCellData(0, i)
  selected = theCell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark
  selected = WorkItemDefList.RowCellData(0, i).AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark
Next

For i = 0 To lastRowIndex
  theCell = WorkItemDefList.RowCellData(0, i)
  selected = theCell.AccessoryType = MobileTableCellData.AccessoryTypes.Checkmark
  If selected Then
    theDef = theCell.Tag
    theItem = New WorkItem(theDef, App.CurrProperty.ID, theRoom.ID, App.CurrProperty.Quality)
    Call App.DB.WorkItemInsert(theItem)
  End
Next

The first loop is just a test loop to see if I can get all the RowCellData objects without the exception, and it works without issue. The second loop is the real loop and the exception is raised where I am assigning a value to theCell.

Interestingly, in this iteration of the code, I get the exception on the second to last row instead of the last row like before. @Greg_O this makes me think of what you mentioned earlier about a race condition - its like the table rows are being removed before I can get to them. But this shouldn’t be happening, right?

The window closes before the loop ends…

I don’t think the screen is closing because I have removed the calls to the Close method (which weren’t executed until after the loop ended). And as far as I know, there is no implicit Close when a toolbar button is pressed.

It shouldn’t, but i would still try changing that loop to use WorkItemDefList.RowCount - 1 instead of your lastRowIndex variable and see if it fixes the problem. That would at least prove or disprove the theory and help define your debugging path.

Curious though… does this line:

Call App.DB.WorkItemInsert(theItem)

Result in any callbacks that modify that table?

No callbacks - it simply inserts a new record in a DB table. However, that DB table is used to get the RowCount for the list’s DataSource. Adding a new record to the DB table would reduce the number of rows in the list if it were to be refreshed. I suppose the list could get refreshed in the middle of my loops? I didnt think that would happen since I’m not asking for it - unless trying to access the RowCellData results in a call to the RowCount method.

If that’s really the case then I can store the list tag objects in a local array and do what I need to do with them instead of trying to pull them from the table.

did you try changing the loop the way I suggested?

Not yet - I’ll give it a go in a little bit.

A better solution could be use an array (dictionary, class or whatever) with each data you want to show in the cell and the relative tag (info to use)
Fill the array from the db
Fill the table from this array
When you select an item set a variable from the event to save the selected tag (and reload the mobile table to give feedback to the user with the check mark)
Using the toolbar button you will have the selected tag without any problem

I use extensively this method

Morale:
The iOSMobileTable is only a view, the shows your data, don’t use it as a datasource

@Greg_O In the loops, I replaced For i = 0 To lastRowIndex with For i = 0 To WorkItemDefList.RowCount and I still get the exception.

So then I placed a break point in the RowCount method for the DataSource and it gets called every time I access RowCellData. I am guessing RowCellData does some bounds checking with the row parameter.

So then as I add new items to the database table, the RowCount method (which gets it’s count from the database table) returns fewer rows because this list shows items that are NOT in the table already. So at some point in my loop the RowCount returns fewer rows than there are actual rows in the list.

So the solution is what @Antonio_Rinaldi suggested, which is to keep a local array of the data and point the DataSource to that array and not the database.

Thank you everyone for helping me think my way through this. I always appreciate the help I get here on the forum. Hopefully one day I’ll know enough to help some of you. Or at least be fast enough to get an answer out before someone else does :slight_smile:

That should be

For i = 0 To WorkItemDefList.RowCount - 1

I noticed quite early in iOS development that RowCellData.tag will call into the datasource and initialize a new cell each time.
To prevent this I add a RowCellDataTag method to the datasource class that will only return the tag.

1 Like

Indeed. I typed it out in the comment instead of copying it from my code. It’s correct in my code.