更新: 我一直在试验我的控件,我想我已经接近了。请继续阅读以获取最新信息。
我有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()
上设置(我猜测是为什么标记值会被忽略)。
这种努力是没有希望的还是我只是从错误的角度接近它?
答案 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.Inner
有set
访问者...
ASP.outercontrol_ascx
(源自OuterControl
的动态生成的类),然后实例化ASP.innercontrol_ascx
的实例(动态生成的类派生自{ {1}})。InnerControl
属性具有Inner
访问者,ASP.NET会创建set
的新实例(不 InnerControl
,正如您所料)并尝试将其ASP.innercontrol_ascx
属性初始化为Url
。当然,这会引发"#"
,因为NullReferenceException
由linkHref
而不是ASP.innercontrol_ascx
创建。InnerControl
设置为它在步骤2中创建的Inner
实例。如果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;