为什么子控制在容器之前初始化?

时间:2011-02-09 13:53:36

标签: asp.net webforms page-lifecycle

尽管多年来使用WebForms,我仍然不时会对事件生命周期感到困惑。这不是一个需要解决的问题,因为希望能够更好地理解为什么事情按照他们的方式运作。

假设您有一个表单:

Default.aspx的:

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

的UserControl1:ASCX:

<MyControls:UserControl2 runat="server">

OnInit事件按此顺序发生:

UserControl2_OnInit
UserControl1_OnInit
Default_OnInit

这不仅仅是低音吗? Init代码不应该按照创建控件的顺序运行吗?在OnInit运行之前,父控件是否不能初始化子属性?也就是说,虽然您可以在标记中初始化子控件的属性,但是没有直接的方法让父控件能够动态设置可用于其OnInit事件的子控件的属性。

我最终做的是这样的事情:

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
}

所以这不是一个不可逾越的问题。我只是不明白为什么它首先是一个问题。

我意识到也许您可能希望能够在OnInit代码中初始化所有子控件。很好,你应该能够首先调用base.OnInit,而不是之后,你自己的初始化代码,这应该导致所有的子控件OnInit事件运行。但事件生命周期不起作用。 Init事件不是递归链接的,它们似乎独立地运行父事件,而最里面的事件总是首先运行。但是,如果它们简单地以链接方式链接,那么生活会变得容易得多,所以你可以在任何特定情况下执行你的事情之前调用基本事件(或不调用)。是否有一些我错过的东西使这种看似违反直觉的情况变得可取或甚至是必要的?

2 个答案:

答案 0 :(得分:2)

This document应该是您生命周期问题的主要真实来源。

基本上,OnInit在完成控件的内部初始化后触发。由于页面控件是初始化的第一个控件,并且在它的内部初始化期间它初始化所有子控件(可能按照Designer.cs文件给出的顺序),然​​后将Page的OnInit事件作为最后一个调用是有意义的,因为它没有完成初始化,直到所有它的子控件被初始化并且它们的OnInit事件被触发。从图形上看,它看起来像这样:

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

在这种情况下,inits的顺序是:

  1. Sub-sub-control3 OnInit
  2. Sub-control1 OnInit
  3. Sub-control2 OnInit
  4. Page OnInit
  5. 加载也类似。通常,您应该将大多数事件视为控件首先执行内部过程(包括在所有子控件上调用相同的事件),然后再触发自定义事件处理代码。

    您发现的大多数示例都特别使用Page_Load,因为它应该是该阶段中调用的最后一个事件(并且在加载回发数据之后)。首先调用Page_Load并且使控件没有处于完全加载状态的自定义事件处理代码的风险是不行的。

答案 1 :(得分:1)

asp.net父母的心态&amp;儿童控制是:

父母都知道他们的孩子,但孩子们对他们的父母一无所知。

这种思维方式对于可重复使用的服务器控件很有意义。可重用性需要自定义子控件不对其使用的页面进行任何假设。

您提供的代码段让我猜测您的孩子用户控件并非旨在可重复使用;而是用于打破大型和复杂性的复杂性的专用控件。棘手的UI?

在这种情况下,我会仍然尝试使用“对父母一无所知的孩子”。想想http://www.google.co.uk/search?q=gof+mediator+pattern,其中父页是您孩子之间的中介(维基百科页面很好)。

但是你的孩子仍然需要了解父母的权利,因为他们正在进行复杂的UI互动?您可以使用接口解决此问题。每个孩子不依赖于父级,而是依赖于定义孩子需要访问权限的接口。正如http://en.wikipedia.org/wiki/SOLID所说,'取决于抽象,而不是结核'。每个子控件都有一个接口:'许多客户端特定接口优于一个通用接口'

但最终看起来过度设计,不是吗?事实证明,组件必须进行交互的组件化UI,只是 复杂,并且组件可能变得非常笨重。这是,imho,MS web表单ajax控件输给jQuery&amp; c的原因之一。甚至之前 MVC出现。

除此之外,网络表单ui很难进行单元测试;以及您对软件质量的信心。

我建议: 如果可以,请转到MVC中的重写。 如果不能,请考虑放弃执行客户端行为的服务器端控件,而是使用jQuery。 如果您不能这样做,请简化简化以简化UI。即使这会降低它的功能。 如果您不想这样做,那么您的选择是:支付设计UI的费用;或支付不做好工程设计的费用。