ASP.NET动态命令按钮事件未触发。

4

我正在尝试动态创建命令按钮,但单击相应的按钮似乎没有引发CommandButton_Click事件。我注意到,在SO的示例中,设置了一个Button.OnCommand属性以及CommandName和CommandArgument,但在智能感知中没有这个选项。

所以问题是,我在这里做错了什么(下面的代码没有OnCommand),它是以其他方式访问的吗?如果是,为什么我找到的所有示例都显示为.OnCommand?

编辑:进一步帮助,我已经添加了处理程序,但事件仍未触发。按钮驻留在UpdatePanel中,并在每次PostBack时重新构建(以及处理程序)。我创建了一个简化的示例,如下所示。如果按钮事件触发,则将“EVENT FIRED”写入txtTestFired文本框-可以说我从来没有见过。这真的让我疯了,非常感谢任何帮助。

.aspx文件

<form id="frmMain" runat="server">
    <asp:ScriptManager ID="scmAddProducts" runat="server">
    </asp:ScriptManager>
    <asp:updatepanel runat="server">
        <ContentTemplate>
            <asp:TextBox ID="txtProduct" runat="server"></asp:TextBox>
            <br />
            <asp:Button ID="btnAddItem" runat="server" Text="Add Line" />&nbsp;
            <asp:TextBox ID="txtTestFired" runat="server"></asp:TextBox>
            <br />
            <br />
            <asp:Panel ID="pnlAddedLines" runat="server"></asp:Panel>
        </ContentTemplate>
    </asp:updatepanel>
</form>

.aspx.vb file

Protected Sub btnAddItem_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddItem.Click
    Dim dtItems As New System.Data.DataTable

    If Session("Items") Is Nothing Then
        Dim dcColumn As New System.Data.DataColumn
        dcColumn.DataType = Type.GetType("System.String")
        dcColumn.ColumnName = "Product"
        dtItems.Columns.Add(dcColumn)
        Session("Items") = dtItems
    End If

    dtItems = CType(Session("Items"), System.Data.DataTable)
    Dim drRow As System.Data.DataRow
    drRow = dtItems.NewRow()
    drRow("Product") = txtProduct.Text
    dtItems.Rows.Add(drRow)

    Session("Items") = dtItems
    txtProduct.Text = ""
End Sub
Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
    If Not Session("Items") Is Nothing Then
        Dim dtItems As System.Data.DataTable = CType(Session("Items"), System.Data.DataTable)
        Dim iItemIndex As Integer = 1
        For Each drRow In dtItems.Rows
            Dim btnClose As New Button
            btnClose.ID = "btnClose" & iItemIndex
            btnClose.CssClass = "formCloseButton"
            btnClose.Text = "X"
            AddHandler btnClose.Click, AddressOf Button_Clicked
            pnlAddedLines.Controls.Add(btnClose)
            btnClose = Nothing

            Dim txtProduct = New TextBox
            txtProduct.ID = "txtProduct" & iItemIndex
            txtProduct.CssClass = "formText"
            txtProduct.Text = drRow("Product")
            txtProduct.Columns = "40"
            pnlAddedLines.Controls.Add(txtProduct)
            iItemIndex += 1

            Dim litHR = New Literal
            litHR.Text = "<hr />"
            pnlAddedLines.Controls.Add(litHR)
            litHR = Nothing
        Next
    End If
End Sub
Private Sub Button_Clicked(ByVal sender As Object, ByVal e As System.EventArgs)
    txtTestFired.Text = "EVENT FIRED"
End Sub

请参见我在回复中的编辑。 - JohnIdol
已移至 Page_Load,并且事件被触发...但是...现在出现了另一个问题(这就是为什么我最初将其放在 Pre_Render 中的原因)。单击按钮事件(以添加文本框等)发生在页面生命周期中的 Page Load 之后。因此,当我将其添加到我的测试中时,我会遇到一个问题:系统总是落后于一次 Postback - 也就是说,在呈现第一组控件之前,必须提交两次。 - Chris
我认为你的原始问题已经正确解决了(事件未触发)- 你当前的问题可能应该作为一个单独的问题添加。听起来有点像你可能太晚添加新控件了,你在点击处理程序期间进行了重定向吗? - dice
谢谢,事件仍未按所需的上下文触发。如果不可能,那就算了,但我宁愿在上下文中继续问题,而不是创建一个几乎重复的问题。问题的关键是用户单击按钮创建控件。控件不知道它们需要在page_load中创建,因为尚未处理按钮单击事件。有一个快速而肮脏的解决方法,即在每次提交后进行虚拟回发或重新加载,但这很笨拙且难看。 - Chris
5个回答

6

您需要像这样连接它(使用C#):

myButton.Click += new EventHandler(this.Button_Clicked);

或者您可以像这样注入属性 --> onclick = "Button_Clicked"

myButton.Attributes.Add("onclick", "Button_Clicked");

如果你想用VB.NET,可以前往该页面进行转换 - 我也可以为你完成,但这有什么意义呢 :)。
编辑:请记住,您必须重新连接事件处理程序(如上所示)每次页面加载。
编辑:好的 - 我现在看到了问题,在您添加了其余代码之后。您正在pre_render上绑定事件处理程序。事件处理程序在load_complete之前执行,而load_complete又比pre_render早。这就是为什么您的事件处理程序不执行:您太晚添加它到页面生命周期中了。将该代码放入page_load例程中,应该可以解决该问题。

你可以访问这个网址 --> http://www.developerfusion.com/tools/convert/csharp-to-vb/ :) - JohnIdol
如果您想通过单击按钮来更新更新面板,则需要将该按钮设置为更新面板的触发器。如果按钮在更新面板中,则不应该有问题(更新面板应该刷新,您的代码后台应该执行)。但也许我漏掉了什么,更新您的问题并显示标记(带有更新面板)可能会有所帮助。 - JohnIdol
您需要在每次Postback时重新创建按钮,否则事件将无法触发。 - Thomas
我本来以为是这样的(不应该这么想)。你每次页面加载时都在重新连接事件处理程序吗? - JohnIdol
1
@Johnldol 实际上,我指的是 Chalkey 对答案不在 VB 中的评论。我的观点是,“如果你不能将这个微小的东西从 C# 移植到 VB,那么你需要解决的问题比让你的事件触发更大。”这更像是一个半开玩笑的评论。我认为您个人超越了发布转换器的范畴。实际上,目前只有我一个人投票支持您的帖子... - San Jacinto
显示剩余8条评论

4

基本上,在页面加载时您需要创建原始控件以便能够处理按钮单击事件,然后需要清除控件并在按钮单击处理程序中重新生成它们,或者像您在pre-render中所做的那样 - 我认为在单击处理程序中更清晰,因此您需要在两者中使用相同的代码(最好重构为一个单独的方法)。

下面的示例将意味着您需要进行最少的代码更改:

Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
    If Not Session("Items") Is Nothing Then
        pnlAddedLines.Controls.Clear()
        GenerateControls()
    End If
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    If Not Session("Items") Is Nothing Then
        GenerateControls()
    End If
End Sub
Private Sub GenerateControls()
    Dim dtItems As System.Data.DataTable = CType(Session("Items"), System.Data.DataTable)
    Dim iItemIndex As Integer = 1
    For Each drRow In dtItems.Rows
        Dim btnClose As New Button
        btnClose.ID = "btnClose" & iItemIndex

谢谢 - 根据我的实际需求,您的方法稍有不同 - 我拥有一个计数器,它创建n+1个命令按钮,然后清除它们以进行可能由按钮点击引起的添加/删除(无论是添加另一个还是删除一个) - 这样我永远不会有超过+2个多余的处理程序漂浮在周围,这是一个问题。工作得很好 - 谢谢。 - Chris
好时机 :) - 我可能会在ViewState中存储一个计数器,而不是在会话中使用DataTable? - JohnIdol
我发现ViewState的问题在于,如果用户刷新页面,控件就会丢失(每行大约有6个控件)- 表单可能需要一段时间来构建,所以如果输入的数据在刷新后没有保留,这对他们来说将是一个非常令人沮丧的时刻。再次感谢您一路上的所有帮助(顺便给您的答案+1声望,很抱歉我不能给更多)。 - Chris

1

我曾经遇到过同样的问题,我的控件加载得非常好。事件第一次触发,但第二次没有反应。问题是我没有将唯一标识符与控件绑定。因此,我为我要添加到页面的控件添加了一个唯一的ID字段,这样每次单击时都可以正常工作。


你能否用一些代码来详细说明你的答案? - Henrik Andersson

0

正确的方式是:

  1. OnLoad - 生成动态内容(创建动态按钮)
  2. Button OnClick - 消费事件并重新生成正确的内容

但是它需要生成内容两次 - 一次用于创建按钮以处理事件,然后清除旧内容并生成正确的内容 - 第二次。您可以像这样绕过它:

  1. OnLoad - 检查是否单击了按钮
  2. PreRender - 使用动态按钮生成正确的内容(内容仅生成一次)

我的例子如下:

<script type="text/javascript" language="javascript">
    function SetPostBackAction(Action,ReturnValue)
    {
        if(ReturnValue)
            document.getElementById('<%=hfPostBackAction.ClientID%>').value=Action;
        return ReturnValue;
    }
</script>
<asp:HiddenField ID="hfPostBackAction" EnableViewState="false" runat="server" />

而在代码中:

protected void Page_PreRender(object sender, EventArgs e)
{
    //Dynamic button in PreRender
    System.Web.UI.WebControls.Button btn=new System.Web.UI.WebControls.Button()
    btn.ID=string.Format("DeleteButton_SomeData{0}", 12345);
    btn.Text="Delete";
    btn.OnClientClick=string.Format("return SetPostBackAction('DeleteButton_SomeData{0}',window.confirm('Are you sure to delete?'))", 12345);
    btn.Click+=new EventHandler(btnDelete_Click);//This method will not be called if button is created in PreRender
}

protected void Page_Load(object sender, EventArgs e)
{
    //Do something...
    //Check Action
    ProccessAction();//You can check action whereever you want
}

protected void ProccessAction()
{
    string Action=Request.Form[hfPostBackAction.UniqueID]??string.Empty;
    int SomeData;

    if(Action.StartsWith("DeleteButton_"))//Is action?
    {
        SomeData=int.Parse(Action.Substring("DeleteButton_SomeData".Length));
        //Do something...
    }
    //Erase Action field not to fire action next time accidentially
    hfPostBackAction.Value=string.Empty;
}

0

您需要连接点击事件。请参考以下代码...

Partial Class _Default
    Inherits System.Web.UI.Page

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

        Dim button1 As New Button
        Dim button2 As New Button

        button1.ID = "button1"
        button1.Text = "Button 1"
        button2.ID = "button2"
        button2.Text = "Button 2"

        AddHandler button1.Click, AddressOf OnClick
        AddHandler button2.Click, AddressOf OnClick

        form1.Controls.Add(button1)
        form1.Controls.Add(button2)

    End Sub

    Private Sub OnClick(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim buttonId As String

        buttonId = DirectCast(sender, Button).ID

        Response.Write("Button clicked: " + buttonId)

    End Sub

End Class

希望这可以帮到你...

谢谢 - 我已经加入了这个代码,但它仍然不起作用!我现在在想是否是因为按钮是在 UpdatePanel 中创建的 - 在你的知识中有这种情况吗? - Chris

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