在Page_Load之外加载控件是禁忌吗?

时间:2009-08-25 02:20:38

标签: asp.net dynamic controls render lifecycle

我提前发表了这样一个冗长的问题。信不信由你,你在这里看到的实际上代表了手头问题/代码的一个相当简洁的版本。虽然我会欣赏有关更好或不同方法的任何指示,但我也非常希望能够深入了解这一点,以便我可以在晚上睡觉:)

我遇到了在不同的aspx页面之间传递确认消息的要求。我选择不使用查询字符串变量,因为查询字符串值“是”粘性的(即它们在所有后续回发中持续存在)并且我不想处理在此周围添加一堆条件逻辑。

无论如何,我提出了一个非常简单的类,它使用Session将通知与特定URL相关联。然后我挂钩了我的母版页 Page_Load 事件来查询此类,以查找应该为当前URL显示的任何通知。如果找到,则动态加载NotificationMessage用户控件并显示消息内容。

尝试在不同的aspx页面之间传递通知时,一切都按预期工作。可以预见,当内容页面尝试向自身添加通知时(即“您输入的数据无效,请重试”),事情不起作用。原因非常明确:当内容页面为自己添加通知时,母版页的 Page_Load 事件已经触发,因此在页面生命周期中为时尚晚做任何好处。相关代码粘贴在下面。

    public class MyMasterPage:MasterPage{

    protected void Page_Load(object sender, EventArgs e)
    {
        LoadNotifications(this.Request.Url.ToString());
    }


    private void LoadNotifications(string url)
    {
        //look for a notification
        Notification? notification = NotificationManager.Instance.RetrieveNotification(url);

        //there are no notifications, nothing to see here 
        if (!notification.HasValue)
        {
            return;
        }

        //there is a Notification for this url, so load it into a user control
        NotificationMessage notificationMessageControl = (NotificationMessage)LoadControl("~/App_UserControls/NotificationMessage.ascx");
        notificationMessageControl.ID = "notificationMessage";
        notificationMessageControl.Notification = notification;
        notificationMessageControl.Visible = true;

        //find the placeholder on the master page
        PlaceHolder placeHolder = (PlaceHolder)PageUtils.FindControlRecursive(this, "NotificationPlaceholder");

        if (placeHolder == null)
        {
            throw new ApplicationException("NotificationPlaceholder control not found.");
        }

        //insert into control
        placeHolder.Controls.Add(notificationMessageControl);
        placeHolder.Visible = true;

        //remove the notification so it doesn't show up next time
        NotificationManager.Instance.RemoveNotification(url);


    }

}

鉴于上面提到的生命周期,我修改了NotificationManager类,以便在为当前页面添加通知时引发事件。主页拦截该事件,如果 Page_Load 已经触发,它将重新启动LoadNotifications方法。

 //bind the event on the page constructor
    public MyMasterPage()
    {
        NotificationManager.Instance.NotificationAdded += this.NotificationAdded;

    }

    private void NotificationAdded(string forUrl)
    {
        if (_pageLoaded){
           LoadNotifications(forUrl);
        }
    }

不幸的是,这不起作用。我已多次介绍此代码,尽管母版页加载了NotificationMessage UserControl并将其添加到适当的占位符而没有发生意外,但最终的aspx HTML 从不包含该UserControl的标记。我将断点放在UserControl的 Page_Load 中,并验证它们在执行期间确实被击中。

如果我从内容页面内部动态加载UserControl并完全绕过Master页面,它会毫无障碍地呈现:

public partial class MyContentPage:Page
{

    public void DoSomethingCool(object sender, EventArgs e)
    {
        if (MyServiceLayer.Save(foo)==false){
            Notification notification = new Notification(NotificationType.Error, "We’re sorry, your document was not saved.");
            NotificationMessage notificationMessage = (NotificationMessage)LoadControl("~/App_UserControls/NotificationMessage.ascx");
            notificationMessage.Notification = notification;
            notificationMessage.Visible = true;
            PlaceHolder holder = (PlaceHolder)PageUtils.FindControlRecursive(this, "NotificationPlaceholder");
            holder.Controls.Add(notificationMessage);
        }
    }
}

为了记录,我删除了UserControl的动态加载,而是选择在母版页标记中的静态声明和基于代码的控件的Visible属性的切换;仍然没有骰子!

如果有人能对这个难题有所了解,我会非常感激。

2 个答案:

答案 0 :(得分:2)

我之前尝试过这种事情,但我从未对此感到满意。我做了什么,是把我的ASCX放在每一页(或主页上),让ASCX控制它的状态,而不是让ASPX控制我的ASCX。

我不确定这会对你的情况有所帮助。

答案 1 :(得分:1)

您似乎希望在控制事件触发后显示您的信息。您可以考虑让这些消息聚合,直到所有控制事件都被触发,然后从NotificationManager中的OnPreRender而不是Page_Load中提取所有消息。这样你就可以摆脱可能使问题复杂化的事件(如NotificationAdded)等。

不是100%问题是什么。知道MasterPage实际上是对页面的控件有时会有所帮助,而不是像你想象的那样反过来。它将受到任何控制对页面的限制。

HTH,安德森