The Paradox of ViewState Events
I thought I’d write a quick blog about a somewhat “Chicken and Egg” style paradox with viewstate events, and events that cause different controls to be rendered out on the page. As you might know, ViewState events are processed between Page.OnLoad and Page.OnLoadComplete, and if the object that the event is fired upon doesn’t exist in the page control hierarchy yet, the event is lost never to be fired again. However, what if you want to add different controls to the page depending on which event was fired, and these controls themselves have events. Most of you are going, “Huh?”
Take the DataGrid control for example. When you call DataBind(), the control creates its control hierarchy. This involves creating a header row in the grid, and the header row has a bunch of cells, each containing Hyperlink objects. These Hyperlink objects have OnServerClick events and allow the grid to change the sort direction. So we know the header row must exist before OnLoadComplete happens, otherwise the Sort event would never be fired. However, this presents us with a problem where the only solution is to create the entire grid control hierarchy twice when the grid is resorted. This can be expensive if you have either a boat load of rows, or some really expensive data binding operation going on. Can it be avoided?
Well, here’s the fundamental problem. Events fire on the child controls (such as the Hyperlink itself). If the authors of DataGrid thought more carefully about performance, they might have avoided this scenario. The way I would have designed it (and I did design our Project Web Access DHTML grid in this way) would be to have the grid implement IPostBackEventHandler. The sort commands, page commands, etc would fire an event that happens on the grid object itself, not the Hyperlinks within the grid’s child control hierarchy. This way, building the control hierarchy could wait until after all viewstate events were processed, and then we knew our final page, final sort order, final everything else and could just generate grid rows once. This would also avoid the grid’s complex “event bubbling” code which is just weird (don’t get me started.)
It would be possible to modify the DataGrid to do this by first implementing IPostBackEventHandler, then overriding the rendering of the title cells. You can do this by writing your own column classes, overriding InitializeCell, and creating your own hyperlink when itemType is ListItemType.Header. The url on the hyperlink would look something like:
link.NavigateUrl = Owner.Page.ClientScript.GetPostBackClientHyperlink(Owner, "Sort " + ID);
The RaisePostBackEvent() method on the grid would look for “Sort <ColumnID>” and fire the OnSort event, etc. Will this optimization give you a noticeable perf improvement? Probably not in most situations, but it’s something to think about when writing your own custom web controls that respond to viewstate events.
Mike – Web Dev Guy