Wednesday, March 16, 2005 #

Dynamic Server Controls & INamingContainer

It turns out that creating a custom server control that uses dynamic controls (controls added at runtime) is a huge pain in the ass. Add to that INamingContainer, which is virtually a necessity when making a composite control (it lets you have more than one copy of the server control on the page by guaranteeing that the child controls have unique id's) completely screws around with control's life-cycle, and you have one frustrated developer..

It's taken me entirely too long to create my data-paging server control, which simply creates the buttons you need to page through data (First, Prev, 1, 2, 3, Next, Last - etc) and provides an event for when the user clicks a button. It doesn't do the actual data-paging, just the controls so it should have been a simple control to write. Nope, I had to try just about every trick and lame technique in the book to get it to work the way I want. Along the way I tried such things as serializing out the settings to a hidden field (essentially creating my own custom viewstate), so that I could re-instantiate my controls before the events are supposed to fire. Turns out that way doesn't work so hot if you need to hide the control for whatever reason (hidden panel, etc), since the hidden field won't get included in the page. I also tried to tap into virtually every part of the control's life-cycle, which ultimately useless, turned out to be good for learning how things worked, inserting hidden fields, or creating and re-creating controls at different points.

Finally I have something that works, though it's not without it's share of shortcuts. I ultimately borrowed some idea and code from Dennis Bauer's DynamicControlsPlaceHolder, to re-create my dynamic controls out of the viewstate on a postback. The downside to this technique is that it doesn't re-wire the events properly, and even worse, you loose command arguments (which is what I used to determine which page button the user clicked on). So I had to work around that by storing some extra info into the persistenceString (a string that is passed along that tracks which controls to restore).

So once the control is loaded, I override CreateChildControls to create the final controls the user sees. I do this by calling this.Controls.Clear() to wipe out the controls that were created by from the viewstate (which could be different depending on which page the user clicked on). It's not great, but it's much easier and probably faster than comparing the new controls that will be generated to the existing ones and adding or removing ones that don't match up.

It would be nice if there was some way to detect which button was clicked so I could only restore that control to capture the event, but I don't know if that's possible. So it now works with only instantiating the controls a maximum of twice per page load, which is an improvement over earlier versions.

All of the leaves my final workaround for INamingContainer. As mentioned above using INamingContainer causes the control life-cycle events to be called in a different order. This meant that my CreateChildControls was being called after my event was firing, the event which set the current page that the CreateChildControls relied upon. That was a problem. I could have forced a call to re-create the child controls after my event fired, but that would be adding a lot of overhead to an already inefficient system by having to re-create the controls 3 times instead of two.

So I ended up manually setting all of the control id's so that they used the parent control's ClientID. This was essentially what INamingContainer did, sans the odd life-cycle changes. It wasn't as big a deal as it could have been since I had to explicitly set the control id's for the DynamicControlsPlaceHolder code to work anyway.

It's not pretty, but it works. I can put as many pagingcontrols on a page as I want (say at the top and bottom of a repeater), even hide them and it all works.  I can't imagine having to do anything more complex with dynamic controls in a server since this one was such a pain. On the upside I learned many of the finer points of creating server controls and the next one I write should go a little more smoothly.

posted @ Wednesday, March 16, 2005 7:50 PM | Feedback (21)