Dynamically remove and rename controls

Here's a tough one (I think)...

Imagine that you have 5 comboboxes that are dynamically added to the screen with a static one at the top (meaning it's there at design time). So that's 6 comboboxes total. Let's say I want to remove combobox number 4 (the second to last one). For my purposes I need to somehow shift number 5 to replace number 4, including its name and all that positioning jazz. I did some simple code on it and decided on this, although I'm not sure it's the simplest solution:

    Public Function FindControl(ByVal name As String) As Control
        Dim ret As Control
        Dim ctl As Control
        For Each ctl In Me.Controls
            If ctl.Name = name Then
                ret = ctl
                Exit For
            End If
        Next
        If Not ret Is Nothing Then
            Return ret
        End If
    End Function

    Private Sub picDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) 'Dynamic delete button that has the row number as the last digit(s) in its name
        Dim row As Integer = CType(CType(sender, PictureBox).Name.Replace("picDelete", ""), Integer) 'Extract the row number
        Dim cboFrom As ComboBox = CType(FindControl("cboFrom" & row), ComboBox)
        Dim cboTo As ComboBox = CType(FindControl("cboTo" & row), ComboBox)
        Dim txtAmount As TextBox = CType(FindControl("txtAmount" & row), TextBox)
        If row = 1 And numberRows = 1 Then
            cboFrom1.SelectedIndex = 0
            cboTo1.SelectedIndex = 0
            txtAmount1.Text = "$0.00"
        Else
            RenumberItems(row)
        End If
    End Sub

    Public Sub RenumberItems(ByVal start As Integer)
        Dim i As Integer = start
        Dim newNumberRows As Integer = numberRows
        'Example : start = 3
        'Takes contents of row 4 and puts them in row 3 and then puts row 5 contents in row 4, etc.
        For i = start To numberRows - 1
            Dim cboFrom As ComboBox = CType(FindControl("cboFrom" & i), ComboBox)
            Dim cboTo As ComboBox = CType(FindControl("cboTo" & i), ComboBox)
            Dim txtAmount As TextBox = CType(FindControl("txtAmount" & i), TextBox)
            If i = 1 Then
                cboFrom = cboFrom1
                cboTo = cboTo1
                txtAmount = txtAmount1
            End If
            Dim cboFromNext As ComboBox = CType(FindControl("cboFrom" & i + 1), ComboBox)
            Dim cboToNext As ComboBox = CType(FindControl("cboTo" & i + 1), ComboBox)
            Dim txtAmountNext As TextBox = CType(FindControl("txtAmount" & i + 1), TextBox)
            cboFrom.SelectedIndex = cboFromNext.SelectedIndex
            cboTo.SelectedIndex = cboToNext.SelectedIndex
            txtAmount.Text = txtAmountNext.Text
            newNumberRows -= 1
        Next
        'This is the last row on the screen. It will be removed
        Dim cboFromDelete As ComboBox = CType(FindControl("cboFrom" & numberRows), ComboBox)
        Dim cboToDelete As ComboBox = CType(FindControl("cboTo" & numberRows), ComboBox)
        Dim txtAmountDelete As TextBox = CType(FindControl("txtAmount" & numberRows), TextBox)
        Dim picDeleteDelete As PictureBox = CType(FindControl("picDelete" & numberRows), PictureBox)
        Me.Controls.Remove(cboFromDelete)
        Me.Controls.Remove(cboToDelete)
        Me.Controls.Remove(txtAmountDelete)
        Me.Controls.Remove(picDeleteDelete)
        numberRows = newNumberRows
        GenerateItems()
    End Sub

Phew....any ideas on how to make this better? Can it be done easier?
LVL 7
wtconwayAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Harisha M GEngineerCommented:
Hi wtconway,
   
Try something like this (It is not the exact code)...

Private Sub DeleteControl(ByVal CtrlNo As Integer)
    Dim i As Integer
    For i = CtrlNo To UBound(CtrlArray) - 1
        CtrlArray(i).Caption = CtrlArray(i+1).Caption
        CtrlArray(i).Top = CtrlArray(i+1).Top
        '... Others if you want      
    Next
    CtrlArray(UBound(CtrlArray)).Delete

End Sub

Bye
---
Harish
muthiahmerchantCommented:
Dim cmb(3) As ComboBox

For j = 0 To 3
      cmb(i) = New ComboBox()

      cmb(i).Text =
      cmb(i).Width = 88
      cmb(i).Top = intTop
      cmb(i).Left = intLeft
      cmb(i, j).Height = 23
      cmb(i).BorderStyle = BorderStyle.FixedSingle
      cmb(i).TextAlign = ContentAlignment.MiddleCenter

      me.Controls.Add(cmb(i))
Next

dynamically add combo boxes and position them as above.


instead of deleting a combo box and replacing the name with a preivous one
you could set the visible property to false for combobox4
and change the position of combobox 5 to the position of combobox4

if you must change the name then change combobox.name = tmp
and then combox5.name = combobox4's name
SanclerCommented:
I am not sure that I am fully understanding, so perhaps this is too simplistic.  

If you use a single panel for each of your "rows" you can put all the controls for that "row" in that panel.  Then, when you want to delete a "row" you just delete that panel - it takes all its contained controls with it - and reposition the lower panels to fill the gap by decreasing their .Top property by the height of the panel that has been removed.

Or am I missing the point?

Roger
Exploring ASP.NET Core: Fundamentals

Learn to build web apps and services, IoT apps, and mobile backends by covering the fundamentals of ASP.NET Core and  exploring the core foundations for app libraries.

wtconwayAuthor Commented:
Here's a little bit of a visual for you:

http://homeoffice.quikpawn.com/it/images/fts.jpg

You'll see in that image what I'm talking about. I like the panel idea but would it be just as easy to reference the controls inside the panel as it is if they're not.

For example:

Dim pnl as Panel = panel1
Msgbox(pnl.cboFrom1.Text)

I would rather just say...
Msgbox(cboFrom.Text) when the control is really inside panel1

Check out the image.
SanclerCommented:
Have a look at this.  It's fairly rudimentary, but it illustrates the idea.  It consists of a UserControl that is more or less equivalent to one of your "rows", and a form containing code to add, remove and get values from the specific controls in each "row".  It does not rename these various sub-controls, but you can access them through the unique rows.  An ArrayList keeps count of what rows are showing and maintains its order so that - although after a deletion or so - the row in position 3 might in fact be named Row5 - it will be in the third position in the ArrayList.

By the way, this is Windows.Forms code.  I really know nothing about Web stuff, so the approach may not be viable at all, or details may need changing, in that context.

======

Public Class cboRow
    Inherits System.Windows.Forms.UserControl

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'UserControl overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Panel1 As System.Windows.Forms.Panel
    Friend WithEvents txtAmt As System.Windows.Forms.TextBox
    Friend WithEvents cboTo As System.Windows.Forms.ComboBox
    Friend WithEvents cboFrom As System.Windows.Forms.ComboBox
    Friend WithEvents btnDelete As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Panel1 = New System.Windows.Forms.Panel
        Me.txtAmt = New System.Windows.Forms.TextBox
        Me.cboTo = New System.Windows.Forms.ComboBox
        Me.cboFrom = New System.Windows.Forms.ComboBox
        Me.btnDelete = New System.Windows.Forms.Button
        Me.Panel1.SuspendLayout()
        Me.SuspendLayout()
        '
        'Panel1
        '
        Me.Panel1.Controls.Add(Me.btnDelete)
        Me.Panel1.Controls.Add(Me.txtAmt)
        Me.Panel1.Controls.Add(Me.cboTo)
        Me.Panel1.Controls.Add(Me.cboFrom)
        Me.Panel1.Dock = System.Windows.Forms.DockStyle.Top
        Me.Panel1.Location = New System.Drawing.Point(0, 0)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(584, 40)
        Me.Panel1.TabIndex = 1
        '
        'txtAmt
        '
        Me.txtAmt.Location = New System.Drawing.Point(384, 8)
        Me.txtAmt.Name = "txtAmt"
        Me.txtAmt.Size = New System.Drawing.Size(152, 20)
        Me.txtAmt.TabIndex = 2
        Me.txtAmt.Text = "txtAmt"
        '
        'cboTo
        '
        Me.cboTo.Location = New System.Drawing.Point(184, 8)
        Me.cboTo.Name = "cboTo"
        Me.cboTo.Size = New System.Drawing.Size(184, 21)
        Me.cboTo.TabIndex = 1
        Me.cboTo.Text = "cboTo"
        '
        'cboFrom
        '
        Me.cboFrom.Location = New System.Drawing.Point(8, 8)
        Me.cboFrom.Name = "cboFrom"
        Me.cboFrom.Size = New System.Drawing.Size(160, 21)
        Me.cboFrom.TabIndex = 0
        Me.cboFrom.Text = "cboFrom"
        '
        'btnDelete
        '
        Me.btnDelete.Location = New System.Drawing.Point(552, 8)
        Me.btnDelete.Name = "btnDelete"
        Me.btnDelete.Size = New System.Drawing.Size(24, 24)
        Me.btnDelete.TabIndex = 3
        Me.btnDelete.Text = "X"
        '
        'cboRow
        '
        Me.Controls.Add(Me.Panel1)
        Me.Name = "cboRow"
        Me.Size = New System.Drawing.Size(584, 40)
        Me.Panel1.ResumeLayout(False)
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private m_cmbFromText As String
    Private m_cmbFromValue As Integer
    Private m_cmbToText As Integer
    Private m_cmbToValue As Integer
    Private m_Amount As String 'or Decimal, or Currency

    Public Event DeleteButtonPressed(ByVal sender As cboRow)
    Public Event ValueChanged(ByVal sender As cboRow)

    Public Property cmbFromText() As String
        Get
            Return m_cmbFromText
        End Get
        Set(ByVal Value As String)
            'code to check if value is permissible?
            m_cmbFromText = Value
            'code to adjust m_cmbFromValue?
            'code to adjust selection in combo box?
        End Set
    End Property

    Public Property cmbFromValue() As Integer
        Get
            Return m_cmbFromValue
        End Get
        Set(ByVal Value As Integer)
            'code to check if value is permissible?
            m_cmbFromValue = Value
            'code to adjust m_cmbFromText?
            'code to adjust selection in combo box?
        End Set
    End Property

    Public Property cmbToText() As String
        Get
            Return m_cmbToText
        End Get
        Set(ByVal Value As String)
            'code to check if value is permissible?
            m_cmbToText = Value
            'code to adjust m_cmbFromValue?
        End Set
    End Property

    Public Property cmbToValue() As Integer
        Get
            Return m_cmbToValue
        End Get
        Set(ByVal Value As Integer)
            'code to check if value is permissible?
            m_cmbToValue = Value
            'code to adjust m_cmbToText?
            'code to adjust selection in combo box?
        End Set
    End Property

    Public Property Amount() As String 'or Decimal or Currency
        Get
            Return m_Amount
        End Get
        Set(ByVal Value As String)
            'code to check if value is permissible?
            m_Amount = Value
        End Set
    End Property

    Private Sub cboRow_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'code to fill comboboxes and set txtAmt.Text
    End Sub

    Private Sub cboFrom_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboFrom.SelectedIndexChanged
        'code to put new values in m_cmbFromText and m_cmbFromValue
        RaiseEvent ValueChanged(Me)
    End Sub

    Private Sub cboTo_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboTo.SelectedIndexChanged
        'code to put new values in m_cmbToText and m_cmbToValue
        RaiseEvent ValueChanged(Me)
    End Sub

    Private Sub txtAmt_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtAmt.Leave
        'code to validate entry and, if OK, to put new value in m_Amount
        RaiseEvent ValueChanged(Me)
    End Sub

    Private Sub btnDelete_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDelete.Click
        RaiseEvent DeleteButtonPressed(Me)
    End Sub
End Class

==========

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents btnAdd As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.btnAdd = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'btnAdd
        '
        Me.btnAdd.Location = New System.Drawing.Point(24, 256)
        Me.btnAdd.Name = "btnAdd"
        Me.btnAdd.Size = New System.Drawing.Size(112, 32)
        Me.btnAdd.TabIndex = 4
        Me.btnAdd.Text = "Add"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(600, 302)
        Me.Controls.Add(Me.btnAdd)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private myRows As New ArrayList
    Private rowCounter As Integer
    Private lastTop As Integer = 20 'probably need something for a top margin

    Private Sub AddRow()
        Dim myrow As cboRow = New cboRow
        myrow.Name = "Row" & rowCounter.ToString
        rowCounter += 1
        myRows.Add(myrow)
        myRow.Top = lastTop
        myRow.Left = 0
        lastTop += myRow.Height
        Me.Controls.Add(myrow)
        AddHandler myrow.DeleteButtonPressed, AddressOf deleteRow
        AddHandler myrow.ValueChanged, AddressOf getValues
    End Sub

    Private Sub btnAdd_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAdd.Click
        AddRow()
    End Sub

    Private Sub getValues(ByVal sender As cboRow)
        'code to deal with changed entries,
        'or just to read values by row
        'values are accessible by
        Dim cmbfromtext As String = sender.cmbFromText
        Console.WriteLine(sender.Name & " cmbfromtext = " & cmbfromtext)
        'etc
    End Sub

    Private Sub getAllValues()
        Dim myrow As cboRow
        For Each myrow In myRows
            getvalues(myrow)
        Next
    End Sub

    Private Sub deleteRow(ByVal sender As cboRow)
        'may need code to confirm OK to delete
        myRows.Remove(sender)
        Me.Controls.Remove(sender)
        Dim myrow As cboRow
        lastTop = 20
        For Each myrow In myRows
            myrow.Top = lastTop
            myrow.Left = 0
            lastTop += myrow.Height
        Next
    End Sub

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
        AddRow()
    End Sub

End Class

======

Roger

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
wtconwayAuthor Commented:
Roger,

I really like the code example you gave me.  I never considered writing my own class that works alot like a windows control; that really is a great idea. Thank you so much for the assistance. If you ever need anything, don't hesitate to shoot me an email.
wtconwayAuthor Commented:
Roger,

Please take a look at a question I have that relates to this one:

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21481453.html

Thank you.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic.NET

From novice to tech pro — start learning today.