5

Edit: look at the bottom for a halfway reason why this is happening

I've a very strange IndexOutOfRangeException (as said in title). It happens when I use foreach to iterate over the controls of a control (recursive FindControl).

strange error 1

I then thought to add an additional check that makes sure root.Controls.Count > 0. However I keep getting the exception, while the debugger clearly says Count == 0.

strange error 2

The root in question is a FormView. If anyone has any ideas why a simple property check throws an IndexOutOfRangeException, please enlighten me!

Exception stackTrace (yes it is complete):

   at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)

Code:

public static Control FindControlRecursive(this Control root, string id)
{
    if (root.ID == id)
    {
        return root;
    }

    if (root.Controls.Count > 0)
    {
        foreach (Control c in root.Controls)
        {
            Control t = c.FindControlRecursive(id);
            if (t != null)
            {
                return t;
            }
        }
    }

    return null;
}

EDIT:

I've tried to use the native FindControl function, this throws the same identical error.


Specific question:

How can foreach throw an IndexOutOfRangeException on a native collection.


EDIT2:

Somehow this seems related to using a ObjectDataSource, I wasn't filling in the inputparameters correctly, but I wasn't getting any errors there. Perhaps this corrupted the FormView (that was using that datasource) somehow. I'm still interested to know how this can happen without errors thrown before accessing the childcontrols.

Destrictor
  • 752
  • 1
  • 4
  • 15
  • It would be better to use `Control.FindControl` if you know the `NamingContainer` instead of a recursive method because: 1. it's faster 2. it's more legible 3. it's less error-prone: a control's ID just need to be unique inside of it's NamingContainer, hence you might get the wrong control with the recursive method(consider a GridView with it's rows and a Control inside of a TemplateField) 4. you learn more how an ASP.NET page works and controls are linked – Tim Schmelter Jan 18 '13 at 10:10
  • @TimSchmelter I've added that Control.FindControl throws the same error. Also I use several servercontrols and usercontrols which need to be searched as well. – Destrictor Jan 18 '13 at 10:13
  • So `FormView1.FindControl("xy")` also throws the exception? – Tim Schmelter Jan 18 '13 at 10:17
  • @TimSchmelter It throws the error on `fvDetail.FindControl("ddlSoortRekening") as DropDownList;` yes. – Destrictor Jan 18 '13 at 10:18
  • 1
    By the way, the `Control.Controls` property is not just a getter. It also reloads the `ViewState` of all child controls. That might cause your issue. You might want to have a look at my answer on a related issue: http://stackoverflow.com/questions/5276573/asp-net-findcontrol-recursively Is something dynamic in your page? – Tim Schmelter Jan 18 '13 at 10:20
  • @TimSchmelter There are no controls added dynamically to the page. (except the main menu, but that's added to every page on the site and it works everywhere else, also note I use FindControl[Recursive] on many pages and I've not had this error anywhere before.) Also do you have anywhere to refer that Control.Controls reloads the ViewState of child controls? – Destrictor Jan 18 '13 at 10:38
  • In your first screenshot, there is a "_controls = null" for that root (which I guess is the backing field for the Controls property) - I suspect that's part of the reason. The HasControls method will return false if that backing field is null. – Hans Kesting Jan 18 '13 at 12:55
  • @HansKesting then why can visual studio show me the Controls object without problems? also note Count == 0, while accessing Count still gave the exception. Also note that any control without childcontrols gives `_controls == null`. – Destrictor Jan 18 '13 at 13:11
  • @Destrictor: Have you looked at the link i've provided. My answer and the (later) answer of the OP(`HasControls` instead) could shed some light on it. You might also want to have a look at these links: http://msdn.microsoft.com/en-us/library/ms972976.aspx#viewstate_topic4 and http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx – Tim Schmelter Jan 18 '13 at 13:14
  • @TimSchmelter As I've edited in later, the error had pretty much nothing to do with the viewstate, but somehow showed an error that had to do with not setting an ObjectDataSource inputparameter correctly. For some reason the error didn't manifest until trying to access the childcontrols of the formview. _sidenote: I did read the links._ – Destrictor Jan 18 '13 at 13:27
  • @Destrictor: Sorry, i didn't have the time to have a look your edits. I've just answered _"do you have anywhere to refer that Control.Controls reloads the ViewState of child controls?"_ – Tim Schmelter Jan 18 '13 at 13:29

2 Answers2

3

Rather than Controls.Count, try using Control.HasControls(), might get around the issue.

Tanner
  • 22,205
  • 9
  • 65
  • 83
  • I will mark this as an answer later if there's no one to explain how bad parameters could throw an error in totally unrelated code, since it allowed me to find the actual error. – Destrictor Jan 18 '13 at 11:24
  • Only thing I thought of was that a label/textbox wouldn't have any child controls and .net simply doesn't handle it gracefully. Did it work for some controls, if so can you check what specific control types it fails for? – Tanner Jan 18 '13 at 13:20
  • It's worked for pretty much any control before, it failed in this occasion as a formview with databinding, and the datasource had an invalid inputparameter. – Destrictor Jan 18 '13 at 13:30
0

Specific question: How can foreach throw an IndexOutOfRangeException on a native collection

It isn't related to foreach; the exception just occurs while accessing the Controls property of the control. That's why it also happens when you try to get root.Controls.Count, because Count is a property of the Controls collection, and thus you are trying to first access the collection before asking it for its Count.

You need to check on which control you are calling this function and at what moment. Maybe the control is loading from corrupted resources, or you are trying to access its control collection before it has properly loaded, or something like that. Hard to say without seeing more of your code.

Try to use this function on a plain vanilla form, and make sure you call it after it has loaded properly and see if it works.