July 30, 2008
@ 09:33 PM

ASP.NET control collections are finicky. They are the most difficult to handle when trying to modify a control collection from a scope outside of that control. The mischievous error sneaked up on me some time ago. It manifests itself with the following error:

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

While fiddling around for a friend I came across some interesting behavior. Perhaps it is best to just show the code and disclaim my ignorance.

Suppose I have an ASP.NET 2.0 application containing two user controls implementing an interface IReadyable.

public interface IReadyable
{
  event EventHandler Ready;
}

Each user control contains handling code to satisfy the interface, like so:

public event EventHandler Ready;

protected override void OnLoad(EventArgs e)
{
  this.OnReady(EventArgs.Empty);
}

private void OnReady(EventArgs e)
{
  if (Ready != null)
  {
    this.Ready(this, e);
  }
}

The idea is that we can use the Ready event to signal a time to swap user controls in and out of our page. Allright, now to set up the straw man. We have a page that contains a placeholder. At some point, one of our user controls is added to the control collection of the placeholder. When the OnLoad event of the control fires, the handler activates the Ready event as well.

Our application code responds to the ready event by swapping the user control for another. This causes the peculiar exception.

/// <summary>
/// This method wires up the event handlers early in the page lifecycle,
/// but not early enough to allow us to modify some control collections.
/// </summary>
protected void Page_Load(object sender, EventArgs e)
{
  base.OnInit(e);
  IReadyable readyable = (IReadyable)this.LoadControl("~/MailControl01.ascx");
  readyable.Ready += new EventHandler(readyable_Ready);
  this.placeHolder.Controls.Add((Control)readyable);
}

/// <summary>
/// This method does not work because it is invoked too late in the page lifecycle.
/// </summary>
void readyable_Ready(object sender, EventArgs e)
{
  this.placeHolder.Controls.Clear(); // <-- An exception is thrown here.
  IReadyable readyable = (IReadyable)this.LoadControl("~/MailControl02.ascx");
  readyable.Ready += new EventHandler(again_Ready);
  this.placeHolder.Controls.Add((Control)readyable);
}

/// <summary>
/// Stub for an event handler.
/// </summary>
void again_Ready(object sender, EventArgs e)
{

}

Notice the first line in the readyable_Ready method?

Now for the interesting behavior. I say interesting because the error states that control collections are unmodifyable during the Init and Load phase; however, if you swap Page_Load for an override on the page's OnInit method, and swap the user control's OnLoad override for an OnInit override, no exception occurs. Everything works just fine. I suppose there are some arcane rules I am unaware of.

Granted, in my day-to-day development, I avoid the entire issue by creating custom server controls. I make each control responsible for its own control collection and ignorant of anything outside. It seems a control can modify its own collection any time it pleases, although there are some costs to doing it late in the page lifecycle. I found this page from Microsoft helpful: http://msdn.microsoft.com/en-us/library/ms178472.aspx.

I hope this turns out helpful for someone. A full demo is attached here: ControlCollections.zip (6.21 KB)

Happy coding.


 
Categories: ASP.NET | C#

July 25, 2008
@ 08:19 PM

Well, I took the plunge. The truth is, that my home-brewed blog engine was too buggy to be practical. I found myself spending a few hours massaging code, and then left with no time to write. Certainly, I want nothing to do with "More Code than Content" syndrome.

My only regret is that permalinks are fatally broken. I suppose a tool such as ISAPI Rewrite could bail me out here; but given time constraints and so few blog entries, I'm settling for burning the ship.

So here goes. Comments are enabled for a number of days. If you see something that peaks your interest, join in the conversation.


 
Categories: Misc