动态创建的用户控件无法处理回发事件

时间:2020-03-06 14:21:27  来源:igfitidea点击:

我有一个用户控件,它使用页面Init中的以下代码动态加载到页面中。

Dim oCtl As Object
oCtl = LoadControl("~/Controls/UserControl1.ascx")

oCtl.Id = "UserControl11"
PlaceHolder1.Controls.Clear()
PlaceHolder1.Controls.Add(oCtl)

用户控件还包含一个按钮,我无法捕获用户控件内的按钮单击。

解决方案

几个问题:

  • 我们在页面生命周期的什么时候加载控件?
  • 事件处理程序代码在哪里?在控件本身中还是尝试将其连接到页面?
  • 到目前为止,我们为完成活动准备了什么?

最后,.Net的样式指南特别建议不要在oCtl中使用任何匈牙利前缀疣,例如o,我们应该将其键入为Control而不是对象。

我们只想在不是isPostBack时加载控件

如果在页面回发时该控件不在控件树中,请确保在每次回发时都加载该控件,ASP.NET不会引发按钮单击事件。

在.NET进入页面生命周期的"回发事件处理"步骤之前,必须确保该控件存在于页面上。由于控件是动态添加的,因此必须确保在每个回发中都重新创建该控件,以便它可以找到触发事件的控件。

这是该页面的完整代码

Partial Class DynamicLoad
    Inherits System.Web.UI.Page

    Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
        If IsPostBack Then
            If Not (Session("ctl") Is Nothing) Then
                Dim oCtl As Object
                oCtl = Session("ctl")
                PlaceHolder1.Controls.Add(oCtl)
            End If
        End If
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
            Dim oCtl As Object
            oCtl = LoadControl("~/Controls/UserControl1.ascx")

            oCtl.Id = "UserControl11"
            PlaceHolder1.Controls.Clear()
            PlaceHolder1.Controls.Add(oCtl)

            Session("ctl") = oCtl
        End If
    End Sub
End Class

这是用户控件的完整代码

Partial Class UserControl1
    Inherits System.Web.UI.UserControl
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Label1.Text = "This is Text AFTER Post Back in User Control 1"
    End Sub
End Class

如我们所问,我将这样编写init事件。我将把Load事件保留为练习:

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
    If IsPostBack AndAlso Session("ctl") IsNot Nothing Then
        Dim MyControl As Control = Session("ctl")
        PlaceHolder1.Controls.Add(MyControl)
    End If
End Sub

我也想出了一个比" mycontrol"更好的名称,但是由于我不知道控件要做什么,因此必须这样做。

我不尝试就不知道,但是如果我们以编程方式连接按钮的事件处理程序怎么办?例如,在用户控件本身的代码背后,在"初始化"或者"加载"(不确定)中:

AddHandler Button1.Click, AddressOf Button1_Click

如果那什么都不做,我知道它的效率会降低,但是如果我们不将User Control实例存储在Session中,并且每次都始终在Page_Init中重新创建它,该怎么办?

我们正在执行的一些操作既不需要,又可能导致问题。

这些是:

  • 无需在会话中存储控制对象。控件本身应使用ViewState和Session State来存储所需信息,而不是整个实例。
  • 创建控件时,我们不应该检查PostBack。必须每次都创建它,以允许ViewState工作并连接事件。
  • 加载ViewState后加载的控件通常无法正常运行,因此请尽可能避免在Page Load事件中加载控件。

该代码对我有用:

Default.aspx

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="Test_User_Control._Default" %>
<!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">
        <asp:PlaceHolder ID="PlaceHolder1" runat="server" />
    </form>
</body>
</html>

Default.aspx.vb

Partial Public Class _Default
    Inherits System.Web.UI.Page

    Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init

        Dim control As Control = LoadControl("~/UserControl1.ascx")
        PlaceHolder1.Controls.Add(control)

    End Sub
End Class

UserControl1.ascx

<%@ Control Language="vb" AutoEventWireup="false" CodeBehind="UserControl1.ascx.vb" Inherits="Test_User_Control.UserControl1" %>
<asp:Label ID="Label1" Text="Before Button Press" runat="server" />
<asp:Button ID="Button1" Text="Push me" runat="server" />

UserControl1.ascx.vb

Public Partial Class UserControl1
    Inherits System.Web.UI.UserControl

    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Label1.Text = "The button has been pressed!"
    End Sub

End Class

我只是遇到了与我们类似的问题,除了在我的情况下,我没有使用会话来存储控件。

就我而言,我将问题定位在这一行:

PlaceHolder1.Controls.Clear()

我所做的是创建一个子控件,并将其添加到Page_Init的父容器中,然后处理了一些事件处理程序,然后在Page_PreRender上,我再次使用更新的数据重新创建了整个列表。

在这种情况下,我使用的解决方案是在页面周期的早期阶段创建一次控件集合。