Fixing Errors in ASP.NET Server Controls, Plus that Big One

September 1st, 2006

While doing work with a Server Control in ASP.NET 2.0 you may get this error. It is maddening because it is really hard to figure it out if you have not fixed the problem before.

The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.

I just worked through a bunch of errors like this one and I am starting to develop a vague understanding of what to do about it. I have learned to avoid it by not adding any instances of controls to the Controls collection before I was done setting properties and adding child controls to them. For example, if you are assembling a hierarchy of controls to build a Table you will follow this sequence:

  • Create instance of a Table control
  • Create an instance of TableRow
  • Create one or more instances of TableCell
  • Create and add controls to your TableCell Controls collection
  • Add those TableCell instances to the TableRow Cells collection
  • Add the TableCell to the Table Rows collection

Now that you have already added the TableCell instances to the Rows collection you cannot add more controls to it at this point. That would result in the above error. Internally the ASP.NET engine must be firing the databinding or other events listed in the error message. Fair enough, I can carefully add things in the right order.

Now my next puzzle. I am still not fully understanding when I should create instances. I have the declarations at the top and sometimes they are null and sometimes they are not. I have tried to make them all null and initialize them in the CreateChildControls method, but the machine does not like that and I was able to cause Visual Studio to crash reliably when I did so. I have more experimenting to do here to decide how I will handle variables in future controls. What it appears I will have to do is call EnsureChildControls() about 100 more times in the control to do it properly, but I do not exactly like that approach.

For the CompositeControl you always call Controls.Clear() at the start of the CreateChildControls method. This essentially resets the hierarchy of controls and seems logical to me. Given that logic, I was working with an instance of Table called mainTable and I had initialized it with the class and each time CreateChildControls I would call mainTable.Rows.Clear() to achieve the same effect. It was working initially but I did come across odd behavior later, such as the above error or null reference exceptions I could not explain. So now instead of calling mainTable.Rows.Clear() I simply set mainTable to a new instance of Table. Now I do not experience any of the odd behavior.

Once I got past these errors I attempted to add a few RequiredFieldValidation controls to the server control. I thought I was smart and had all of the answers to do this without any problems. I did browse a few comments people have posted online about their problems with adding validation controls to custom server controls but did not think I would have the same trouble. It appears I did. But with a bit of debugging and hacking I came up with a solution.

For starters I made the control implement IValidator and created the Validate function and IsValid property as a part of the interface. That just gave me a good place to put the code. I was baffled at why the control was not working. I have code similar to the following:

string validationGroup = this.UniqueID;
tbName.ID = "tbName";
tbName.ValidationGroup = validationGroup;

rfvName= new RequiredFieldValidator();
rfv.ValidationGroup = validationGroup;
rfv.ControlToValidate = tbName.ID;
rfv.Display = ValidatorDisplay.Static;

This control has a button on it and each button has a property named CausesValidation which is set to true by default. But when I would click it, the Validate routine for this RequiredFieldValidator would not fire. I got stuck on that problem for a while.

I considered what value I supposed to use for the ControlToValidate property. I could try ID or ClientID but the example code I found which did work was just using ID, so I did have it right. I could not explain it until I realized I had this problem before and it has nothing to do with custom server controls. It is all about the ValidationGroup property. Each TextBox and RequiredFieldValidator had it set, but the Button did not. I set the ValidationGroup on the Button to the same value as the rest and the validators all start to fire, as if by magic. It can be annoying how software does what you tell it and not what you mean.

My control is now working as I want. It has great design-time support including editable templates, a smart tag and lots of properties which cause the design-time display to change immediately. Despite a few hiccups, it was not all that bad.

[ Web Development Tools for the Power Developer ]

One Response to “Fixing Errors in ASP.NET Server Controls, Plus that Big One”

  1. Brennan’s Blog » Blog Archive » Debugging ASP.NET 2.0 with Visual Studio 2005 Says:

    [...] Fixing Errors in ASP.NET Server Controls, Plus that Big One [...]