Dynamically assign reference of a control - vb.net

I have a desktop application where I have many textboxes inside a tab control and four tabs, as each of the tabs is exact copy.
Currently I'm assigning line by line controls to corresponding control array so I can easily access control of active tab. Each tab represents a shopping basket. And I have four tabs for now. lvBasket1 is on the fist tab, lvBasket2 is on the second etc...
Part of my code:
Private Sub InitControlsArrayAndEventHandlers()
lvBasket(0) = lvBasket1
lvBasket(1) = lvBasket2
lvBasket(2) = lvBasket3
lvBasket(3) = lvBasket4
btnSave(0) = btnSave1
btnSave(1) = btnSave2
btnSave(2) = btnSave3
btnSave(3) = btnSave4
For i As Integer = 0 To 3
AddHandler lvBasket(i).MouseDoubleClick, AddressOf lvBasket_MouseDoubleClick
AddHandler btnSave(i).Click, AddressOf btnSave_Click
Next
End Sub
Question is; Is it possible somehow to assign control reference to its array, inside the for loop. Like eval in javascript:
lvBasket(i) = Eval("lvBasket" & i)
btnSave(i) = Eval("btnSave" & i)

It looks like you already have 3 references to the controls:
The one in the controls collection
the one in the array
Possibly the lvBasketN variables shown
Since it is pretty easy to get a control from the controls collection, you really do not need a separate collection of them. To hook up a set of newly added controls to event handlers (given the name, I am assuming Listviews):
For Each lv As ListView In TabPage8.Controls.OfType(Of ListView)()
AddHandler lv.MouseDoubleClick, AddressOf lv_MouseDoubleClick
Next
And I have four tabs for now. lvBasket1 is on the fist tab, lvBasket2 is on the second etc... [an edit not in the original post]
To keep track of controls scattered across different controls collections, use a List(of T) and just add them when they are created. If you hook up event handlers as part of creating the control, you dont need a loop at all.
Private baskets As New List(of Listview)
...
Dim lv As New ListView ' e.g lvbasjket1
lv.Name = "ziggy"
... many props
AddHandler lv.MouseDoubleClick, AddressOf lvBasket_MouseDoubleClick
baskets.Add(lv) ' add to secondary collection
BasketTab1.Controls.Add(lv) ' add to controls collection
lv = New ListView ' ie lvBasket2
...
baskets.Add(lv)
BasketTab2.Controls.Add(lv)
The handlers were added as the control was created, so there is no need for any loop, though you could create them in a loop and add them to the list.
Lists are easier to work with than arrays, but baskets(0) will refer to the first one created, baskets(1) to the second etc. You can do the same thing for buttons, textboxes etc, but these are all still grouped together in each TabPage's control collection making it easy to get them without creating additional references:
' do something to basket one on tabpage 1
Dim n = 1
Dim lv = TabControl2.TabPages(n - 1).Controls().OfType(Of ListView)().FirstOrDefault()
If lv IsNot Nothing Then
' do something wonderful
End If

Yes, use Me.Controls.Find as follows:
Option Strict On
Public Class Form1
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Call InitControlsArrayAndEventHandlers()
End Sub
Private lvBasket(-1) As ListView
Private btnSave(-1) As Button
Private Sub InitControlsArrayAndEventHandlers()
Dim i As Integer = 1
Do
Dim ctl() As Control = Me.Controls.Find("lvBasket" & i.ToString, True)
If ctl.GetUpperBound(0) = -1 Then Exit Do 'finished
ReDim Preserve lvBasket(i - 1)
lvBasket(i - 1) = DirectCast(ctl(0), ListView)
ctl = Me.Controls.Find("btnSave" & i.ToString, True)
ReDim Preserve btnSave(i - 1)
btnSave(i - 1) = DirectCast(ctl(0), Button)
i += 1
Loop
For i = 0 To lvBasket.GetUpperBound(0)
AddHandler lvBasket(i).MouseDoubleClick, AddressOf lvBasket_MouseDoubleClick
AddHandler btnSave(i).Click, AddressOf btnSave_Click
Next
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs)
MsgBox(DirectCast(sender, Button).Name)
End Sub
Private Sub lvBasket_MouseDoubleClick(sender As Object, e As MouseEventArgs)
MsgBox(DirectCast(sender, ListView).Name)
End Sub
End Class

Here is how I've implemented a solution. My actual controls are named without underscore, like lvBasket1.
Imports System.Linq.Expressions
Private _txtNameSurname(BasketCount), _txtPhone(BasketCount) As TextBox
Private _btnSelectCustomer(BasketCount), _btnAddProduct(BasketCount) As Button
Private _lblInfo(BasketCount) As Label
Private _lvBasket(BasketCount) As ListView
Private Sub AssignControl(Of ControlType)(index As Integer, controlArrayFunc As_
Expression(Of Func(Of ControlType())))
Dim controlArray = controlArrayFunc.Compile()()
Dim keyword = CType(controlArrayFunc.Body, MemberExpression).Member.Name.TrimStart("_"c)
controlArray(index) =_
Me.Controls.Find(keyword & (index + 1), True).OfType(Of ControlType).Single()
End Sub
Private Sub InitControlsArrayAndEventHandlers()
For i As Integer = 0 To BasketCount
AssignControl(i, Function() _lvBasket)
AssignControl(i, Function() _txtNameSurname)
AssignControl(i, Function() _txtPhone)
AssignControl(i, Function() _btnSelectCustomer)
AssignControl(i, Function() _btnAddProduct)
AssignControl(i, Function() _lblInfo)
AddHandler _lvBasket(i).MouseDoubleClick, AddressOf vlBasket_Click
AddHandler _btnSelectCustomer(i).Click, AddressOf btnSelectCustomer_Click
Next
End Sub

Related

TabPage selection, move the Focus to the previous ActiveControl when a TabPage is reselected

I need some help to focus a particular control when a TabPage is revisited. I followed many other blogs, but I wasn't able to solve the problem myself.
I created the TabPages inside a MDIForm:
Public Sub Tab_Open(Of T As {Form, New})(name As String, NameofTab As String, Tabnumber As String)
Dim _formByName As New Dictionary(Of String, Form)
Dim Frm As Form = Nothing
If Not _formByName.TryGetValue(name, Frm) OrElse _formByName(name).IsDisposed Then
Frm = New T()
_formByName(name) = Frm
End If
Dim childTab As TabPage = New TabPage With {
.Name = NameofTab & " : " & Tabnumber,
.Text = NameofTab & " : " & Tabnumber,
.Tag = Frm.Name
}
Form1.tabForms.TabPages.Add(childTab)
Frm.TopLevel = False
Frm.FormBorderStyle = FormBorderStyle.None
Frm.Parent = Form1.tabForms.TabPages(Form1.tabForms.TabCount - 1)
Frm.Dock = DockStyle.Fill
Frm.Show()
Form1.tabForms.SelectedTab = childTab
Form1.tabForms.Visible = True
End Sub
Let's assume that in first TabPage the Focus was on a TextBox (with TabIndex = 4), now I may be click on the second TabPage.
After some calculations, when I select the previous TabPage, the Focus should be set to the TextBox with TabIndex = 4 again, but that's not happening.
I tried to create a Dictionary in the MDIForm as:
Public Tab_Last_Focus_info As New Dictionary(Of String, String())
and in SelectedIndexChanged I have this code:
Private Sub tabForms_SelectedIndexChanged(sender As Object, e As EventArgs) Handles tabForms.SelectedIndexChanged
If Tab_Last_Focus_info.ContainsKey(tabForms.SelectedTab.Name) Then
Dim FullTypeName1 As String = String.Format("{0}", Tab_Last_Focus_info.Item(tabForms.SelectedTab.Name))
Dim Indxval As String = String.Format("{1}", Tab_Last_Focus_info.Item(tabForms.SelectedTab.Name))
Dim FullTypeName As String = Application.ProductName & "." & FullTypeName1
Dim FormInstanceType As Type = Type.GetType(FullTypeName, True, True)
Dim frm As Form = CType(Activator.CreateInstance(FormInstanceType), Form)
Dim Focus_on As Integer = Integer.Parse(Indxval)
frm.Controls(Focus_on).Focus()
' Not working too =>
' frm.Controls(Focus_on).Select()
' Invisible or disabled control cannot be activated =>
' ActiveControl = frm.Controls(Focus_on) 'System.ArgumentException:
End If
End Sub
In the Form, which is opened via a Menu, I have this code for the Control that's focused:
Private Sub All_Got_Focus(sender As Object, e As EventArgs) Handles TB_ImageLoc.GotFocus, TB_CompWebsite.GotFocus,
TB_CompPinCD.GotFocus, TB_CompPAN.GotFocus, TB_CompName.GotFocus, TB_CompMobile.GotFocus,
TB_CompMD.GotFocus, TB_CompLL.GotFocus, TB_CompGSTIN.GotFocus, TB_CompFax.GotFocus, TB_CompEmail.GotFocus,
TB_CompCD.GotFocus, TB_CompAreaCity.GotFocus, RTB_CompADD.GotFocus, PB_Logo.GotFocus, DTP_CompEst.GotFocus, DGV_CompList.GotFocus,
CHKB_CompIsRegTrans.GotFocus, CB_CompStateID.GotFocus, CB_CompDistrictID.GotFocus, But_Upd.GotFocus, But_SelectLogo.GotFocus,
But_Search.GotFocus, But_Reset.GotFocus, But_Refresh.GotFocus, But_GridSelect.GotFocus, But_Exit.GotFocus, But_Edit.GotFocus,
But_Del.GotFocus, But_Add.GotFocus
If Form1.Tab_Last_Focus_info.ContainsKey(Form1.tabForms.SelectedTab.Name) Then
Form1.Tab_Last_Focus_info.Remove(Form1.tabForms.SelectedTab.Name)
End If
Form1.Tab_Last_Focus_info.Add(Form1.tabForms.SelectedTab.Name, New String() {Me.Name, Me.ActiveControl.TabIndex})
End Sub
Now in TabIndexChange I'm getting a correct value from the Dictionary, but I'm not able to focus on the required tab.
Kindly help and let me know what I am missing or what need to taken care for this issue or please let me know any other better idea for the same.
First thing, a suggestion: test this code in a clean Project, where you have a MDIParent and one Form with a TabControl with 2 o more TabPages, containing different types of Controls. Test the functionality, then apply to the Project that is meant to use it.
You need to keep track of the selected Control in a TabPage - the current ActiveControl - switch to other TabPages, restore the previous ActiveControl in a TabPage when it's brought to front again.
The procedure is simple, implemented as follows:
To keep track of the current ActiveControl - the Control that has the Focus, you need to know when a Control becomes the ActiveControl. This Control of course must be child of a TabPage.
The ContainerControl class (the class from which Form derives) has a protected virtual method, UpdateDefaultButton(), that's overridden in the Form class. It's used to determine which child Button is activated when a User presses the Enter Key.
This method is called each time a new Control becomes the ActiveControl: overriding it, we can be informed when this happens, so we can check whether the new ActiveControl is one we're interested in, because it's child of a TabPage of our TabControl.
When the new ActiveControl is one we need to keep track of, we can store the reference of this Control and the Index of the TabPage it belongs to in a collection, so we can then use this reference, when the selected TabBage changes, to set it again as the ActiveControl in its TabPage.
Here, to store the state, I'm using a Dictionary(Of Integer, Control), where the Key is the Index of the TabPage and the Value is the reference of its ActiveControl.
When the TabControl.Selected event is raised - after a TabPage has been selected - we can lookup the Dictionary and restore the previous ActiveControl of that TabPage if one was stored.
► Here, BeginInvoke() is used to defer the action of setting the new ActiveControl, because this also causes a call to UpdateDefaultButton() and this method is called before the TabControl.Selected event handler completes.
Public Class SomeMdiChildForm
Private tabPagesActiveControl As New Dictionary(Of Integer, Control)()
' This method is called each time a Control becomes the ActiveControl
Protected Overrides Sub UpdateDefaultButton()
MyBase.UpdateDefaultButton()
If TypeOf ActiveControl.Parent Is TabPage Then
Dim tabPageIdx = CType(CType(ActiveControl.Parent, TabPage).Parent, TabControl).SelectedIndex
If tabPagesActiveControl.Count > 0 AndAlso tabPagesActiveControl.ContainsKey(tabPageIdx) Then
tabPagesActiveControl(tabPageIdx) = ActiveControl
Else
tabPagesActiveControl.Add(tabPageIdx, ActiveControl)
End If
End If
End Sub
Private Sub TabControl1_Selected(sender As Object, e As TabControlEventArgs) Handles TabControl1.Selected
Dim ctrl As Control = Nothing
If tabPagesActiveControl.TryGetValue(e.TabPageIndex, ctrl) Then
BeginInvoke(New Action(Sub() Me.ActiveControl = ctrl))
End If
End Sub
End Class
C# Version:
(assume tabControl1 is the name of the TabControl instance)
public partial class SomeForm : Form
{
private Dictionary<int, Control> tabPagesActiveControl = new Dictionary<int, Control>();
// [...]
// This method is called each time a Control becomes the ActiveControl
protected override void UpdateDefaultButton()
{
base.UpdateDefaultButton();
if (ActiveControl.Parent is TabPage tp) {
var tabPageIdx = (tp.Parent as TabControl).SelectedIndex;
if (tabPagesActiveControl.Count > 0 && tabPagesActiveControl.ContainsKey(tabPageIdx)) {
tabPagesActiveControl[tabPageIdx] = ActiveControl;
}
else {
tabPagesActiveControl.Add(tabPageIdx, ActiveControl);
}
}
}
private void tabControl1_Selected(object sender, TabControlEventArgs e)
{
if (tabPagesActiveControl.TryGetValue(e.TabPageIndex, out Control ctrl)) {
BeginInvoke(new Action(() => ActiveControl = ctrl));
}
}
}
As mentioned previously Tab_Open sub is used to create a form as tab.
In Main form (MDI) created Dictionary as
Public tabPagesActiveControl As New Dictionary(Of String, Integer)
In each form when the control is focused the value has been added to dictionary as
Private Sub DateTimePicker1_Leave(sender As Object, e As EventArgs) Handles RadioButton1.GotFocus,
DateTimePicker1.GotFocus, ComboBox1.GotFocus, CheckBox1.GotFocus, Button1.GotFocus, TextBox3.GotFocus, TextBox4.GotFocus, RichTextBox1.GotFocus
If Form1.tabPagesActiveControl.ContainsKey(Form1.TabControl1.SelectedTab.Name) Then
Form1.tabPagesActiveControl(Form1.TabControl1.SelectedTab.Name) = Me.ActiveControl.TabIndex
Else
Form1.tabPagesActiveControl.Add(Form1.TabControl1.SelectedTab.Name, Me.ActiveControl.TabIndex)
End If
End Sub
And when the tab is focused:
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
If tabPagesActiveControl.ContainsKey(Me.TabControl1.SelectedTab.Name) Then
Dim Indxval As String = String.Format(tabPagesActiveControl.Item(Me.TabControl1.SelectedTab.Name))
SendKeys.Send("{TAB " & Indxval & "}")
End If
End Sub
As mentioned in the comments it has flaws. Kindly please check and help or do let me know what can be tried.
Finally I solved the issue after struggling for 8 Days :)
As I mentioned earlier I Open the forms as tabs using the Sub Tab_Open mentioned in the question.
Defined or created a new dictionary in MDI form as
Public tabPagesActiveControl As New Dictionary(Of String, Control)
and defined a control variable as
Dim Sel_Control As Control
Now in each form when the control is focused I have the below code to assign the current control alone to the dictionary:
Private Sub All_Focus(sender As Object, e As EventArgs) Handles TBox_Reg_website.GotFocus,
TBox_Reg_To.GotFocus, TBox_Reg_State.GotFocus, TBox_Reg_PinCD.GotFocus, TBox_Reg_PAN.GotFocus, TBox_Reg_office_num.GotFocus,
TBox_Reg_mobile_num.GotFocus, TBox_Reg_GSTIN.GotFocus, TBox_Reg_fax_no.GotFocus, TBox_Reg_email.GotFocus, TBox_Reg_country.GotFocus,
TBox_Reg_Company.GotFocus, TBox_Reg_City.GotFocus, TBox_Reg_Add2.GotFocus, TBox_Reg_Add1.GotFocus, TB_Curr_website.GotFocus,
TB_Curr_state.GotFocus, TB_Curr_RegTo.GotFocus, TB_Curr_Pincd.GotFocus, TB_Curr_Pan.GotFocus, TB_Curr_office_num.GotFocus,
TB_Curr_Mobile_num.GotFocus, TB_Curr_Gstin.GotFocus, TB_Curr_fax_no.GotFocus, TB_Curr_email.GotFocus, TB_Curr_country.GotFocus,
TB_Curr_Company.GotFocus, TB_Curr_city.GotFocus, TB_Curr_add2.GotFocus, TB_Curr_add1.GotFocus,
PICBox_Reg_Logo.GotFocus, MSP_Reg.GotFocus, Label9.GotFocus, Label8.GotFocus, Label7.GotFocus, Label6.GotFocus, Label5.GotFocus,
Label4.GotFocus, Label3.GotFocus, Label2.GotFocus, Label15.GotFocus, Label14.GotFocus, Label13.GotFocus, Label12.GotFocus,
Label11.GotFocus, Label10.GotFocus, Label1.GotFocus,
ChkBx_Upd_Logo.GotFocus, Chkbox_NoLogo.GotFocus
If Form1.tabPagesActiveControl.ContainsKey(Form1.TabControl1.SelectedTab.Name) Then
Form1.tabPagesActiveControl.Remove(Form1.TabControl1.SelectedTab.Name)
End If
Form1.tabPagesActiveControl.Add(Form1.TabControl1.SelectedTab.Name, Me.ActiveControl)
End Sub
and in the MDI form when tab select index changes having the below code:
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles TabControl1.SelectedIndexChanged
If tabPagesActiveControl.ContainsKey(Me.TabControl1.SelectedTab.Name) Then
Sel_Control = tabPagesActiveControl.Item(Me.TabControl1.SelectedTab.Name)
Sel_Control.Focus()
End If
End Sub
Thanks :)

controls not being displayed

I have two similar methods to add a user control to a panel as needed. However, upon attempting to add, the method is called and completed, but does not add the user control to the form. While attempting various different ways of adding the user control, one time I could move the initial user control around and it would move to leave space for another user control, but the user control was not visible.
Edit: the initial add method (addInitialItemGroupTest) does work
Public Sub addItemGroupTest(ByVal sender As Object, ByVal e As EventArgs)
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
Dim item_block_new As New ucItemsetItemBlock
' item_block_new.Visible = True
' item_block_new.Dock = DockStyle.Top
item_block_new.flpMain.Name = (10 + item_set.blocks.Count()).ToString
item_block_new.BringToFront()
frm.flpItemBlocks.Controls.Add(item_block_new)
' item_block_new.Show()
AddHandler item_block_new.flpMain.Click, AddressOf addItemToItemBlock
End Sub
Public Sub addInitialItemGroupTest()
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
Dim item_block As New ucItemsetItemBlock
' item_block.Dock = DockStyle.Top
item_block.flpMain.Name = (10 + item_set.blocks.Count()).ToString
item_block.BringToFront()
frm.flpItemBlocks.Controls.Add(item_block)
AddHandler item_block.flpMain.Click, AddressOf addItemToItemBlock
' item_block.Sh
End Sub
Public Sub showEditor()
frm = New frmItemsetEditor
frm.TopLevel = False
frm.WindowState = FormWindowState.Maximized
frm.FormBorderStyle = FormBorderStyle.None
If frm.Location.X < 0 Then
frm.Location = New Point(0, frm.Location.Y)
End If
If frm.Location.Y < 0 Then
frm.Location = New Point(frm.Location.X, 0)
End If
frm.Show()
addItems()
If id > 0 Then
Console.WriteLine("loading item set")
For Each item_block In item_set.blocks
frm.flpItemBlocks.Controls.Add(item_block)
Next
Else
Console.WriteLine("creating item set")
item_set = New LeagueItemSet
addInitialItemGroupTest()
' addInitialItemGroup()
End If
AddHandler frm.btnAddItemGroup.Click, AddressOf addItemGroupTest
loadUserControl()
frm.pnlItemSetUserControl.Controls.Add(uc)
frm.flpItemBlocks.BringToFront()
AddHandler uc.btnAddUpdate.Click, AddressOf updateSetting
End Sub
Private Sub btnCreateItemset_Click(sender As Object, e As EventArgs) Handles btnCreateItemset.Click
Parent.AccessibleDescription = "status:Loading: Itemset Creation Wizard"
rgoism.addSetting()
rgoism._Settings.Last.frm.Parent = Me.Parent
rgoism._Settings.Last.frm.Location = New Point(Convert.ToInt32((Parent.Size.Width / 2) - (rgoism._Settings.Last.frm.Size.Width / 2)), Convert.ToInt32((Parent.Size.Height / 2) - (rgoism._Settings.Last.frm.Size.Height / 2)))
rgoism._Settings.Last.frm.BringToFront()
Me.Hide()
Parent.AccessibleDescription = "status:Ready"
End Sub
I kept debugging for nearly 12 hours straight. While nearly falling asleep I entered a bunch of console.writeline() into a method to test it since I have been lost. Next thing I know, it is adding visible user controls to the form. I just need to test them and make sure they're unique but I am too tired. I have no idea how it works now since I did not intentionally change any lines of code. Maybe I just lucked out and changed the right line of code by accident. I did comment out and back in a few lines as well. I am completely and utterly lost but it works now.

Adding an event handler when a subcontrol is instantiated

I've got a custom FlowLayoutPanel: an "AlbumFlowLayout" which inherits from a FlowLayoutPanel and is used to hold a collection of UserControls ("AlbumItems"). Typically, this would reside on a form ("FrmMain")so that the hierarchy of items is:
Form ("FrmMain")
AlbumFlowLayout ("AlbumFlowLayout1")
AlbumItems (1 or more)
[Is there a way/what is the protocol] for adding a "WasClicked" handler to a created AlbumItem whenever it's created/added to the AlbumFlowLayout?
Ideally I'd like to encapsulate the handler construction code inside the AlbumFlowLayout so that it happens automatically whenever the code in FrmMain does an AlbumFlowLayout.Controls.Add of a new AlbumItem, rather than having a second line in the FrmMain add the handler before adding the control, e.g.:
Dim myItem As New AlbumItem
AddHandler myItem.WasClicked, AddressOf AlbumFlowLayout1.AlbumItem_WasClicked
AlbumFlowLayout1.Controls.Add(myItem)
Thanks!
-Pete
Plutonix had the solution. Here's what the final code looks like to use it:
Partial Public Class AlbumFlowLayout
Inherits FlowLayoutPanel
' A FlowLayoutPanel for handling collections of AlbumItems
Public SelectedItems As New List(Of String)
' Holds IDs of currently selected items
Private Sub AlbumFlowLayout_ControlAdded(sender As Object, e As ControlEventArgs) Handles Me.ControlAdded
' Wire up each user item as it's added so that it will pass its
' Wasclicked up to here
Dim myAlbumItem As AlbumItem = e.Control
AddHandler myAlbumItem.WasClicked, AddressOf Me.AlbumItem_WasClicked
End Sub
' Other methods...
' ...
Public Sub AlbumItem_WasClicked(ByVal sender As Object, e As EventArgs)
' Deselects all previously selected items. Doing this via a List to
' Allow for expansion item where we permit multi-selection via
' Control-key or the like; currently is single-select
Dim myItem As AlbumItem = sender
For Each itm As String In SelectedItems
If itm <> myItem.Name Then
DirectCast(Me.Controls(itm), AlbumItem).IsSelected = False
End If
Next
SelectedItems.Clear()
SelectedItems.Add(myItem.Name)
End Sub
End Class

How to manage dynamically created controls in VB.NET?

I just understand how to make controls dynamically in VB.NET (I mean, only part of adding a new one)
But, unlike VB6, it seems hard to handle those dynamic things.
When I click the DONE button, I want to make an array filled with the text of textboxes.
At the same time, I want to make a Delete button that removes the button itself and the textbox in the same line.
Is there any simple method or an sample code for this?
Thank you!
Drop a TableLayoutPanel on your form, called pnlLayout, and also the Add button called btnAdd. Configure TableLayoutPanel to have two columns, adjust column width as needed.
Paste below code into your form:
Public Class Form1
Dim deleteButtons As List(Of Button)
Dim textBoxes As List(Of TextBox)
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
deleteButtons = New List(Of Button)
textBoxes = New List(Of TextBox)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim elementCount As Integer = deleteButtons.Count
Dim txt As New TextBox
txt.Width = 100
txt.Height = 20
textBoxes.Add(txt)
Dim btn As New Button
btn.Width = 100
btn.Height = 20
btn.Text = "Delete " & elementCount.ToString
AddHandler btn.Click, AddressOf btnDelete
deleteButtons.Add(btn)
pnlLayout.SetCellPosition(txt, New TableLayoutPanelCellPosition(0, elementCount))
pnlLayout.SetCellPosition(btn, New TableLayoutPanelCellPosition(1, elementCount))
pnlLayout.Controls.Add(btn)
pnlLayout.Controls.Add(txt)
End Sub
Private Sub btnDelete(sender As Object, e As EventArgs)
Dim senderButton As Button = DirectCast(sender, Button)
Dim txt As TextBox = textBoxes(deleteButtons.IndexOf(senderButton))
pnlLayout.Controls.Remove(senderButton)
pnlLayout.Controls.Remove(txt)
End Sub
End Class
By default, it will have no textboxes and no Delete buttons, you can add as many rows of "Textbox + Delete button" as you want. When you press Delete, the row will be removed (and everything shifted to accommodate the empty space).
For the textbox'ex part:
Dim strcol() As String = {TextBox2.Text, TextBox3.Text}
For Each strtxt In strcol
MessageBox.Show(strtxt)
Next
It really depends on your code, but, if you have their name use this to delete the buttons &/ textbox'es:
For i As Integer = Me.Controls.Count - 1 To 0 Step -1
If TypeOf Me.Controls(i) Is TextBox Then
If Me.Controls(i).Name = "TextBox2" Then
Me.Controls.RemoveAt(i)
End If
End If
If TypeOf Me.Controls(i) Is Button Then
If Me.Controls(i).Name = "Button3" Then
Me.Controls.RemoveAt(i)
End If
End If
Next
But it depends on your code...

Retrieving data on dynamic controls

I am using dynamically created controls and need to retrieve information about the control at runtime.
If IsLoaded <> "free" Then
flow_display.Controls.Clear()
For x As Integer = 0 To populate.Count - 1
If populate(x).parentID = 2 Then
Dim NewPicBox As PictureBox = New PictureBox
NewPicBox.Size = New System.Drawing.Size(697, 50)
NewPicBox.ImageLocation = pw_imgLink & populate(x).imageID
AddHandler NewPicBox.Click, AddressOf catWindow
flow_display.Controls.Add(NewPicBox)
End If
Next
IsLoaded = "free"
End If
End Sub
Here I create the control when the user clicks on the appropriate label. Right now the catWindow sub is empty. I need to figure out which button is clicked and figure out its location on the populate list. I have tried a few things and from what I've read from other questions can't seem to find anything the helps. Thanks :)
For finding out which PictureBox is pressed, your catWindow Sub should look like this:
Public Sub catWindow(ByVal sender As Object, ByVal e As EventArgs)
Dim box As PictureBox = TryCast(sender, PictureBox)
If box Is Nothing Then Exit Sub
'Now "box" refers to the PictureBox that was pressed
'...
End Sub
If you want to find it's location in the populate list, you will need to iterate through the list until you find the matching box. You could also pre-empt a property on your PictureBox that isn't doing anything else and use it to store the index. Older forms tools used to have a .Tag property especially for this kind of thing. But really, the need to do this smells like a design flaw to me.
FWIW, I'd rewrite your original sample like this:
If IsLoaded <> "free" Then
flow_display.SuspendLayout()
flow_display.Controls.Clear()
For Each box As PictureBox In populate
.Where(Function(p) p.parentID = 2)
.Select(Function(p) New PictureBox() With {
.Size = New System.Drawing.Size(697, 50),
.ImageLocation pw_imgLink & p.imageID })
AddHandler box.Click, AddressOf catWindow
flow_display.Controls.Add(box)
Next box
flow_display.ResumeLayout()
IsLoaded = "free"
End If