Why do subcontrols initialize before their containers?
Asked Answered
G

2

6

Despite working with WebForms for years I still find myself getting confused about the event lifecycle from time to time. This isn't so much a problem that needs resolving, as hoping to get a better understanding of why things work the way they do.

Suppose you have a form:

Default.aspx:

<form>
  <MyControls:UserControl1 runat="server">
</form>

UserControl1:ascx:

<MyControls:UserControl2 runat="server">

The OnInit events occur in this order:

UserControl2_OnInit
UserControl1_OnInit
Default_OnInit

Isn't this just bass-ackwards? Shouldn't the Init code be run in the order that controls are created? Shouldn't a parent control be able to initialize properties of a child before its OnInit runs? That is, while you can initialize properties of subcontrols in markup, there's no direct way to have a parent control be able to dynamically set properties of the child control that will be available to its OnInit event.

What I've ended up doing is stuff like this:

override void UserControl2_OnInit()
{
    NamingContainer.OnInit += new EvenHandler(UserControl1_ActualInit);
}
protected void UserControl2_ActualInit(..) {
  // do actual init code here, which will occur before OnLoad but after it's parent
  // OnInit
}

So it's not an insurmountable problem. I just don't understand why it's a problem in the first place.

I realize that perhaps you might want to be able to have all your child controls initialized in your OnInit code. So fine, you should be able to call base.OnInit first, instead of after, your own initialization code, which should cause all the child control OnInit events to get run. But the event lifecycle doesn't work that way. The Init events are not chained recursively, they seem to run independently the parent events, and the innermost one always gets run first. But seems life would be a lot easier if they were simply chained recursively so you could either call the base event (or not) before you do your thing in any given situation. Is there something I'm missing that makes this seemingly counterintuitive situation desirable or even necessary?

Greggs answered 9/2, 2011 at 13:53 Comment(3)
I agree with you since base classes are initialized before their children.Calycle
I don't have an answer to your question, but I can tell you that the guidelines are that you shouldn't rely on parent or child containers at this stage in the lifecycle. msdn.microsoft.com/en-us/library/…Lina
This comes down to what I think is a fundamental design flaw in the webforms architecture. So you aren't supposed establish relationships between parent and child controls until (I suppose) OnLoad. However, it is often important to do things before OnLoad, like recreate controls that have been dynamically created (which ms says to do in OnInit: support.microsoft.com/kb/317794 So what if you need information from the database to figure out which of those controls to create? So what if your data source is determined by a parent?Greggs
G
3

This document should be the main source of truth for your lifecycle questions.

Basically, OnInit fires after a control's internal initialization in finished. Since the page control is the first control initialized, and during it's internal initialization it initializes all sub-controls (perhaps in the order that the Designer.cs file gives), then it makes sense for the Page's OnInit event to be the last one called, since it's not finished initializing until all it's sub-controls are initialized and their OnInit events fired. Graphically, it looks like this:

Page internal init
    Sub-control1 internal init
        Sub-sub-control3 internal init
        Sub-sub-control3 init finished / OnInit fired
    Sub-control1 init finished / OnInit fired
    Sub-control2 internal init
    Sub-control2 init finished / OnInit fired
Page init finished / OnInit fired

So order of inits in this case is:

  1. Sub-sub-control3 OnInit
  2. Sub-control1 OnInit
  3. Sub-control2 OnInit
  4. Page OnInit

Load also work similarly. In general you should treat most of the events as though the control will go through it's internal process first (which includes calling the same event on all sub-controls), and then fire your custom event handling code afterwards.

Most examples you find use Page_Load specifically because that should be the last event called in that phase (and it's after post back data is loaded). It wouldn't work very well for Page_Load to be called first and risk having controls not in a fully loaded state for your custom event handling code.

Geesey answered 30/4, 2012 at 23:37 Comment(0)
K
1

The mindset for asp.net parent & child controls is:

Parents know all about their children, but children know nothing about their parent.

This mindset makes sense for re-usable server controls. Re-usability needs the custom child control making no assumptions about the page it gets used on.

The snippet you give makes me guess that your child user controls are not aimed at re-usable as such; but rather are specialized controls which you use to break down the complexities of a large & tricky UI?

In this case I would still try to work with the 'children known nothing about their parent' mindset. Think http://www.google.co.uk/search?q=gof+mediator+pattern where the parent page is the mediator between your children (the wikipedia page is good).

But your children still need to know something about the parent right, because they are doing complex UI interactions? You can address this with interfaces. Each child depends not on the parent, but on an interface that defines exactly what the children need access to. As http://en.wikipedia.org/wiki/SOLID puts it, 'depend on abstractions, not on concretions'. DO one interface per child control: 'many client specific interfaces are better than one general purpose interface'

But it all ends up looking over-engineered, doesn't it? It turns out that a componentised UI where the components must interact, just is complex, and the components may turn out big n clunky. This was, imho, one of the reason for MS web forms ajax controls losing out to jQuery &c. even before MVC came along.

Add to this that web forms ui is very hard to unit test; and your confidence in your software quality dives.

I recommend: If you can, escape to a rewrite in MVC. If you can't, consider abandoning server-side controls which do clientside behaviour, and use jQuery instead. If you can't do that, simplify simplify simplify the UI. Even if that makes it less functional. If you don't want that, then your choices are: pay the expense of engineering the UI well; or pay the expense of not engineering it well.

Kettie answered 6/7, 2012 at 9:51 Comment(5)
In truth, I do pretty much do what you say: avoid webforms. It still comes up in legacy code unfortunately - even as my q. is more than a year old. But the specifics of my question don't involve a child having any knowledge of its parent. I wanted to know why a child initializes before its parent. A parent, having full knowledge of is childs, has no opportunity to basically inject dependencies before it is created in webforms. The construct I created does involve a child referring to its parent, but only as a mechanism to let its parent have access to a constructor.Greggs
Same here. Webforms will be with us for years. Which is how I stumbled across this Q, I was searching around control lifecycle topics for help in how far I could instantiate and test a page in a unit test. I got stuck on child controls. But - isn't wanting to inject something in the child constructor a symptom of the child needing to know something about its environment?Kettie
Maybe I'm misunderstanding your point about "environment" I mean, sum(a,b) is a class that needs to know the two things that it's going to add together. But it is provided that information at construction; it doesn't ask for it from something outside itself. You can't do even that with a usercontrol, except in the form of compile-time properties passed in your markup. So if you basically mean "a user control isn't supposed to be configurable in any way by a user" then yeah... that's the way it is... but that's not a SOLID principle. It just makes them less useful.Greggs
Fair point. Maybe what really bugged me with complex user controls was the feeling that properties set in markup was the way child controls were "supposed" to work - but achieving that seemed to me unreasonably hard. Whereas hooking into events did in my experience always lead to parent&child becoming un-Solidly closely entangled.Kettie
I completely agree. The whole webforms event model was conceived in hell.Greggs

© 2022 - 2024 — McMap. All rights reserved.