从用户控件中添加到页面控件集合

5
我有一个代表“弹出”对话框的asp.net用户控件。基本上,它是jQuery UI对话框的包装器,可以被子类化以轻松创建对话框。
作为此控件的一部分,我需要将一个div注入到使用该控件的页面中,无论是在表单的顶部还是底部,以便在实例化弹出窗口时,其父级更改为此div。这允许“嵌套”的弹出窗口,而不会使子弹出窗口被困在父弹出窗口中。
问题是,我找不到一种安全的方法来将此div注入到页面中。用户控件没有preinit事件,因此我无法在那里执行操作,在Init、Load或PreRender中调用Page.Form.Controls.Add(...)会导致标准异常“The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases.”
我曾经认为通过使用...找到了解决方案。
ScriptManager.RegisterClientScriptBlock(Page, Me.GetType, UniqueID + "_Dialog_Div", containerDiv, False)

最近一个同事试图在对话框中放置UpdatePanel,但出现了错误:“为类型'ASP.controls_order_viewzips_ascx'和键'ctl00$ContentBody$OViewZips_Dialog_Div'注册的脚本标记具有脚本标记之外的无效字符:。只有格式正确的脚本标记才能被注册。”

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

2个回答

7
我不确定为什么你需要在页面表单中添加这个div,但是以下方法可以实现:

我不确定为什么你真的需要在页面表单中添加这个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上修改”,我需要将Literal Control添加到我的主控件页头部来自用户控件。 Literal Control将包含CSS链接。
您是否想将文字直接添加到主控件页的HeadContent-ContentPlaceHolder控件或页面标题(html head元素)?无论如何,这里我都会展示。
以下是您的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方法,我在主文件中提供了以下属性:

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

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

2
有一个小但重要的区别。如果您处理“页面”的init事件,您并没有直接处理页面的init事件。实际上,您正在处理用户控件的init事件,并且该事件在页面本身之前触发。因此,您想要更改尚未完全初始化的页面的控件集合。通过添加事件处理程序,您可以确保div将直接在页面初始化后而在page_load之前被添加。这里有一些有趣的细节:http://msdn.microsoft.com/en-us/library/ms178472.aspx#additional_page_life_cycle_considerations 很高兴能帮助。 - Tim Schmelter
@TimSchmelter 我指的是如何在C#中实现。对于混淆表示抱歉。总是出现“在Load、PreRender等上不允许修改控件集合”的错误。 - Fahad Abid Janjua
我需要在用户控件中添加一个文本控件到主页头部。该文本控件将包含CSS链接。 - Fahad Abid Janjua
1
@FahadAbidJanjua:编辑了我的答案以提供解决方案。下次最好问自己的问题,因为答案变得混乱了。 - Tim Schmelter
1
@FahadAbidJanjua:MasterPagePage的子控件,因此实际上只有页面会有一个头部,主控件将与内容页合并。因此,它使用页面的Header属性,如上所示。如果您仍想访问主控件的头部控件,可以使用与HeadContent相同的方法。因此,您需要为主控件的头部分配一个ID。然后,您可以添加一个返回此头部的属性:public System.Web.UI.HtmlControls.HtmlHead Head { get { return this.MasterHead; } }。用户控件中的部分与HeadContent相同。 - Tim Schmelter
显示剩余8条评论

4

UserControl没有PreInit事件,但是您可以在UserControl中添加一个Page PreInit事件处理程序:

Page.PreInit += new EventHandler(Page_PreInit);

编辑 - 你是对的 - 无法从用户控件捕获 PreInit,尽管我很惊讶你不能 - 但你仍然可以通过在 UserControl 的构造函数中添加代码来更改页面控件集合。 我尝试了这个方法,它有效。

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

在构造函数中将事件处理程序添加到Page.PreInit可以编译,但从未触发。
话虽如此,我不确定为什么需要这样做才能实现您的目标。您为什么不让对话框控件在表单中的任何位置呈现其自己的div,并将其用作父项,而不是尝试在表单的其他位置创建一个新的div?我想不出为什么它在表单的开头或结尾物理上呈现很重要。它是一个对话框,所以直到使用它之前它总是不可见的,对吧?

我觉得我漏掉了什么。控件可以访问的最早事件是Init,那么我应该把这行代码放在哪里来注册PreInit事件呢?我可以在页面上做到这一点,但这有点违背使用控件的目的。我像这样注入div,是因为我在使用jQuery UI时遇到了问题,这意味着从另一个对话框打开一个对话框会导致第二个对话框嵌入到第一个对话框中而不是独立的。 - Zarigani
编辑后的答案。但为什么需要这样做的基本观点仍然存在。如果你的jQuery代码可以找出用户控件添加到主页面的单个div,为什么它不能找到由父级内联添加的相同的div呢?我不明白div出现的位置为什么很重要。jQuery应该能够找出它是否已经在一个div中,并在它出现的任何地方使用现有的div。 - Jamie Treworgy
1
顺便说一下,我很抱歉我用C#举例(直到现在我才注意到它是VB),但我认为相同的语法在VB中也可以工作。 - Jamie Treworgy
感谢您的回复。从HttpContext获取页面引用确实很有趣;我以前没有见过这样的方法。在构造函数期间,控件的页面属性为空,因此我认为您无法在那个时候附加事件。对于C# / VB没有问题 - 我已经习惯了在这个阶段进行翻译 :) 很抱歉,Tim已经赢得了奖励,但感谢您的贡献。 - Zarigani
控件的页面属性为空,但由于您可以从HttpContext获取页面引用,因此我尝试在构造函数中将PreInit事件附加到它。这似乎应该起作用,但也许它已经触发了,尽管我很惊讶任何事件会在子对象的构造函数运行之前触发。无论如何,它确实允许您从构造函数修改控件集合。不用担心赏金,我不是为了钱而参加的 :) - Jamie Treworgy

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