skip to Main Content

I’m trying to come up with a way to loop thru all the controls ( textboxes, check boxes, dropdown lists,etc) on an aspx page. I tried converting some of the solutions given on line for C# to VB but nothing works.

I use the

for all c in me.controls
    id = c.id 
next 

loop but don’t get any of the controls on the page. I have also tried this with the same result:

Sub checkcontrol(ByRef Parent As Control)
    Dim c As Control
    Dim x As Integer = Parent.Controls.Count
    For Each c In Parent.Controls
        If c.GetType.ToString = "ASP.site_master" Then
            checkcontrol(c)
        ElseIf c.GetType() Is GetType(TextBox) Then
            'is it a Text Box?
            Dim t As TextBox = c
            Debug.Print("textBox " & c.ID)
        ElseIf c.GetType() Is GetType(DropDownList) Then
            'is it a dropdown list?
            Dim d As DropDownList = c
            Debug.Print("DropDown Box " & c.ID)
            '   d.ClearSelection()
        ElseIf c.GetType() Is GetType(CheckBox) Then
            Debug.Print("check Box " & c.ID)
        ElseIf c.GetType() Is GetType(RadioButton) Then
            Debug.Print("Radio Button " & c.ID)
        End If
    Next
End sub

The first type I get is "ASP.site_master" since the page is linked to the Site Master.I’m on Visual studio community 2017 and running in the debug mode.

What am I doing wrong?

2

Answers


  1. There could be more layers. Always look for child controls:

    Sub checkcontrol(ByRef Parent As Control)   
        For Each c As Control In Parent.Controls       
            If c.GetType() Is GetType(TextBox) Then
                'is it a Text Box?
                Dim t As TextBox = c
                Debug.Print("textBox " & c.ID)
            ElseIf c.GetType() Is GetType(DropDownList) Then
                'is it a dropdown list?
                Dim d As DropDownList = c
                Debug.Print("DropDown Box " & c.ID)
                '   d.ClearSelection()
            ElseIf c.GetType() Is GetType(CheckBox) Then
                Debug.Print("check Box " & c.ID)
            ElseIf c.GetType() Is GetType(RadioButton) Then
                Debug.Print("Radio Button " & c.ID)
            End If
    
            If c.Controls.Count > 0 Then checkcontrol(c)
        Next
    End Sub
    

    For fun:

    Iterator Function AllControls(ByRef Parent As Control) As IEnumerable(Of Control)
        For Each c As Control In Parent.Controls       
            Yield c
            If c.Controls.Count > 0 Then
                For Eachchild As Control In AllControls(c)
                   Yield child
                Next
            End If
        Next
    End Sub
    

    And then use it like this:

    For Each c As Control In AllControlls(Me)
        Debug.Print($"Type: {c.GetType()}{vbTab}ID: {c.Id}{vbTab}Name: {c.Name}")
    Next
    
    Login or Signup to reply.
  2. Actually, I become super tried of writing code to take a data table row, send to controls on the web page. Let user edit, and then send the values of the controls back to the database. Coming from VBA, vb, vb.net desktop, then it seemed like a silly waste of time to write such code over and over.

    So, I wrote a routine do loop the controls. However, on a given page, I often need more then one section (one set of values might come from one table, and another section comes from another table).

    So, I assume the grouping of controls I want to work with is placed in a div.

    So, say I want to edit a hotel information.

    And I don’t want to wear out my keyboard.

    And I don’t want to write the same code over and over every time I want to edit a record?

    well, we build 2 routines.

    Routine to send a data row to the web page – (but, we use a "div" to control what parts).

    Routine to send web page back to data row.

    And then routine to send data row (data table) back to database.

    The only other part? How do we data bind the controls? Well, we can just out of the blue add any old markup to each control. So, I just use "f" and that means the data column name.

    The end result?

    We actually wind up with a "mini" frame work to now edit data, do so with out messy binding expressions, and do so without messy templates, or anything.

    Just simple code, simple markup, and you now have a full CRUD system.

    Example:

    Simple grid view display some hotels:

        <div id="MyGrid" runat="server" style="width:40%">
            <asp:GridView ID="GHotels" runat="server" AutoGenerateColumns="False" 
                DataKeyNames="ID" CssClass="table">
                <Columns>
                    <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
                    <asp:BoundField DataField="LastName" HeaderText="LastName"    />
                    <asp:BoundField DataField="HotelName" HeaderText="HotelName"  />
                    <asp:BoundField DataField="Description" HeaderText="Description"  />
                    <asp:TemplateField HeaderText="Edit">
                        <ItemTemplate>
                            <asp:Button ID="cmdEdit" runat="server" Text="Edit" CssClass="btn" OnClick="cmdEdit_Click" />
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
            <br />
        </div>
    

    Ok, code to load:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            LoadGrid()
        End If
    
    End Sub
    
    Sub LoadGrid()
    
        Dim cmdSQL As New SqlCommand("SELECT * FROM tblHotelsA ORDER BY HotelName")
        GHotels.DataSource = MyrstP(cmdSQL)
        GHotels.DataBind()
    
    End Sub
    

    And we now have this:

    enter image description here

    So far, nothing new – dead simple. (don’t worry, the helper routines I’ll share in a bit).

    Ok, so, in above we dropped in a plane jane asp.net button into the grid view (don’t bother with the built-in events for the GV – they are more pain then help).

    Now, normally, to add a click even to a button, you just double click on such buttons, but since buttons (or any control) inside of a GV does not allow this?

    Then you use markup, type in onclick="". You get/see this:

    enter image description here

    so, choose create new event. Now, we get to use a simple click event in the gridview (this trick works for all controls – drop downs or whatever). This trick saves us world poverty, and we now get to enjoy and use a simple standard button click event for the GV – no messy on row command and that world poverty’s causing efforts.

    When we click on a row, we will:

    hide the GV, display our div to edit the row. Use our handy page looping controls routine (your current question), and fill out the div.

    So, first our div we drop below the grid – this is our edit div.

    say this:

        <div id="EditRecord" runat="server" style="float:left;display: normal" clientidmode="Static"  >
            <style>
                .iForm label {display:inline-block;width:90px}
                .iForm input {border-radius:8px;border-width:1px;margin-bottom:10px}                
                .iForm textarea {border-radius:8px;border-width:1px;margin-bottom:10px}     
                .iForm input[type=checkbox] {margin-right:8px}
            </style>
    
            <div style="float:left" class="iForm">
                    <label>HotelName</label><asp:TextBox ID="txtHotel" runat="server" f="HOtelName" width="280"></asp:TextBox> <br />
                    <label>First Name</label><asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140"></asp:TextBox> <br />
                    <label>Last Name</label><asp:TextBox ID="tLN" runat="server" f="LastName" Width="140"></asp:TextBox> <br />
                    <label>City</label><asp:TextBox ID="tCity" runat="server" f="City" Width="140"></asp:TextBox> <br />
                    <label>Province</label><asp:TextBox ID="tProvince" runat="server" f="Province" Width="75"></asp:TextBox> <br />
            </div>
            <div style="float:left;margin-left:20px" class="iForm">
                <label>Description</label> <br />
                <asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine" 
                    Height="150px" f="Description" ></asp:TextBox> <br />
                <asp:CheckBox ID="chkActive" f="Active" Text=" Active" runat="server" TextAlign="Right" />
                <asp:CheckBox ID="chkBalcony" f="Balcony" Text=" Has Balcony" runat="server" TextAlign="Right" />
            </div>
            <div style="clear:both"></div>
            <button id="cmdSave" runat="server" class="btn" onserverclick="cmdSave_ServerClick" >
                <span aria-hidden="true" class="glyphicon glyphicon-floppy-saved"> Save</span> 
            </button>
    
            <button id="cmdCancel" runat="server" class="btn" style="margin-left:15px"
                onserverclick="cmdCancel_ServerClick"
                >
                <span aria-hidden="true" class="glyphicon glyphicon-arrow-left"> Back/Cancel</span>
            </button>
    
            <button id="cmdDelete" runat="server" class="btn" style="margin-left:15px">
                <span aria-hidden="true" class="glyphicon glyphicon-trash"> Delete</span>
            </button>
       </div>
    

    Now, in above, I decided to use just a html button, and the reason why is EASE in which to add icons to the buttons. (but, plane jane asp.net buttons could also have been used).

    Noting really special in above EXCEPT for our adding of "f". That’s the data base column mapping!!! Really simple, and a gazillion times less work then say using a databound object like repeater or whatever.

    So, now the button code click:

    Protected Sub cmdEdit_Click(sender As Object, e As EventArgs)
    
        Dim btn As Button = sender
        Dim gRow As GridViewRow = btn.NamingContainer
        Dim pkID = GHotels.DataKeys(gRow.RowIndex).Item("ID")
    
        Dim cmdSQL As New SqlCommand("SELECT * from tblHotelsA where ID = @ID")
        cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = pkID
        Dim rstData As DataTable = MyrstP(cmdSQL)
    
        Call fLoader(Me.EditRecord, rstData.Rows(0)) ' load up hidden div with data
        ' hide grid
        MyGrid.Style.Add("display", "none")
        EditRecord.Style.Add("display", "normal")
        ViewState("rstData") = rstData
    
    End Sub
    

    Once again, the above code – very simple, very plain.

    We now see this when I hit edit on a given row:

    enter image description here

    And the save button code:

    Protected Sub cmdSave_ServerClick(sender As Object, e As EventArgs)
    
        Dim rstData As DataTable = ViewState("rstData")
        Call fWriterW(EditRecord, rstData.Rows(0))  ' div to table
        Call SaveTable(rstData, "tblHotelsA")  ' send table back to database
    
        LoadGrid()    ' refresh grid
        MyGrid.Style.Add("display", "normal")
        EditRecord.Style.Add("display", "none")
    
    
    End Sub
    

    Again, note how simple the code is. note that I COULD HAVE combined the two routines into one (fwriterW, and SaveTable). but, often after we pull data from web form back to table, we might want to do some additonal process or code on the data BEFORE we save.

    So, now the looping code. We have two such routines:

    data to web page

    web page to data table.

    So, this is the looping controls part:

    first the loader:

    Public Sub fLoader(F As HtmlGenericControl, rst As DataRow)
    
        For Each c As System.Web.UI.Control In F.Controls
            Select Case c.GetType
                Case GetType(TextBox)
                    Dim ctlC As TextBox = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                        End If
                    End If
                Case GetType(Label)
                    Dim ctlC As Label = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                        End If
                    End If
                Case GetType(DropDownList)
                    Dim ctlC As DropDownList = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            ctlC.Text = IIf(IsDBNull(rst(ctlC.Attributes("f"))), "", rst(ctlC.Attributes("f")))
                        End If
                    End If
                Case GetType(CheckBox)
                    Dim ctlC As CheckBox = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            ctlC.Checked = rst(ctlC.Attributes("f"))
                        End If
                    End If
                Case GetType(RadioButtonList)
                    Dim ctlC As RadioButtonList = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            ctlC.SelectedValue = rst(ctlC.Attributes("f"))
                        End If
                    End If
            End Select
        Next
    
    End Sub
    

    You can over time add a few more control types if you use them. So, note how the above loops the controls, fills them based on "f" attribute setting.

    And in my global set of routines, we have also

    Public Sub fWriterW(f As HtmlGenericControl, rst As DataRow)
    
        For Each c As System.Web.UI.Control In f.Controls
            Select Case c.GetType
                Case GetType(TextBox)
                    Dim ctlC As TextBox = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                        End If
                    End If
                Case GetType(Label)
                    Dim ctlC As Label = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                        End If
                    End If
                Case GetType(DropDownList)
                    Dim ctlC As DropDownList = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            rst(ctlC.Attributes("f")) = IIf(ctlC.Text = "", DBNull.Value, ctlC.Text)
                        End If
                    End If
                Case GetType(CheckBox)
                    Dim ctlC As CheckBox = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            rst(ctlC.Attributes("f")) = ctlC.Checked
                        End If
                    End If
    
                Case GetType(RadioButtonList)
                    Dim ctlC As RadioButtonList = c
                    If Not ctlC.Attributes("f") Is Nothing Then
                        If rst.Table.Columns.Contains(ctlC.Attributes("f")) Then
                            rst(ctlC.Attributes("f")) = ctlC.SelectedValue
                        End If
                    End If
    
    
            End Select
        Next
    
    End Sub
    

    This is really just the reverse of the first loop.

    So, we pass these routines a "div" (with id, and runat server always).

    So the only other two helper routines are:

    Public Function MyrstP(sqlCmd As SqlCommand) As DataTable
    
        Dim rstData As New DataTable
        Using sqlCmd
            Using conn = New SqlConnection(My.Settings.TEST4)
                conn.Open()
                sqlCmd.Connection = conn
                rstData.Load(sqlCmd.ExecuteReader)
            End Using
        End Using
        Return rstData
    
    End Function
    

    Again, I use the above everyone – in all my code. Again, super easy.

    And then the code to write the table back:

    Sub SaveTable(rstData As DataTable, strTable As String)
    
        Using conn As New SqlConnection(My.Settings.TEST4)
            Using cmdSQL As New SqlCommand("select * FROM " & strTable, conn)
                Dim da As New SqlDataAdapter(cmdSQL)
                Dim daU As New SqlCommandBuilder(da)
                conn.Open()
                da.Update(rstData)
            End Using
        End Using
    
    End Sub
    

    So, that’s about it.

    The above in fact is a whole re-useable frame work. It allows you now re-use the above code – allows easy edit of any data. And you not re-writing the same code over and over.

    Adopting above code quite gives you true RAD development, and great ease of editing records in web forms – in fact it becomes as easy as desktop software.

    So, in most cases looping every control on the page is not all so useful. But, placing controls in a "div" (with id, and runat=server) now allows you to pass THAT single div. It also means that you can have multiple parts on a page – maybe a table of billing address and customer record.

    and all of the above just simple works – including when you have a master page.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search