停留在动态控件、视图状态和回传上。简化示例

Stuck on Dynamic Controls, Viewstate, and Postback. Simplified example

我一直在阅读帖子和链接,以及有关 viewstate、statebag、回发和页面创建的文章。

我是一个相当不错的业余爱好者,但我并没有完全理解这个,有人可以告诉我一个如何做到这一点的例子吗?

好的,这就是我要用这个例子做的事情

创建一个 table,其中左列有 LinkBut​​tons

单击每个 linkBut​​ton 时,它会在右栏中打开一堆图像按钮

然后每个 Imagebutton 弹出一条消息

问题是图像按钮没有触发消息

这是前端:

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="simple.aspx.vb" Inherits="simple" %>

<!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">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>simple</title>
</head>
<body>
<form id="form1" runat="server">

<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>

<asp:Table id="MainTable" runat="server">
<asp:TableRow>

<asp:TableCell ID ="cell1" BackColor="LightBlue" />

<asp:TableCell ID ="cell2" BackColor="LightBlue" />

</asp:TableRow>
</asp:Table>

</form>
</body>
</html>

这是代码:

Partial Class simple
Inherits System.Web.UI.Page

Protected Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init

    Dim NewTableButton As LinkButton = New LinkButton
    NewTableButton.Text = "Text Created from 1st SQL Query"
    AddHandler NewTableButton.Click, AddressOf LoadContacts

    cell1.Controls.Add(NewTableButton)

End Sub

Protected Sub LoadContacts(sender As Object, e As System.EventArgs)

    Dim TextPassedfromLinkButton As String = sender.text.ToString

    Dim imgGear As New ImageButton
    imgGear.ImageUrl = "../graphics/gear.jpg"
    imgGear.AlternateText = "Text Created from 2nd SQL Query using the " _
 & TextPassedfromLinkButton & " as a query parameter"
    AddHandler imgGear.Click, AddressOf message

    cell2.Controls.Add(imgGear)

End Sub

Protected Sub message(ByVal sender As Object, ByVal e As System.EventArgs)

    Dim strText As String = sender.text.ToString
    Dim alertString As String = "alert('" & strText & "');"
    ScriptManager.RegisterStartupScript(Me, [GetType](), "showalert", alertString, True)

End Sub

End Class

这是您单击应弹出消息的按钮后的查看源代码

 <table id="MainTable">
 <tr>
 <td id="cell1" style="background-color:LightBlue;"><a href="javascript:__doPostBack(&#39;ctl03&#39;,&#39;&#39;)">Text Created from 1st SQL Query</a></td>
 <td id="cell2" style="background-color:LightBlue;"><input type="image" name="ctl04" src="../graphics/gear.jpg" alt="Text Created from 2nd SQL Query using the Text Created from 1st SQL Query as a query parameter" /></td>
 </tr>
 </table>

所以与此同时我让这个工作(更新 10:49 est)

Protected Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init

    Dim NewTableButton As LinkButton = New LinkButton
    NewTableButton.Text = "Text Created from 1st SQL Query"
    'AddHandler NewTableButton.Click, AddressOf LoadContacts
    AddHandler NewTableButton.Click, Function() LoadContacts(NewTableButton.Text)

    cell1.Controls.Add(NewTableButton)

    If IsPostBack Then
        LoadContacts(NewTableButton.Text)
    End If

End Sub

Private Function LoadContacts(strText As String) As Integer

    Dim TextPassedfromLinkButton As String = strText

    Dim imgGear As New ImageButton
    imgGear.ID = "uniquename"
    EnableViewState = False
    imgGear.ImageUrl = "../graphics/gear.jpg"
    imgGear.AlternateText = "Text Created from 2nd SQL Query using the " & TextPassedfromLinkButton & " as a query parameter"
    AddHandler imgGear.Click, AddressOf message

    cell2.Controls.Add(imgGear)

    Return 0

End Function

唯一的问题是我得到了 2 个相同的 imgGears

duplicate buttons http://reinsmith.net/graphics/simple1.png message displayed http://reinsmith.net/graphics/simple2.png

这里的问题是您正在实例化 imgGear 按钮 在另一个按钮的事件处理程序期间

这样做似乎完全合乎逻辑,但是如果 imgGear 在页面生命周期中创建和添加到控件树的时间过晚,则永远不会触发 imgGear 的事件处理程序,例如在事件处理程序中或在 OnPreRender 期间。

使用标准的 .Net 实践很难实现您想要做的事情...我从来没有做到过。您可能还会注意到在事件处理程序期间添加的文本框通常不会在回发时保留其值,同样是因为它在页面生命周期中添加得太晚了。

您确实需要在 OnInit 或 CreateChildControls 期间检测上下文,因为在该阶段实例化并添加到控件树的按钮触发其处理程序。

不过我已经开发了一个变通办法...和大多数变通办法一样,它并不漂亮...但它可以让您摆脱困境:

在 OnInit 期间,您可以判断是否是您的按钮导致了回发,如果它是一个成员变量。在构造函数中初始化 NewTableButton...然后您可以检测它是否在 OnInit 期间被单击...

   Private NewTableButton as Button

   Public Sub New()
        MyBase.New()
        NewTableButton = New LinkButton
        NewTableButton.Text = "Text Created from 1st SQL Query"
    End Sub

    Protected Overrides Sub OnInit(e As EventArgs)
        MyBase.OnInit(e)
        cell1.Controls.Add(NewTableButton)
        If Not IsNothing(Page.Request(NewTableButton.UniqueID)) Then
           ' Create and add imgGear Button
           LoadContacts
        End If
    End Sub

但是,文本框/下拉值在使用标准做法的 OnInit 期间不可用...因此,您可能必须 a) 推迟读取它们直到 OnPreRender,或 b) 使用 Page.Request(Control.UniqueID) 在 OnInit 期间读取输入控件的值。

这是我在 .Net web 开发中遇到的最大问题之一,它让人头疼,我不喜欢变通...

我很想知道是否有替代此解决方法的方法,但这是我找到的实现您想要实现的目标的唯一方法。

这里的问题与页面生命周期以及事件的连接方式有关。当您已经进入页面生命周期的事件处理程序阶段时,为新控件连接事件为时已晚。您需要在此过程的早期做一些事情才能起作用。

为了解决这个问题,我想到了 3 种对 Webforms 更友好的潜在方法。

  1. 通过创建 Repeater 控件、DataGridView 或类似控件并将 Sql 查询的结果绑定到它来使用数据驱动的方法。此解决方案的一部分可能涉及编写一些原始的 html/javascript 以与转发器结合使用,并结合 WebMethod,以使您的事件主要发生在客户端并避免导致完全回发。鉴于我们已经知道您有一个 SQL 查询,这似乎是最合适的。
  2. 为您需要的每个可能的控件创建空的占位符控件并将 Visible 属性 设置为 False。然后在 运行 时将数据分配给现有控件,并且只为您实际使用的那些控件设置 VisibleTrue。当您知道项目数量相对较少且具有最大上限时,这通常是最佳选择。
  3. 开发一个自定义控件,将 ImageButton/Message/Event 概念封装到单个控件中。

此外,您对另一个答案的评论表明您可能想要考虑为 viewing/editing 单个客户创建一个单独的页面,而不是查看所有客户(可能使用查询字符串参数)。在这个页面中,对于模态弹出窗口,我将其放入 div,其中支持弹出窗口的数据和其他所有内容在页面开头 已加载 , 只需使用 javascript 而不是服务器代码来更改页面 DOM 以显示或隐藏弹出窗口。