为什么我的孩子控制在事件附件时未初始化?

时间:2009-08-05 15:02:21

标签: c# asp.net events user-controls

我有一个页面和一个用户控件 - 我们称之为Detail.aspx和Selector.ascx。

假设该页面显示了数据库中各个记录的详细信息。用户控件基本上由DropDownList控件和一些关联的HTML组成。 DropDownList显示要随时切换到的其他记录的列表。

当DropDownList触发其SelectedIndexChanged事件时,我想在这种情况下使用父页面Detail.aspx来处理它。毕竟,他需要知道选择了什么,以便他可以适当地更改URL和显示的细节等。

要做到这一点,我已经做了我通常做的事情,这也是this StackOverflow question中最佳答案所说的:

public event EventHandler DropDownSelectedIndexChanged
{
    add
    {
        MyDropDownList.SelectedIndexChanged += value;
    }
    remove
    {
        MyDropDownList.SelectedIndexChanged -= value;
    }
}

上面的代码出现在Selector.ascx.cs代码隐藏文件中。

因此,在Detail.aspx上,我可以这样使用它:

<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />

到目前为止,没有什么花哨或令人惊讶的。

这是我的问题:

当浏览器访问Detail.aspx时,这会导致NullReferenceException

调试问题表明,当首次点击页面时,我上面显示的公共事件会尝试添加事件,但MyDropDownList为null,从而抛出异常。据我所知,事件是在选择器用户控件的Load事件触发之前添加(或尝试添加),因此也是在DropDownList的Load事件触发之前。

奇怪的是,如果我从Detail.aspx中省略了OnDropDownSelectedIndexChanged属性,而是将以下内容放在Detail.aspx.cs中的Page_Load事件中:

protected void Page_Load(object sender, EventArgs e)
{
    RecordSelector1.DropDownSelectedIndexChanged += new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}

它完全按预期工作。事件附加和处理得很好。没问题。

但这意味着几件坏事:

  1. 我必须记住不要使用设计器将所述事件添加到我的用户控件
  2. 我必须记住在源视图中工作时不要通过属性添加事件
  3. 最糟糕的是,作为控件的作者,我需要确保其他人使用我的控件知道1和2
  4. 那么我做错了什么?我到目前为止看到的每个例子都显示了通过用户控件公开子控件事件的类似用法。

1 个答案:

答案 0 :(得分:1)

这是有效的原因:

protected void Page_Load(object sender, EventArgs e)
{
    RecordSelector1.DropDownSelectedIndexChanged 
        += new EventHandler(RecordSelector1_DropDownSelectedIndexChanged);
}

而这不是:

<cc1:RecordSelector ID="RecordSelector1" runat="server"
OnDropDownSelectedIndexChanged="RecordSelector1_DropDownSelectedIndexChanged" />

是因为第一个在控件初始化之后添加了处理程序(通过页面的Init)。第二个示例更早地解析,因此页面在控件初始化之前尝试添加处理程序。

由于页面生命周期的性质,我认为您可能不得不在代码隐藏中添加事件处理程序。在初始化控件之前无法添加处理程序,因为在初始化之前该控件将始终为null