Asp.net用户控件LoadControl问题

15

当我使用LoadControl( type, Params )时,出现了问题。让我解释一下...

我有一个超级简单的用户控件(ascx)。

<%@ Control Language="C#" AutoEventWireup="True" Inherits="ErrorDisplay" Codebehind="ErrorDisplay.ascx.cs" EnableViewState="false" %>

<asp:Label runat="server" ID="lblTitle" />
<asp:Label runat="server" ID="lblDescription" />

使用 C# 编写后台代码:

public partial class ErrorDisplay : System.Web.UI.UserControl
{

    private Message _ErrorMessage;    

    public ErrorDisplay()
    {
    }

    public ErrorDisplay(Message ErrorMessage)
    {
        _ErrorMessage = ErrorMessage;
    }    

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);

        if (_ErrorMessage != null)
        {
            lblTitle.Text = _ErrorMessage.Message;
            lblDescription.Text = _ErrorMessage.Description;
        }
    }
}

在我的 web 应用程序的其他地方,我使用以下代码将用户控件的实例添加到页面中:

divValidationIssues.Controls.Add(LoadControl(typeof(ErrorDisplay), new object[] { MessageDetails }));   

我正在使用LoadControl的重载版本,因为我想将Message参数传递给构造函数。所有这些看起来都很好。

但是,当ErrorDisplay用户控件上的OnPreRender()被触发时,lblTitle和lblDescription变量都为null,尽管它们具有标记等效物。message变量已经正确填充。

是否有人可以解释为什么会发生这种情况?

谢谢

编辑:

只是为了澄清,我还要补充一下,以编程方式向页面添加用户控件的代码是响应按钮按下事件而运行的,因此“hosting page”已经经历了Init、Page_Load,并且现在正在处理事件处理程序。

我无法在更早的asp生命周期阶段添加用户控件,因为它们是响应按钮点击事件而创建的。

6个回答

23

我也尝试了以下代码- 结果相同(即lblTitle和lblDescription都为null)

protected void Page_Load(object sender, EventArgs e)
{
    if (_ErrorMessage != null)
    {
        lblTitle.Text = _ErrorMessage.Message;
        lblDescription.Text = _ErrorMessage.Description;
    }
}

我曾认为LoadControl函数将其正在加载的控件提升到它被包含在其中的页面的当前“状态”上。因此,Init、Page_Load等都是作为LoadControl调用的一部分运行的。
有趣的是,这个(未回答的)asp.net论坛帖子展示了与我经历的相同的问题。 MSDN论坛帖子 此外 - 来自MSDN:
当您将控件加载到容器控件中时,容器会引发所有已添加控件的事件,直到它赶上当前事件。但是,添加的控件不会赶上PostBack数据处理。为了使添加的控件参与PostBack数据处理(包括验证),必须在Init事件中而不是在Load事件中添加控件。
因此,LoadControl不应该正确初始化控件吗?
编辑:
好吧,我在这里回答自己的问题。。
我发现了我链接到的论坛帖子的已回答版本Here 基本上,答案是LoadControl( type, params )无法推断出要解析的前端ascx,因此它不会费力地初始化任何控件。当您使用LoadControl( "ascx path" )版本时,它会给出前端页面,并因此执行所有解析和初始化。

总结一下,我需要改变初始化控件的代码,并将其分成单独的部分。即:

Control ErrorCntrl = LoadControl("ErrorDisplay.ascx");
ErrorCntrl.ID = SomeID;
(ErrorCntrl as ErrorDisplay).SetErrorMessage = MessageDetail;
divErrorContainer.Controls.Add(ErrorCntrl);

希望它能够正常工作...虽然不如我之前的尝试那么精简,但至少可以正常运行。

我仍然乐于接受改进建议。

干杯!


3
谢谢你解决我的问题,我给你点赞和感谢的留言。 :) - Brant Bobby

0
我在日历控件DayRender事件中遇到了类似的问题,我想将一个用户控件添加到e.Cell.Controls集合中。尝试了几种失败的方法,如用户控件Page_Load未触发或ascx上的列表框引发了空异常,后来我发现如果在表单上使用LoadControl(ascx)初始化控件,然后开始访问ascx上的标记控件,一切都正常运行。这种方法完全不依赖于ascx的Page_Load事件。
ASCX标记

代码后台

公共类CPCalendarCell 继承自System.Web.UI.UserControl

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

End Sub

Public Sub Add(txt As String)
    Dim li As New ListItem(txt, txt)
    lstDay.Items.Add(li)
End Sub

结束类

ASPX页面标记

在表单上的日历DayRender事件的代码后台

Private Sub Calendar1_DayRender(sender As Object, e As System.Web.UI.WebControls.DayRenderEventArgs) Handles Calendar1.DayRender
    Dim div As CPCalendarCell = LoadControl("~/UserControls/CPCalendarCell.ascx")
    div.ID = "dv_" & e.Day.Date.ToShortDateString.Replace(" ", "_")

    **e.Cell.Controls.Add(div)**

    div.Add(e.Day.Date.Month.ToString & "/" & e.Day.Date.Day.ToString)
    div.Add("Item 1")
    div.Add("Item 2")
    e.Cell.Style.Add("background-color", IIf(e.Day.IsWeekend, "whitesmoke", "white").ToString)
End Sub

0

这里是由Steven Robbins撰写的博客文章。该文章解释了如何使用LoadControl为用户控件传递参数,类似于LoadControl(Type, object[])的功能,不同的是它可以正常工作 :)


0
同意Mitchel的观点,您应该能够在控件代码本身中放置一个page_load,这将在控件完全可用后触发。

1
PreRender发生在Load之后(http://msdn.microsoft.com/en-us/library/ms178472.aspx#lifecycle_events),因此PreRender应该是安全的。 - Chris

0

好的,总是可以添加自己的加载事件,并在运行构造函数并将控件添加到页面后调用它,但这与您拥有的内容并没有太大区别,尽管出于样式原因,我可能会选择它。

很高兴你找到了解决方案!


-1
根据ASP.NET页面生命周期,您的控件在预呈现阶段并未完全添加,为什么不在page_load中加载值呢?

1
这不是正确的。PreRender发生在Load之后。 在页面对象创建所有控件后引发 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接