Retaining dynamically created controls during Post

2019-08-19 06:30发布

问题:

I have a panel with a form that has a drop down list with values 1-10. When a button on the panel is pressed, that panel is made invisible and a second made visible. On the second panel I have rows of controls being generated dynamically. The number of rows is determined by the selecteditem on the drop down list.

My problem is that when I want to carry out validation and then storing of the values of the contents of controls that are dynamically created, the controls are removed.

I understand this is to do with the lifecycle of the page and the persistence of the dynamically created controls but I have no idea to get round the problem. I have read many other similar questions on here and few have working examples of who to solve this so I am none the wiser. This includes suggestions that the dynamically created controls need to be created on pageload or pageinit, how can this be since we don't know how many to make?

Any help is much appreciated, as always.

Imports DotNetNuke.Entities.Modules
Imports DotNetNuke
Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Web.Configuration
Imports System.Net.Mail
Imports System.Data.SqlClient

Partial Class View
Inherits SeatPlannerModuleBase
Implements IActionable
Dim valid As String = "success"

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

        If Not Page.IsPostBack Then

        End If

    Catch exc As Exception        'Module failed to load
        ProcessModuleLoadException(Me, exc)
    End Try
End Sub

Public ReadOnly Property ModuleActions() As Entities.Modules.Actions.ModuleActionCollection Implements Entities.Modules.IActionable.ModuleActions
    Get
        Dim Actions As New Entities.Modules.Actions.ModuleActionCollection
        Actions.Add(GetNextActionID, Localization.GetString(Entities.Modules.Actions.ModuleActionType.AddContent, LocalResourceFile), Entities.Modules.Actions.ModuleActionType.AddContent, "", "", EditUrl(), False, DotNetNuke.Security.SecurityAccessLevel.Edit, True, False)
        Return Actions
    End Get
End Property

Protected Sub Filldata()
    Dim conn As New SqlConnection()
    conn.ConnectionString = WebConfigurationManager.ConnectionStrings("Congress_SeatPlanner_ConnectionString").ConnectionString
    Dim Qstr As String = "select * From [Seat_planner_2013].[dbo].[booking]"
    Try
        Using connection As New SqlConnection(conn.ConnectionString)
            connection.Open()
            Dim command As New SqlCommand(Qstr, connection)
            Dim reader As SqlDataReader = command.ExecuteReader()
            If reader.Read() Then
                Try
                    Dim title As String

                    title = reader("title")
                    title = title.Trim
                    title_Txt.Text = title
                Catch
                    title_Txt.Text = ""
                End Try
                Try
                    first_name_Txt.Text = reader("first_name")
                Catch
                    first_name_Txt.Text = ""
                End Try
                Try
                    surname_Txt.Text = reader("surname")
                Catch
                    surname_Txt.Text = ""
                End Try
                Try
                    company_name_Txt.Text = reader("company_name")
                Catch
                    company_name_Txt.Text = ""
                End Try
                Try
                    contact_number_Txt.Text = reader("contact_phone")
                Catch
                    contact_number_Txt.Text = ""
                End Try
                Try
                    contact_email_Txt.Text = reader("contact_email")
                Catch
                    contact_email_Txt.Text = ""
                End Try
                Try
                    number_of_tickets_Ddl.SelectedValue = reader("number_of_tickets")
                Catch
                    number_of_tickets_Ddl.SelectedValue = ""
                End Try
                Try
                    purchase_date_Txt.Text = reader("date_of_purchase")
                Catch
                    purchase_date_Txt.Text = ""
                End Try
            End If
        End Using
    Catch ex As Exception
        ErrLabel_Lbl.Visible = True
        ErrLabel_Lbl.Text = ex.Message.ToString
    End Try
End Sub

Protected Sub validate()

    If title_Txt.Text.Length < 2 Then
        title_error_Lbl.Text = "Please enter a title"
        valid = "fail"
    Else
        title_error_Lbl.Text = "*"
    End If
    If first_name_Txt.Text.Length < 1 Then
        first_name_error_Lbl.Text = "Please enter a first name"
        valid = "fail"
    Else
        first_name_error_Lbl.Text = "*"
    End If
    If surname_Txt.Text.Length < 1 Then
        surname_error_Lbl.Text = "Please enter a surname"
        valid = "fail"
    Else
        surname_error_Lbl.Text = "*"
    End If
    If company_name_Txt.Text.Length < 1 Then
        comnpany_name_error_Lbl.Text = "Please enter a company name"
        valid = "fail"
    Else
        comnpany_name_error_Lbl.Text = "*"
    End If
    If contact_email_Txt.Text.Length < 1 Then
        contact_email_error_Lbl.Text = "Please enter a contact email address"
        valid = "fail"
    Else
        contact_email_error_Lbl.Text = "*"
    End If
    If contact_number_Txt.Text.Length < 1 Then
        contact_phone_error_Lbl.Text = "Please enter a contact phone number"
        valid = "fail"
    Else
        contact_phone_error_Lbl.Text = "*"
    End If
    If number_of_tickets_Ddl.SelectedValue = "Please select" Then
        number_of_tickets_error_Lbl.Text = "Please select a number of tickets to allocate"
        valid = "fail"
    Else
        number_of_tickets_error_Lbl.Text = "*"
    End If
    If purchase_date_Txt.Text.Length < 1 Then
        date_of_purchase_error_Lbl.Text = "Please enter the date you purchased the tickets"
        valid = "fail"
    Else
        date_of_purchase_error_Lbl.Text = "*"
    End If

    If valid = "fail" Then
        ErrLabel_Lbl.Text = "Failed validation"
    Else
        ErrLabel_Lbl.Text = "Validation succeeded"
    End If

End Sub

Protected Sub validate_step2()

'validation here that doesn't work due to the postback issue

End Sub

Protected Sub IterateThroughTitle(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In title_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.TextBox") AndAlso c.ID Is Nothing Then
            'DirectCast(c, TextBox).Text = "TextBox_Title " + count.ToString()
            DirectCast(c, TextBox).ID = "TextBox_Title_" + count.ToString()
            DirectCast(c, TextBox).CssClass = "DYN_TextBox"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughTitle(c)
        End If
    Next
End Sub

Protected Sub IterateThroughFirstname(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In firstname_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.TextBox") AndAlso c.ID Is Nothing Then
            'DirectCast(c, TextBox).Text = "TextBox_Firstname " + count.ToString()
            DirectCast(c, TextBox).ID = "TextBox_Firstname_" + count.ToString()
            DirectCast(c, TextBox).CssClass = "DYN_TextBox"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughFirstname(c)
        End If
    Next
End Sub

Protected Sub IterateThroughSurname(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In surname_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.TextBox") AndAlso c.ID Is Nothing Then
            'DirectCast(c, TextBox).Text = "TextBox_Surname " + count.ToString()
            DirectCast(c, TextBox).ID = "TextBox_Surname_" + count.ToString()
            DirectCast(c, TextBox).CssClass = "DYN_TextBox"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughSurname(c)
        End If
    Next
End Sub

Protected Sub IterateThroughTicketNum(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In ticketNum_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.TextBox") AndAlso c.ID Is Nothing Then
            'DirectCast(c, TextBox).Text = "TextBox_TicketNum " + count.ToString()
            DirectCast(c, TextBox).ID = "TextBox_TicketNum_" + count.ToString()
            DirectCast(c, TextBox).CssClass = "DYN_TextBox"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughTicketNum(c)
        End If
    Next
End Sub

Protected Sub IterateThroughBooking(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In booking_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.Label") AndAlso c.ID Is Nothing Then
            DirectCast(c, Label).Text = "Booking #" + count.ToString()
            DirectCast(c, Label).ID = "BookLabel_" + count.ToString()
            DirectCast(c, Label).CssClass = "DYN_Label"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughBooking(c)
        End If
    Next
End Sub

Protected Sub IterateThroughReservation(ByVal parent As Control)
    Dim count As Integer = 1

    For Each c As Control In res_Placeholder.Controls
        If c.[GetType]().ToString().Equals("System.Web.UI.WebControls.Label") AndAlso c.ID Is Nothing Then
            DirectCast(c, Label).Text = "Guest"
            DirectCast(c, Label).ID = "ResLabel_" + count.ToString()
            DirectCast(c, Label).CssClass = "DYN_Label"
            count += 1
        End If

        If c.Controls.Count > 0 Then
            IterateThroughReservation(c)
        End If
    Next
End Sub

Protected Sub Retreive_first_row()

    CType(Me.FindControl("ResLabel_1"), Label).Text() = "You"
    CType(Me.FindControl("TextBox_Title_1"), TextBox).Text() = title_Txt.Text
    CType(Me.FindControl("TextBox_Title_1"), TextBox).Enabled() = False
    CType(Me.FindControl("TextBox_Firstname_1"), TextBox).Text() = first_name_Txt.Text
    CType(Me.FindControl("TextBox_Firstname_1"), TextBox).Enabled() = False
    CType(Me.FindControl("TextBox_Surname_1"), TextBox).Text() = surname_Txt.Text
    CType(Me.FindControl("TextBox_Surname_1"), TextBox).Enabled() = False

End Sub

Protected Sub CreateTextBoxes()

    If Not Page.IsValid Then
        Return
    End If

    Dim n As Integer = number_of_tickets_Ddl.SelectedValue

    For i As Integer = 0 To n - 1
        res_Placeholder.Controls.Add(New Label())
        booking_Placeholder.Controls.Add(New Label())

        title_Placeholder.Controls.Add(New TextBox())
        firstname_Placeholder.Controls.Add(New TextBox())
        surname_Placeholder.Controls.Add(New TextBox())
        ticketNum_Placeholder.Controls.Add(New TextBox())

    Next

    IterateThroughBooking(Me)
    IterateThroughReservation(Me)
    IterateThroughTitle(Me)
    IterateThroughFirstname(Me)
    IterateThroughSurname(Me)
    IterateThroughTicketNum(Me)

    Retreive_first_row()

End Sub

Protected Sub testconn_submit_Btn_Click(ByVal sender As Object, ByVal e As EventArgs) Handles testconn_submit_Btn.Click

    Filldata()

End Sub

Protected Sub next_submit_Btn_Click(ByVal sender As Object, ByVal e As EventArgs) Handles next_submit_Btn.Click

    validate()

    If valid = "fail" Then

    Else

        Step1_Pnl.Visible = False
        Step2_Pnl.Visible = True
        CreateTextBoxes()
        Button1.Attributes.Add("OnClick", "validate_step2(); return false;")
    End If

End Sub

Protected Sub clear_submit_Btn_Click(ByVal sender As Object, ByVal e As EventArgs) Handles clear_submit_Btn.Click

    title_Txt.Text = ""
    first_name_Txt.Text = ""
    surname_Txt.Text = ""
    company_name_Txt.Text = ""
    contact_number_Txt.Text = ""
    contact_email_Txt.Text = ""
    number_of_tickets_Ddl.SelectedValue = "Please select"
    purchase_date_Txt.Text = ""

    title_error_Lbl.Text = "*"
    first_name_error_Lbl.Text = "*"
    surname_error_Lbl.Text = "*"
    comnpany_name_error_Lbl.Text = "*"
    contact_phone_error_Lbl.Text = "*"
    contact_email_error_Lbl.Text = "*"
    number_of_tickets_error_Lbl.Text = "*"
    date_of_purchase_error_Lbl.Text = "*"

End Sub

Protected Sub back_Btn_Click(ByVal sender As Object, ByVal e As EventArgs) Handles back_Btn.Click

    Step1_Pnl.Visible = True
    Step2_Pnl.Visible = False

End Sub

Protected Sub next_submit2_Btn_Click(ByVal sender As Object, ByVal e As EventArgs) Handles next_submit2_Btn.Click

    validate_step2()

End Sub

End Class

回答1:

The first time your user selects an Item in the drop down, you get the count and create dynamic controls. The trick lies in remembering the count for the next PostBack.

You can use the session for this to remember this count.

Now when you receive the second PostBack, you can recreate the controls on Init. If you want these dynamically created controls to receive events, Load is too late.



回答2:

Generally, when creating dynamic controls, one would create the controls in Page_Init and set the event handlers and all of the initial properties (i.e. ID, name, style, text, etc). When a postback occurs, ViewState will be applied AFTER Page_Init (provided that ViewState is turned on).

In this case, I would change to a repeater or some kind of datagrid control and use databinding to populate it. The repeater control can be databound to an array of numbers essentially, repeating your template X times.

Perfect example of doing this with a repeater can be found here: VB.NET Repeater Simple Data Binding Without Datasource