为什么我的自定义用户控件的子项未初始化?

时间:2009-01-09 18:41:29

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

更新: 我一直在试验我的控件,我想我已经接近了。请继续阅读以获取最新信息。

我有2个ASP.NET 2.0用户控件,其中一个位于另一个里面。在内部控件内部,我有一个HtmlAnchor标记控件,我正在尝试从外部控件设置URL。当我尝试从呈现页面的标记设置HRef属性时,HtmlAnchor控件为null并抛出异常。

内部或外部控件的OnInit事件之前以及主页面的“set”事件之前,URL OnPreInit属性被称为。我假设这是因为我在页面上的父控件的标记中设置子控件的URL我正在呈现控件,这意味着值设置在OnInit()之前。这是我正在使用的代码的(精简版):

[ParseChildren(true)]// <- Setting this to false makes the 
                     //    page render without an exception 
                     //    but ignores anything in the markup.
[PersistChildren(false)]
public partial class OuterControl : System.Web.UI.UserControl
{
    // The private '_Inner' control is declared 
    // in the .ascx markup of the control
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public InnerControl Inner
    {
        get{ return _Inner; }
        set{ _Inner = value; }
    }
}

public partial class InnerControl : System.Web.UI.UserControl
{
    // The private 'linkHref' control is declared
    // in the .ascx markup of the control
    public string Url
    {
        get { return linkHref.HRef; }
        set { linkHref.HRef = value; }
    }
}

OuterControl在我的Default.aspx页面上使用,如下所示:

<uc1:OuterControl ID="OuterCtrl1" runat="server">
    <Inner Url="#" />
</uc1:OuterControl>

在上面的标记示例中,如果我尝试渲染此页面,则抛出异常,因为linkHref控件为null。使用我的调试器,我可以看到InnerControl中的每个控件都是null,但InnerControl和amp;访问OnInit()属性时,尚未触发OuterControl的Url事件。

更新
我认为添加“ParseChildren”和“PersistChildren”属性会有所帮助。我以前在服务器控件中使用它们但从未在用户控件中使用它们,尽管效果似乎相似。我不认为我正确地解释了这两个属性的文档,但它可以阻止异常被抛出。但页面标记会被忽略。

有没有人知道如何让这项工作。我不明白为什么这些控件在OnInit()之前设置值。当我尝试使用ASPX标记设置它们的值时,InnerControl的构造函数被调用两次。一旦根据标记(我假设)设置值,再次在OnInit()上设置(我猜测是为什么标记值会被忽略)。

这种努力是没有希望的还是我只是从错误的角度接近它?

5 个答案:

答案 0 :(得分:2)

问候丹,

我在使用嵌套控件之前遇到了类似的问题,所以我发现自己急于求助,而且我也很喜欢这个,因为我喜欢它。

我将问题重现如下: -

创建了一个名为Inner的用户控件:

Inner.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Inner.ascx.cs" Inherits="Inner" %>
<a runat="server" id="linkHref">I'm inner</a>

Inner.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Inner : System.Web.UI.UserControl
{
    public string Url
    {
        get
        {
            return this.linkHref.HRef;
        }
        set
        {
            this.linkHref.HRef = value;
        }
    }
}

创建了一个名为Outer的用户控件:

Outer.ascx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Outer.ascx.cs" Inherits="Outer" %>
<%@ Register src="Inner.ascx" tagname="Inner" tagprefix="uc1" %>
<uc1:Inner ID="_Inner" runat="server" />

Outer.ascx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Outer : System.Web.UI.UserControl
{
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public Inner Inner
    {
        get
        {
            return this._Inner;
        }
    }
}

然后创建了一个名为Default的页面:

Default.aspx的

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register src="Outer.ascx" tagname="Outer" tagprefix="uc1" %>
<%@ Reference Control="~/Inner.ascx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <uc1:Outer ID="Outer1" runat="server">
            <Inner Url="http://google.com" />
        </uc1:Outer>
    </div>
    </form>
</body>
</html>

所有这些,页面“默认”运行良好。

我在代码中发现奇怪的是这一行:

public InnerControl Inner
    {
        //...
        set{ _Inner = value; }
    }

为内部控件创建setter有什么意义?我无法理解。内部控件实例应该是从外部控件中的标记创建的,而这是创建的实例,其html锚应该被操作。 如果我将这些行添加到Outer.ascx.cs中的Inner属性

set
{
    this._Inner = (ASP.inner_ascx)value;
}

我将获得原始案例中的空引用异常。 我认为setter指示ASP.Net页面构建器创建另一个内部控件实例并使用Inner属性设置它。我不确定,但如果你有时间,你可以通过检查页面构建器生成的cs文件确切地知道这是如何发生的,它们驻留在临时的ASP.Net文件中。

答案 1 :(得分:1)

如果OuterControl.ascx看起来像

<%@ Register TagPrefix="uc1" TagName="InnerControl" Src="InnerControl.ascx" %>
<uc1:InnerControl runat="server" ID="_Inner" />

然后问题是OuterControl.Inner属性有一个set访问者。删除set访问者以解决问题:

[PersistenceMode(PersistenceMode.InnerProperty)]
public InnerControl Inner
{
    get { return _Inner; }
}

说明:如果OuterControl.Innerset访问者...

  1. 首先,ASP.NET实例化ASP.outercontrol_ascx(源自OuterControl的动态生成的类),然后实例化ASP.innercontrol_ascx的实例(动态生成的类派生自{ {1}})。
  2. 接下来,由于InnerControl属性具有Inner访问者,ASP.NET会创建set的新实例( InnerControl,正如您所料)并尝试将其ASP.innercontrol_ascx属性初始化为Url。当然,这会引发"#",因为NullReferenceExceptionlinkHref而不是ASP.innercontrol_ascx创建。
  3. 最后(如果没有抛出异常),ASP.NET将InnerControl设置为它在步骤2中创建的Inner实例。
  4. 如果InnerControl没有OuterControl.Inner访问者,那么在第2步中,ASP.NET只会将set设置为OuterCtrl1.Inner.Url,并跳过第3步。< / p>

    注意:其他几个答案表明控件是在"#"中创建或初始化的。这是不正确的;标记中声明的控件是在页面生命周期的早期创建并初始化的,Init(发生在FrameworkInitialize之前)。

答案 2 :(得分:0)

您是否在Page的OnInit方法上设置了HRef?如果是这样,请尝试将作业移至Page_Load。

从最外层到最内层的控件初始化。这意味着如果您在Page的OnInit上分配值,则控件尚未初始化。

这是关于页面生命周期的体面文档: http://www.codeproject.com/KB/aspnet/lifecycle.aspx

答案 3 :(得分:0)

你说:

// The private 'linkHref' control is declared
// in the .ascx markup of the control

如果你把它变成受保护的控件,它会改变什么吗?

答案 4 :(得分:0)

控制树是在Init()之后构建的;事实上,在反序列化视图状态之前,Init()是将控件添加到树的位置。在构建控件树之前,您无法访问linkHref。

一种可能的解决方案是存储Url,直到您可以使用它。在内部控件内,将Url属性更改为简单字符串:

public string Url { get; set; }

在内部控件的Load或PreRender事件处理程序中,将字符串传播到(现在已初始化的)linkHref对象:

linkHref.HRef = value;