【问题标题】:Adding to page control collection from inside a user control从用户控件内部添加到页面控件集合
【发布时间】:2011-06-25 22:31:45
【问题描述】:

我有一个代表“弹出”对话框的 asp.net 用户控件。基本上,它是 jQuery UI 对话框的包装器,可以将其子类化以轻松制作对话框。

作为该控件的一部分,我需要将一个 div 注入到使用该控件的页面中,无论是在表单的最顶部还是最底部,以便在实例化弹出窗口时,它的父级会更改为该 div .这允许“嵌套”弹出窗口,而不会将子弹出窗口困在父弹出窗口中。

问题是,我找不到将这个 div 注入页面的安全方法。用户控件没有 preinit 事件,所以我不能在那里做,并且在 Init、Load 或 PreRender 中调用 Page.Form.Controls.Add(...) 会导致标准异常“无法修改控件集合在 DataBind、Init、Load、PreRender 或 Unload 阶段。"

我以为我已经通过使用...找到了解决方案

ScriptManager.RegisterClientScriptBlock(Page, Me.GetType, UniqueID + "_Dialog_Div", containerDiv, False)

... 这似乎正常工作,但最近一位同事尝试在对话框中放置一个 UpdatePanel,现在她收到错误“为类型 'ASP.controls_order_viewzips_ascx' 和键 'ctl00$ContentBody$OViewZips_Dialog_Div 注册的脚本标签” ' 在脚本标签之外有无效字符: 。只能注册格式正确的脚本标签。"

您应该如何从用户控件内部向页面控件集合添加控件?

【问题讨论】:

    标签: asp.net vb.net user-controls jquery-ui-dialog


    【解决方案1】:

    我不确定您为什么真的需要将此 div 添加到页面的表单中,但这应该可以:

      Public Class WebUserControl1
        Inherits System.Web.UI.UserControl
    
            Private Sub UC_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
                AddHandler Me.Page.Init, AddressOf Me.Page_Init
            End Sub
    
            Private Sub Page_Init(ByVal sender As Object, ByVal e As EventArgs)
                Dim dialogDiv As New Panel
                dialogDiv.ID = "DialogDiv"
                If Not Page.Form.Controls.Contains(dialogDiv) Then
                    Page.Form.Controls.AddAt(0, dialogDiv)
                End If
            End Sub
    
        End Class
    

    如何在 C# 中实现它。我总是得到错误控制 集合不能在 Load、PreRender 上修改 我需要从用户控件将文字控件添加到我的母版页的头部。文字控件将包含 css 链接。

    您要将文字添加到主控中的HeadContent-ContentPlaceHolder 控件还是页面的标题(html 头元素)?但是,这里我都展示了。

    这是您的 UserControl 的代码隐藏:

    public partial class UC_AddToMaster : System.Web.UI.UserControl
    {
        private void Page_Init(object sender, System.EventArgs e)
        {
            this.Page.Init += UC_AddToMaster_Init;
        }
    
        private void UC_AddToMaster_Init(object sender, EventArgs e)
        {
            Literal literal = new Literal{ Text = "Hi World!" };
            // if you want to add it to the header of the page:
            if (!Page.Header.Controls.Contains(literal))
            {
                Page.Header.Controls.AddAt(0, literal);
            }
    
            // if you want to add it to the master's HeadContent ContentPlaceHolder control:
            var siteMaster = Page.Master as SiteMaster;
            if (siteMaster != null)
            {
                if (!siteMaster.Head.Controls.Contains(literal))
                {
                    siteMaster.Head.Controls.AddAt(0, literal);
                }
            }
        }
    }
    

    对于上面提到的HeadContent 方法,我在master 中提供了以下属性:

    // in the master
    public ContentPlaceHolder Head { get { return this.HeadContent; } }
    

    因此,我需要在UserControl 中将页面的母版转换为它的实际类型(此处为SiteMaster)。否则我无法访问此属性。

    【讨论】:

    • 您不能在 Page Init 中更改页面控件集合。此外,控件已经具有 init 事件,因此无需注册。如果您的意思是 PreInit,那么我不知道控件何时可以注册 PreInit 事件,因为它有权访问的第一个事件是 Init?
    • 你试过了吗?使用此代码,面板将在页面上呈现为 div。我已经为页面的 init 事件添加了一个处理程序,因为您无法将 usercontrol 的 init 事件中的控件添加到页面的表单中,因为此时表单将一无所有(usercontrol 的 init 在页面的 init 之前触发)。
    • 有一个很小但很重要的区别。如果您处理“页面”的 init 事件,则不会直接处理页面的 init 事件。实际上,您正在处理用户控件的 init 事件,该事件在页面本身之前被触发。因此,您想更改尚未完全初始化的页面的控件集合。通过添加事件处理程序,您可以确保在页面初始化之后和 page_load 之前直接添加 div。以下是一些有趣的细节:msdn.microsoft.com/en-us/library/…很高兴我能提供帮助。
    • @FahadAbidJanjua:编辑了我的答案以提供解决方案。下次你最好问自己的问题,因为答案越来越混乱。
    • @FahadAbidJanjua:MasterPagePage 的子控件,所以实际上只有页面会有头部,主控件会与内容页面合并。所以它与页面的Header 属性一起使用,如上所示。如果您想访问主控的头部控制,您可以使用与HeadContent 相同的方法。因此你需要给主人的脑袋一个ID。然后您可以添加一个返回此头部的属性:public System.Web.UI.HtmlControls.HtmlHead Head { get { return this.MasterHead; } }。用户控件中的部分与HeadContent相同。
    【解决方案2】:

    Usercontrol 没有 PreInit 事件,但没有什么能阻止您向 UserControl 中的 Page PreInit 事件添加处理程序:

    Page.PreInit += new EventHandler(Page_PreInit);
    

    编辑 - 你是对的 - 你不能从用户控件捕获 PreInit,虽然我很惊讶你不能 - 但你仍然可以通过向用户控件的构造函数添加代码来更改页面控件集合。我试过了,效果很好。

    public MyUsercontrol()
    {
        Page page = (Page)HttpContext.Current.Handler;
        Literal lit = new Literal();
        lit.Text="text";
        page.Controls.Add(lit);
    }
    

    在构造函数中向 Page.PreInit 添加事件处理程序可以编译,但它永远不会触发。

    (结束编辑)

    也就是说,我不确定为什么这对于实现您的目标是必要的。你为什么不让你的对话框控件在任何你将它放入表单的地方直接渲染它自己的 div,并将其用作父级,而不是尝试在表单的其他地方创建一个呢?我想不出为什么在表单的开头或结尾处物理呈现它很重要。这是一个对话框,所以在你使用它之前它总是不可见的,对吧?

    【讨论】:

    • 我想我错过了一些东西。控件可以访问的最早事件是 Init,那么我应该将这行代码放在哪里来注册 PreInit 事件?我可以在页面上这样做,但这会破坏使用控件的目的。我像这样注入 div 是因为我遇到了 jQuery UI 问题,这意味着从另一个对话框打开一个对话框会导致第二个对话框嵌入第一个而不是独立的。
    • 已编辑答案。但是关于你为什么需要这样做的基本观点仍然存在。如果您的 jQuery 代码可以使用用户控件添加到主页的单个 div,为什么它无法使用父级内联添加的相同 div?我不明白为什么 div 出现在哪里很重要。 jquery 应该能够确定它是否已经在其中,并在它出现的任何地方使用现有的 div。
    • 顺便说一句,我很抱歉我用 C# 给出示例(直到现在我才注意到它是 VB),但我认为同样适用于 VB 语法。
    • 感谢您的回复。从 HttpContext 获取页面引用真的很有趣;我以前没见过。控件的页面属性在构造函数期间为空,因此我认为您当时无法附加事件。 C# / VB 没问题 - 我已经习惯了在这一点上进行翻译 :) 恐怕 Tim 会打败你,但感谢你的意见。
    • 控件的页面属性为空,但由于可以从 HttpContext 中获取页面引用,因此我尝试在构造函数中将 PreInit 事件附加到它。看起来它应该可以工作,但也许它已经触发了,尽管我很惊讶在子对象的构造函数运行之前会触发任何事件。无论如何,它确实允许您从构造函数修改控件集合。不用担心赏金,我不是为了钱:)
    猜你喜欢
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-21
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多