Domanda

I am new to asp.net and vb.net programming and i can't find a answer to my problem. I have dynamically generated checkboxes in a loop at runtime within a sub.

This is a grid scheduler program that displays selected day's and selected hours from a location which is selected from a different page. I want to acces the checkboxes by id but i cant get acces to them because the checkboxes are not declared at class level.

Can anyone help me please, i have searched all day long for a solution. I Prefer VB but C# is fine also.

Below is my codebehind

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    If Not IsPostBack Then
      BindLocationDayTime()
   End If
End Sub

Public Sub BindLocationDayTime()
        Dim ID As Integer
        Dim Name As String
        Dim Day As Integer
        Dim Time As Integer
        Dim StartDate As DateTime
        Dim EndDate As DateTime

        Dim Locations As SqlDataReader = GetLocations()

        For Each Item In Locations

            Dim LRow As New TableRow()
            Dim LCell As New TableCell()
            LCell.Text = Locations.Item("Name")
            LCell.Attributes.Add("class", "LocationHeader")
            LCell.Attributes.Add("colspan", "5")
            LRow.Cells.Add(LCell)
            LocationData.Rows.Add(LRow)

            Dim Location As SqlDataReader = GetLocation(Convert.ToInt32(Locations.Item("Id")))

            While Location.Read()
                Name = Location("Name").ToString()
                StartDate = Location("StartDate")
                EndDate = Location("EndDate")
            End While

            Dim dtfi As Globalization.DateTimeFormatInfo = Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat
            Dim tRowCount As Integer = 0

            Do While StartDate <= EndDate
                Dim LocationDayTime As SqlDataReader = GetPlayDayTime(Convert.ToInt32(Locations.Item("Id")))

                For Each row In LocationDayTime
                    Day = LocationDayTime.Item("DayID")
                    Time = LocationDayTime.Item("TimeID")
                    ID = Locations.Item("Id")
                    If Day = 1 Then
                        If StartDate.DayOfWeek = DayOfWeek.Monday Then
                            BindDays(StartDate, ID, tRowCount, Time)
                            tRowCount = tRowCount + 1
                        End If
                    ElseIf Day = 2 Then
                        If StartDate.DayOfWeek = DayOfWeek.Tuesday Then
                            BindDays(StartDate, ID, tRowCount, Time)
                            tRowCount = tRowCount + 1
                        End If
                    ElseIf Day = 3 Then
                        If StartDate.DayOfWeek = DayOfWeek.Wednesday Then
                            BindDays(StartDate, ID, tRowCount, Time)
                            tRowCount = tRowCount + 1
                        End If
                    ElseIf Day = 4 Then
                        If StartDate.DayOfWeek = DayOfWeek.Thursday Then
                            BindDays(StartDate, ID, tRowCount, Time)
                            tRowCount = tRowCount + 1
                        End If
                    ElseIf Day = 5 Then
                        If StartDate.DayOfWeek = DayOfWeek.Friday Then
                            BindDays(StartDate, ID, tRowCount, Time)
                            tRowCount = tRowCount + 1
                        End If
                    End If
                Next
                StartDate = StartDate.AddDays(1)
            Loop
        Next
    End Sub

Public Sub BindDays(ByVal StartDate As DateTime, ByVal ID As Integer, ByVal tRowCount As Integer, ByVal Time As Integer)
        Dim dtfi As Globalization.DateTimeFormatInfo = Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat
        Dim tRow As New TableRow()

        Dim Cell1 As New TableCell()
        Dim strDayOfWeek As String = dtfi.GetDayName(StartDate.DayOfWeek)
        Cell1.Text = UppercaseFirstLetter(strDayOfWeek & " ") & (StartDate.Date.ToShortDateString & " om ") & (Time & "uur ")
        Cell1.Attributes.Add("class", "MemberCell")
        tRow.Cells.Add(Cell1)

        Dim Cell2 As New TableCell()
        Dim cbAvailible As New CheckBox()
        cbAvailible.ID = (StartDate.Date) & "," & (Time)
        cbAvailible.Checked = False
        Cell2.Controls.Add(cbAvailible)
        tRow.Cells.Add(Cell2)

        Dim Cell3 As New TableCell()
        Dim Label As New Label()
        Label.Text = ("(Op deze datum ben ik verhinderd)")
        Cell3.Controls.Add(Label)
        tRow.Cells.Add(Cell3)

        If tRowCount Mod 2 Then
            tRow.Attributes.Add("class", "alternatingItemStyle")
        Else
            tRow.Attributes.Add("class", "itemStyle")
        End If

        LocationData.Rows.Add(tRow)

    End Sub

#End Region

#Region " Events "

Private Sub Insert_Click(sender As Object, e As EventArgs) Handles Insert.Click
' I want to get here al the checkbox id and insert the values to a databse
End Sub

End Region
È stato utile?

Soluzione

Solution 1 - Using recursive control search

I have done something similar with a dynamically generated form with a variable number of controls (textboxes, dropdowns, and checkboxes) and control state being data driven. I would not be looking to tie into the events of the generated controls (would require a jerky postback) but have a "Save" button and from that event do a recursive GetChildControls function that starts from the container holding your dynamic controls. Have a convention when assigning an id for each dynamic control so when you later loop back through them you can know which control is related to which record.

The recursive function:

Public Class ControlUtils
    Shared Function GetChildControls(ByVal ctrl As Control, Optional ByVal ctrlType As Type = Nothing) As Control()
        Dim controls As New ArrayList()
        For Each c As Control In ctrl.Controls
            ' add this control and all its nested controls
            If ctrlType Is Nothing OrElse ctrlType.IsAssignableFrom(c.GetType()) Then
                controls.Add(c)
                controls.AddRange(GetChildControls(c))
            End If
        Next
        ' return the result as an array of Controls
        Return DirectCast(controls.ToArray(GetType(Control)), Control())
    End Function
End Class

Basic idea with a contrived dynamic form...

A class to represent the database info:

Public Class Location
    Public Property ID As Integer
    Public Property Name As String
    Public Property StartDate As Date
    Public Property EndDate As Date

    Shared Function GetSampleLocations() As List(Of Location)
        Dim sample As New List(Of Location)
        Dim loc As Location
        For j = 1 To 5
            loc = New Location
            loc.ID = j
            loc.Name = "Location " & j
            loc.StartDate = Date.Today
            loc.EndDate = Date.Today.AddDays(6 - j)
            sample.Add(loc)
        Next
        Return sample
    End Function
End Class

The class that has the methods to build the "form" and save its data:

Public Class LocationsDynamicForm

    Dim _Locations As IEnumerable(Of Location)

    Sub New(locations As IEnumerable(Of Location))
        _Locations = locations
    End Sub

    Sub InsertEditForm(plc As PlaceHolder, setUserInput As Boolean)
        'build and add controls to placeholder
        Dim tbl As New Table
        Dim r As TableRow
        Dim c As TableCell
        For Each loc As Location In _Locations
            r = New TableRow

            'add cell for location name
            c = New TableCell
            c.Controls.Add(New LiteralControl(loc.Name)) 'add plain text through literal control
            r.Cells.Add(c)

            'add cell for each day in the date range for current location
            Dim currentDate As Date = loc.StartDate
            Do Until currentDate > loc.EndDate
                c = New TableCell
                Dim chk As New CheckBox
                chk.ID = "chkLocationDate_" & loc.ID & "_" & currentDate.Ticks
                chk.Text = currentDate.ToShortDateString
                If setUserInput Then
                    'set the check state based on current database value
                    Dim pretendValueCameFromDB As Boolean = True
                    chk.Checked = pretendValueCameFromDB
                End If
                c.Controls.Add(chk)
                r.Cells.Add(c)
                currentDate = currentDate.AddDays(1)
            Loop

            tbl.Rows.Add(r)
        Next
        plc.Controls.Add(tbl)
    End Sub

    Sub SaveForm(ByVal plc As PlaceHolder)
        Dim ctl As Control
        Dim controlIDParts() As String
        Dim drp As DropDownList
        Dim txt As TextBox
        Dim chk As CheckBox
        For Each ctl In ControlUtils.GetChildControls(plc, GetType(Control))
            If ctl.GetType Is GetType(DropDownList) Then
                drp = CType(ctl, DropDownList)
                If drp.ID Like "drpIT_*" Then
                    controlIDParts = drp.ID.Split("_")
                    'update record...
                End If
            ElseIf ctl.GetType Is GetType(TextBox) Then
                txt = CType(ctl, TextBox)
                If txt.ID Like "txtIT_*" Then
                    controlIDParts = txt.ID.Split("_")
                    'update record...
                End If
            ElseIf ctl.GetType Is GetType(CheckBox) Then
                chk = CType(ctl, CheckBox)
                If chk.ID Like "chkLocationDate_*" Then
                    controlIDParts = chk.ID.Split("_")
                    Dim locationID = controlIDParts(1)
                    Dim ticks As Long = Val(controlIDParts(2))
                    Dim d As New Date(ticks)
                    'update record...
                End If
            End If
        Next
        'commit record changes...
    End Sub

End Class

And its use inside the webform (assuming you have a save button and placeholder control):

Dim _Locations As List(Of Location)
Dim _LocationsForm As LocationsDynamicForm

Protected Sub Page_Init(sender As Object, e As EventArgs) Handles Me.Init
    _Locations = Location.GetSampleLocations()
    _LocationsForm = New LocationsDynamicForm(_Locations)
    _LocationsForm.InsertEditForm(plcLocations, Not Me.IsPostBack)
End Sub

Protected Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
    _LocationsForm.SaveForm(plcLocations)
End Sub

Solution 2 - Use AddHandler with Dynamically added controls

This is closer to what you want, but requires a postback on each checkbox change. In your BindDays routine add these lines when you are adding the checkbox.

cbAvailible.AutoPostBack = True
AddHandler cbAvailible.CheckedChanged, AddressOf Insert_Click

You should trim the Handles keyword at the end of your sub Insert_Click signature. The Handles is nice when you have foreknowledge of the control(s) you will be handling but the controls don't exist at design time.

Private Sub Insert_Click(sender As Object, e As EventArgs)
    ' I want to get here al the checkbox id and insert the values to a databse
    Dim chk As CheckBox = CType(sender, CheckBox)
    Label1.Text = "ID = " & chk.ID & ", Checked = " & chk.Checked
End Sub

I'm not sure how you are persisting your 'LocationData' across postbacks or adding it to the web page but I was able to get a modified version of your code working.

Altri suggerimenti

' Global declaration inside the "Form" class.
Public Checkboxes as New List(of Checkbox)

Each time you create a "new checkbox" add it to the collection.

...
Dim cbAvailible As New CheckBox()
Checkboxes.Add(cbAvailable)
...

Later you can simply refer to the checkbox by Index.

Dim chk as boolean = Checkboxes(2).checked ' example

The other Alternative is to use a Generic.Dictionary to store the checkboxes, in that case each box can have a "Key" like a string that relates to the row or something specific.

Looping through Checkboxes.

For iQ AS integer = 0 to Checkboxes.Count -1
    Dim cb as checkbox = Checkboxes(iq) ' just a way to not use long name during operations.
    Dim checked as boolean = cb.checked ' ... ' do your work here
    ' ...
Next iQ

Odds are you will need to do the same with all your objects (per row).

The Last Index should be the same for all of them. Which should be the same as the number of rows in your table object as well.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top