Garbage. Of course it can. What a stupid error message.

What it should say is, "Ancestors' control collections cannot be modified..." or "Control collections in a base server control class where a derived class uses markup cannot be modified..."

You'll see this error for a bunch of reasons to do with modifying control collections, and by and large it's probably because you're messing with someone else's collection. (Hint: don't do that. It's a bad practice. Clean up your code and don't mess around with other classes' internals. It's like reaching into someone else's trousers and adjusting things. You just don't do it. Ugh.)

Consider this scenario, though:


public class FooControl: UserControl
{
HiddenField _hiddenFoo;

protected override void CreateChildControls()
{
base.CreateChildControls();

_hiddenFoo = new HiddenField { ID = "hiddenFoo" };
Controls.Add(_hiddenFoo);
}

// Presumably we'll also actually *do* something with hiddenFoo, but
// that's neither here nor there.
}

Remarkably boring. Until, that is, someone creates a control using markup (a .ascx control) and uses FooControl as the base class. All of a sudden, the exception above is going to be thrown, and you're going to be scratching your head, wondering what on earth's going on.

The most common scenario for this is where there's post data for your hiddenFoo field, and the page's ProcessPostData method indirectly calls EnsureChildControls() on everything it needs to in order to re-populate the control tree to where it was last time and then stuff the post data into the relevant field. This can sometimes (and don't ask me about "sometimes") lead to the above exception.

Calling EnsureChildControls() in your OnInit handler won't work, as your control's events won't reliably have started firing yet. (It might work, but it won't guarantee it.) Inspecting your Controls property at a breakpoint in CreateChildControls method will give you a clue, though: your child controls that are declared in markup already exist at this point.

What you want to do instead is the following:


public class FooControl: UserControl
{
HiddenField _hiddenFoo;

protected override void AddParsedSubObject(object obj)
{
EnsureChildControls();
base.AddParsedSubObject(obj);
}

protected override void CreateChildControls()
{
base.CreateChildControls();

_hiddenFoo = new HiddenField { ID = "hiddenFoo" };
Controls.Add(_hiddenFoo);
}

// Presumably we'll also actually *do* something with hiddenFoo, but
// that's neither here nor there.
}

The AddParsedSubObject method is what's called to add controls that were parsed from markup to your control tree. As we already know that our parsed controls are happily ensconced in our control tree, all we have to do is demand that all of our other controls get loaded first.

Bear in mind that this won't work if you're doing silly things with your control collection, and it will incur a slight performance hit: AddParsedSubObject is called once per markup element (including for whitespace literals) and calling EnsureChildControls will result in a check against ChildControlsCreated, so you're going to have a lot of those hits. Still', it's better than an unhandled exception, right?