0

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
Agamemnon
  • 587
  • 2
  • 15
  • 44
  • You can have an existing, hidden, control to tell you how many dynamic controls you need to create. You can also consider changing the UI so you don't have to create dynamic controls in this manner. – Oded Aug 17 '12 at 10:54
  • I originally had a very simple grid of controls with 10 rows where only there visbility was changed. This look very cumbersome and I thought going 'dynamic' just had to be better. – Agamemnon Aug 17 '12 at 11:15

2 Answers2

1

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

Community
  • 1
  • 1
Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
  • It seems I am still not quite 'getting it'. If I put the code that generates the controls in the Page_Init that basically says - create a loop from 1 to whatever is in the DDL and then make that many rows of controls, this will happen before the DDL is even selected, how will it 'know' how many rows to create? – Agamemnon Aug 17 '12 at 11:09
  • I see your problem... switch to a repeater control – Chris Gessler Aug 17 '12 at 11:20
  • I have no experience of repeaters at all, are you able to shed any light on them and their use? – Agamemnon Aug 17 '12 at 11:29
  • I have read that post a couple of times and can't see how I could get that method to work with what I need. – Agamemnon Aug 20 '12 at 13:52
0

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.

nunespascal
  • 17,584
  • 2
  • 43
  • 46
  • Would this effectively 'destroy' the controls but then recreate new ones based on the number that created them originally? If so that makes sense, but how do I also retain the controls' values? – Agamemnon Aug 17 '12 at 11:22
  • Http is stateless. The controls are always created on every request. Asp.net populates the values back when it loads the `ViewState`. If you generate the same IDs that you used the first time, asp.net will do the rest. – nunespascal Aug 17 '12 at 11:24
  • @Ryan - ViewState is applied AFTER Page_Init and before Page_Load. You have to create the exact same controls as before, otherwise you'll receive "ViewState cannot be applied" errors. – Chris Gessler Aug 17 '12 at 11:26
  • Ok just to see if I'm getting this: The controls are created, values are entered and assigned to the controls' IDs. Controls are destroyed and new controls are created with IDs that match the previous ones. The values still exist and still relate to IDs that the new controls have and so the values are retreivable? In response to the above comment: How would the page know how many controls to make? This is decided after the page has loaded, by the user. – Agamemnon Aug 17 '12 at 11:29
  • I assume then, that on every postback I would have to rebuild the grid of controls and then retreive the values from veiwstate? If so ('noob question') how do I save the values to the viewstate? – Agamemnon Aug 17 '12 at 11:39
  • No, you only have to create and add the controls. Each asp.net control will retrieve the its values from the `ViewState`. Saving to `ViewState` too is also a job on the control itself. Not for you to worry about. – nunespascal Aug 17 '12 at 11:44
  • When I put the code in to 'rebuild' the controls I receive an error telling me basically that I cannot have multiple controls using the same IDs. What would be the correct way to do this? – Agamemnon Aug 20 '12 at 13:51
  • If there are multiple controls with the same name, you can use a counter. IDs must be unique strings for each control. – nunespascal Aug 20 '12 at 19:31