dynamically create a new tab with textbox, button in the tab with click and keypress events (AddHandler is what i cant get to work) - vb.net

I am creating some tabs and I need two things to work that I can't get to work. I need to AddHandler for a Textbox.Keypress event AND a Button.Click event. I can make these things work outside of the tabcontrol but not in.
In the example below my text box and buttons have same name from on tab to the another, I thought that might be my problem but even changing names between tabs does not work. I assume I need to be more specific in the AddHandler part to give the tab name as well as control. There is a logic in my real code to allow me to give unique names to each tab panel and controls, but i can't get the simple part to work.
I left some of the things I tried commented, but I tried LOTS and LOTS of other things.
Public Class Form1
Public Sub addTab(tabPageName As String)
Dim tabpage As New TabPage
tabpage.Text = tabPageName
tabpage.Name = "tabPage1" 'real code has logic to make sure names are unique
Dim label1 As New Label
Dim txtCreator As New TextBox
Dim combox1 As New ComboBox
Dim tabPageButton2 As New Button
tabPageButton2.Parent = tabpage
label1.Parent = tabpage
txtCreator.Parent = tabpage
combox1.Parent = tabpage
label1.Location = New Point(10, 10)
txtCreator.Location = New Point(150, 10)
combox1.Location = New Point(300, 10)
tabPageButton2.Location = New Point(20, 40)
label1.Text = "Creator"
txtCreator.Name = "txtCreator"
'fill the comboboxes...this will come from a database but testing now.
combox1.Items.Add("one")
combox1.Items.Add("two")
combox1.Items.Add("three") 'ok that works so should work from DB no problem.
tabRoleClass.TabPages.Add(tabpage)
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
addTab("First Tab")
AddHandler Controls("tabRoleClass.tabPage1.tabPageButton2").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("tabPageButton"), Button).Click, AddressOf tabPageButton_click
'AddHandler Controls("tabPageButton").Click, AddressOf tabPageButton_click
AddHandler CType(Controls("txtCreator"), TextBox).KeyPress, AddressOf txtcreator_keypress 'the Keypress to call lookup
End Sub
Private Sub tabPageButton_click(sender As System.Object, e As System.EventArgs) 'Handles tabPageButton.click
MessageBox.Show(tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
addTab("Second Tab")
tabRoleClass.SelectedIndex = tabRoleClass.TabCount - 1
'AddHandler Controls("tabRoleClass.tabPage1.tabPageButton2").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("tabPageButton"), Button).Click, AddressOf tabPageButton_click
'AddHandler Controls("tabPageButton").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("txtCreator"), TextBox).KeyPress, AddressOf txtcreator_keypress 'the Keypress to call lookup
End Sub
Private Sub txtcreator_keypress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) 'Handles txtCreator.KeyPress
MessageBox.Show("keypress worked on " & tabRoleClass.SelectedTab.Name.ToString)
End Sub
End Class

This is a very confusing question and your code could really do with some cleaning, but you need to add the AddHandler code to the addTab subroutine as pointed out by #Plutonix:
Public Sub addTab(tabPageName As String)
Dim tabpage As New TabPage
Dim tabPageButton As New Button
Dim txtCreator As New TextBox
/.../
AddHandler tabPageButton.Click, AddressOf tabPageButton_click
AddHandler txtCreator.KeyDown, AddressOf txtcreator_keypress
tabRoleClass.TabPages.Add(tabpage)
End Sub
Private Sub tabPageButton_click()
MessageBox.Show(tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub txtcreator_keypress()
MessageBox.Show("keypress worked on " & tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
addTab("First Tab")
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
addTab("Second Tab")
tabRoleClass.SelectedIndex = tabRoleClass.TabCount - 1
End Sub
AddHandler works by adding event handlers to your controls. This means that each time an event is raised during this runtime, the new event handler will handle the event; everytime you click your tabPageButton the associated event tabPageButton_click will handle it.
Therefore, you will only need to add the handler once, preferably upon the creation of the control. There is absolutely no need to create them upon every single keypress, for example. You should look up event handlers on MSDN.
Hope this helps!

Sorry if the code was confusing, I cut up my actual code to make a "sample" and I can see the confusion. Now of course I AM confused, I originally had the AddHandler INSIDE the addTab sub that creates the tab and it didn't work there, I incorrectly assumed the reason was that the control was not yet created so I moved it out. Moving it back in this sub this morning worked perfectly, I don't know what I did wrong but its working GREAT by moving it up to where it belongs, thanks A LOT, I worked on this for 2 days trying and googling things. Next time I will post real code instead of a sample to be less confusing and also remove my commented attemps (I thought those would help show what I was trying but I think it didn't)

Related

Why doesn't Me.Controls.OfType work in VB.NET?

I'm using .NET Framework 4.7.2 for reference.
I'm using Me.Controls.OfType for automated handles in an event in my form.
Sub AddTextBoxes_TextChanged()
Dim textboxes = Me.Controls.OfType(Of TextBox)()
Console.WriteLine(textboxes.Count)
For Each txt In textboxes
AddHandler txt.TextChanged, AddressOf AllTextBoxes_TextChanged
Next
End Sub
Private Sub SampleForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddTextBoxes_TextChanged()
End Sub
Private Sub AllTextBoxes_TextChanged(sender As Object, e As EventArgs)
' ...
End Sub
However, the For loop doesn't work, so I checked if there are actual textbox controls within textboxes with Console.WriteLine(textboxes.Count). Well, the result is 0. I've checked multiple times in the Form Design for textboxes, and they exist. Why doesn't Controls.OfType(Of TextBox)() work?
Place your handler in the code for the form.
In design view select the one of the text boxes.
In the Properties window select the lightning bolt to display all the events available for a TextBox. Choose the TextChanged event and the drop down box arrow. Your AllTextBoxes_TextChanged method will be listed because the signature matches. Select your method and the Handles code will be added to the method.
Do the same for each text box you wish to use this method.
Of course you can always type the extended Handles clause.
However, I don't see what is wrong with your code.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim textboxes = Controls.OfType(Of TextBox)() '.ToList
For Each txt In textboxes
AddHandler txt.TextChanged, AddressOf AllTextBoxes_TextChanged
Next
End Sub
Private Sub AllTextBoxes_TextChanged(sender As Object, e As EventArgs)
Dim tb = DirectCast(sender, TextBox)
MessageBox.Show($"The text changed event fired by {tb.Name}")
End Sub
Works for me.

Hiding Dynamically Created Buttons on click in Visual Basic Forms

Im currently trying to make dynamically created buttons disappear when clicked.
I have a Private Sub that handles when the button is clicked and increases the players score. However, i do not know how to make the specific object disappear as all the Objects are called the same name as they are created by the same subroutine set on a timer ( a new button made every 2 seconds).
I have tried adding every new button created to an array but am still struggling to make the program figure out which button has been clicked.
Any help would be greatly appreciated.
One of the parameters on your button click event handler should be sender As Object, which is a reference to the button that was clicked. You just need to cast it to a button object, the set the Visible property to false. Here is an example:
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim b As Button = CType(sender, Button)
b.Visible = False
End Sub
Further to #Icemanind's answer, you can actually do it in one line:
CType(sender, Button).Visible = False
This works without validation because you know that sender is of type Button.
If you have multiple buttons to handle, you can also add additional events to the handler so you don't end up with a zillion individual handlers:
Protected Sub Button_Click(s As Object, e As EventArgs) _
Handles _
Button1.Click, Button2.Click, Button3.Click
CType(s, Button).Visible = False
End Sub
Yet another option is to add the same handler to multiple buttons rather than adding buttons to the handler:
In Form instantiation (Sub New()):
AddHandler Button1.Click, AddressOf Button_Click
AddHandler Button2.Click, AddressOf Button_Click
AddHandler Button3.Click, AddressOf Button_Click
In Form class code:
Protected Sub Button_Click(s As Object, e As EventArgs)
CType(s, Button).Visible = False
End Sub
This last method works well with dynamic controls because you can add the handlers on the fly:
Dim button As Button
For i As Integer = 0 To 9
button = New Button With {.Name = $"Button{i}", .Text = $"Button{i}", .Left = 42, .Top = 50 + (i * 30)}
Me.Controls.Add(button)
AddHandler button.Click, AddressOf Button_Click
Next i

AddHandler to ToolStripMenuItem through ContextMenuStrip.Opening event not working

Hi to you all and Merry Christmas.
I have recently inherited a VB projects that I must add functionality. So I have the code below:
Private Sub AddItems()
Dim itemMenu = DirectCast(ContextMenuStrip.Items.Find("name", False)(0), ToolStripMenuItem)
For Each dbObject In dbObjects
Dim item As New ToolStripMenuItem(dbObject.Name)
item.Tag = dbObject
AddHandler item.Click, AddressOf Item_Click
itemMenu.DropDownItems.Add(item)
Next
End Sub
Private Sub RemoveItems()
Dim itemMenu = DirectCast(ContextMenuStrip.Items.Find("name", False)(0), ToolStripMenuItem)
For Each item As ToolStripItem In itemMenu.DropDownItems
RemoveHandler item.Click, AddressOf Item_Click
Next
itemMenu.DropDownItems.Clear()
End Sub
Private Sub ContextMenuStrip_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles ContextMenuStrip.Opening
AddItems()
End Sub
Private Sub ContextMenuStrip_Closing(sender As Object, e As ToolStripDropDownClosingEventArgs) Handles ContextMenuStrip.Closing
RemoveItems()
End Sub
Private Sub Item_Click(sender As Object, e As EventArgs)
' Do the work
End Sub
The logic is to fill a sub-menu in a context menu each time with valid database objects.
The problem is that this code does not work. It adds the items to context menu perfectly but the AddHandler item.Click, AddressOf Item_Click does nothing.
The strange thing is that if I call the AddItems() in Form_Load then it works perfectly.
Any help would be appreciated.
2016.12.22 Solution after WozzeC answer
Private Sub AddItems()
RemoveItems()
Dim itemMenu = DirectCast(ContextMenuStrip.Items.Find("name", False)(0), ToolStripMenuItem)
For Each dbObject In dbObjects
Dim item As New ToolStripMenuItem(dbObject.Name)
item.Tag = dbObject
AddHandler item.Click, AddressOf Item_Click
itemMenu.DropDownItems.Add(item)
Next
End Sub
Private Sub RemoveItems()
Dim itemMenu = DirectCast(ContextMenuStrip.Items.Find("name", False)(0), ToolStripMenuItem)
For Each item As ToolStripItem In itemMenu.DropDownItems
RemoveHandler item.Click, AddressOf Item_Click
Next
itemMenu.DropDownItems.Clear()
End Sub
Private Sub ContextMenuStrip_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles ContextMenuStrip.Opening
AddItems()
End Sub
Private Sub Item_Click(sender As Object, e As EventArgs)
' Do the work
End Sub
I have been playing around with this for a little while now and I have managed to reproduce your issue. The reason for your headache is that RemoveItems fires before your Click event. So when you perform RemoveHandler, the click event disappear.
What I suggest that you do instead is to not add the click handler on the ContextMenuItems. Instead you add an EventHandler for DropDownItemClicked on the Parent node. The result will be the same as if Item.Click worked, but without the headache of handling Handlers dynamically.
I also tried switching from Closing to Closed event on the ContextMenu for RemoveItems. But to no avail I am afraid.
Another way to solve this issue is by moving RemoveItems into the first row of AddItems. Then you remove the closing event and its call to RemoveItems. This means that whenever you want to create a new ContextMenu the previous one is disposed properly. This will also solve your future bug where the ContextMenu items are added twice or more. Which currently happens for you if you right click multiple times really fast.

Visual Studio - Affect all textboxes at once

I am trying to remove leading zeroes from my textboxes. I have the code working, but I have close to 50 textboxes. I don't really want to have to make 50 textbox.TextChanged events.
Is there anyway to affect all of the textboxes with the same code?
This is the code I am using:
Private Sub txtTier1_100_TextChanged(sender As Object, e As EventArgs) Handles txtTier1_100.TextChanged
txtTier1_100.Text = txtTier1_100.Text.TrimStart("0"c)
End Sub
First step is to define a general purpose handler
Private Sub HandleTextChanged(sender As Object, e As EventArgs)
Dim tb = CType(sender, TextBox)
tb.Text = tb.Text.TrimStart("0"c)
End Sub
Then attach every one of your TextBox instances to this single handler
AddHandler txtTier1_100.TextChanged, AddressOf HandleTextChanged
AddHandler txtTier1_101.TextChanged, AddressOf HandleTextChanged
AddHandler txtTier1_102.TextChanged, AddressOf HandleTextChanged
Note that if you had all of the TextBox instances in a collection this could be done with a For Each loop as well
ForEach tb in textBoxList
AddHandler tb.TextChanged, AddressOf HandleTextChanged
Next
Private Sub txtTier1_100_TextChanged(sender As Object, e As EventArgs) Handles txtTier1_100.TextChanged, txtTier1_101.TextChanged, txtTier1_102.TextChanged...
'sender is the right textbox
End Sub

.Net WindowsForms MouseDown event not firing

As a new user to VB, I am struggling to see why this code works in one project but not in another. This code works fine if I create a new project and 2 new forms but when I place in my project, it doesn't fire at all on either left or right click.
I have tried a try/catch statement, but no errors are being reported. How do I go about troubleshooting this to find out the error. I have tried to rem out code and run after each comment but still the same. I have even tried removing all other code on the form leaving just the 2 subs but no joy. Any help would be greatly appreciated.
frmMain
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'StorageDataSet1.Customers' table. You can move, or remove it, as needed.
Me.CustomersTableAdapter.Fill(Me.StorageDataSet1.Customers)
'TODO: This line of code loads data into the 'StorageDataSet.User' table. You can move, or remove it, as needed.
Me.UserTableAdapter.Fill(Me.StorageDataSet.User)
'Dim frmDepartmentsLive As New frmDepartment
'frmDepartmentsLive.Owner = Me
'frmDepartmentsLive.ShowDialog()
lblDate.Text = Now
Timer1.Start()
rdoCustomer.Enabled = False
rdoCustomer.Checked = True
rdoDepartment.Enabled = False
rdoDepartment.Checked = False
For Each ctrl In Me.Controls
If TypeOf ctrl Is Button Then
AddHandler CType(ctrl, Button).MouseDown, AddressOf btn_MouseDown
End If
Next
End Sub
Private Sub btn_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs)
If (e.Button = MouseButtons.Right) Then
Dim btn = CType(sender, Button)
frmRacks.buttonName = btn.Name.Replace("btn", "")
frmRacks.Show()
ElseIf (e.Button = MouseButtons.Left) Then
MessageBox.Show("To be coded")
End If
End Sub
frmRacks
Public Class frmRacks
Public buttonName As String
Private Sub racksfrm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblRacks.Text = buttonName
End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Me.Close()
End Sub
End Class
Since the controls are on a panel, they are members of that panel's controls array, not the form's. This -- and other things -- are apparent if you look thru the form's designer (on solution explorer, click Show All, then open formXXX.designer.vb). DOnt change anything, but it shows how controls are created and added. So...
For Each ctrl In thepanelName.Controls
If TypeOf ctrl Is Button Then
AddHandler CType(ctrl, Button).MouseDown, AddressOf btn_MouseDown
End If
Next
If it is ONLY those buttons on the panel you can short cut it:
For Each btn As Button In thepanelName.Controls
AddHandler CType(ctrl, Button).MouseDown, AddressOf btn_MouseDown
Next