So here's what I have:
Code that makes buttons when you click the button "New Button" (Button1).
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ActionsMade = ActionsMade + 1
Dim actionButton = New Windows.Forms.Button
ActionContainer.Controls.Add(actionButton)
actionButton.Text = "New Action " + ActionsMade.ToString
actionButton.Width = 107
actionButton.Height = 56
AddHandler actionButton.Click, AddressOf OnActionButtonClick
End Sub
When you click the buttons that were created, it shows an input box, where you type what you want to rename the button to.
Sub OnActionButtonClick()
If IsRenaming Then
Dim renameTo As String
renameTo = InputBox("Rename ActionButton To:")
Else
MessageBox.Show("Action started! Not renaming")
End If
End Sub
The only problem is, I have no way to identify between these buttons.
tl;dr:
I created buttons programmatically
I want to rename them
I don't know how to identify between these buttons and rename them individually
This is more concise and includes a referencable button name (eg Button_03):
Private Sub MakeButton_Click(sender As Object, e As EventArgs) Handles MakeButton.Click
Static ActionsMade As Integer = 0
ActionsMade += 1
ActionContainer.Controls.Add(New Button With {
.Name = $"Button_{ActionsMade:00}",
.Text = $"New Action {ActionsMade:00}",
.Size = New Size(107, 56),
.Location = New Point(100, 250 + 25 * ActionsMade)
})
AddHandler ActionContainer.Controls($"Button_{ActionsMade:00}").Click, AddressOf OnActionButtonClick
End Sub
Note that in the above code I haven't actually defined a new button per se, just New-ed it within the Controls.Add() statement, and then used the generated button name to add the event handler.
Another way would be declare the button as an independent object and then add it:
Dim actionButton As New Button With {
.Name = $"Button_{ActionsMade:00}",
.Text = $"New Action {ActionsMade:00}",
.Size = New Size(107, 56),
.Location = New Point(100, 250 + 25 * ActionsMade)
}
ActionContainer.Controls.Add(actionButton)
AddHandler actionButton.Click, AddressOf OnActionButtonClick
Other notes:
Use of a Static ActionsMade integer variable which retains its value between calls. This could also be a module-wide variable and removed from the MakeButton_Click Sub.
Use of string interpolation in assigning the button's Name and Text properties according to a formated version of the ActionsMade value.
Use of button properties Size and Location.
Automatic increment of button vertical position based on the value of ActionsMade.
Regarding your Action Button event handler, it should look like this:
Private Sub OnActionButtonClick(sender As Object, e As EventArgs)
If ActionStarted Then
MessageBox.Show("Action started! Not renaming.")
Else
CType(sender, Button).Text = InputBox("Rename ActionButton To:")
End If
End Sub
Note that I've checked for a started Action rather than a renaming in progress but that's just an illustration of an alternative approach. Note also that you don't need to know the name of the button to change its properties.
Related
I am creating tabs to keep track of open files. When I create them I can set their properties and add them to the list in a MenuStrip.
Before I do so I want to add a click event function. Since this is in a function and will be run multiple times, I need to have a dynamic way to do add these event handlers. I cannot write this:
Private Sub ToolStripTextBox1_Click(sender As Object, e As EventArgs) Handles ToolStripTextBox1.Click
' ...
End Sub
If I do this I can only name them all one name. I want to be able to add a click event that applies to them separately as individual items.
UPDATE:
Dim textFile As ToolStripTextBox = New ToolStripTextBox("textFile")
FileList.Items.Add(textFile)
textFile.Text = filename
textFile.ReadOnly = True
textFile.BackColor = Color.FromArgb(61, 61, 61)
textFile.ForeColor = Color.White
This is the creation and personalization code. Though when you guys suggested AddButton_Click() Handles AddButton.Click It doesn't work because AddButton isn't an actual button
You can keep track of the items, add add and remove click handlers with the following code
Private FileList As New List(Of ToolStripItem)()
' put this code where you add an item
Dim filename = "whatever"
Dim textFile As ToolStripTextBox = New ToolStripTextBox(filename)
textFile.Text = filename
textFile.ReadOnly = True
textFile.BackColor = Color.FromArgb(61, 61, 61)
textFile.ForeColor = Color.White
add(textFile)
Private Sub clickHandler(sender As Object, e As EventArgs)
Dim item = DirectCast(sender, ToolStripItem)
MessageBox.Show(item.Text)
End Sub
Private Sub add(textFile As ToolStripItem)
FileList.Add(textFile) ' Since I don't know what FileList is, I ued List
AddHandler textFile.Click, AddressOf clickHandler
OpenProjectToolStripMenuItem.DropDownItems.Add(textFile) ' add to whatever menu item you normally do
End Sub
Private Sub remove(textFile As ToolStripItem)
FileList.Remove(textFile)
RemoveHandler textFile.Click, AddressOf clickHandler
OpenProjectToolStripMenuItem.DropDownItems.Remove(textFile)
End Sub
The sample click event in your code includes this:
sender As Object
You can re-use this one event handler, because whichever menu item is clicked will be the sender. So you do something like this:
Private Sub ToolStripTextBox1_Click(sender As Object, e As EventArgs) Handles ToolStripTextBox1.Click
Dim menuItem = DirectCast(sender, ToolStripMenuItem)
If menuItem Is Nothing Then Return
'If you need to choose different code for each item:
Select Case menuItem.name
Case "some item"
Case "some other item"
End Select
End Sub
And you can connect your dynamic toolstrip items to this method using AddHandler:
AddHandler myMenuStrip.Click, AddressOf ToolSTripTextBox1_Click
I have a bit of code where i have a dynamically created array or buttons with staff pictures on them, as well as the staff's name. I've added one handler to handle any button click from any of the buttons. where i am stuck is, if you look at the code below, it all works fine, and if you click any of the buttons you get the "aha" test message. but i want the name of the staff clicked on (so btnArray(i).Text) to be passed to the handler for further processing. I tried adding a ByVal parameter to the handler but that caused an error. what's the correct way to do this? As i said, the code below works for me, i just am at a loss as to how to add the extra functionality.
Dim btnArray(staffcount) As System.Windows.Forms.Button
For i As Integer = 1 To staffcount - 1
btnArray(i) = New System.Windows.Forms.Button
btnArray(i).Visible = True
btnArray(i).Width = 80
btnArray(i).Height = 101
btnArray(i).BackgroundImage = Image.FromFile(picloc(i))
btnArray(i).BackgroundImageLayout = ImageLayout.Stretch
btnArray(i).Text = staffname(i)
Dim who As String
who = btnArray(i).Text
AddHandler btnArray(i).Click, AddressOf Me.theButton_Click
btnArray(i).ForeColor = Color.White
btnArray(i).TextAlign = ContentAlignment.BottomCenter
Dim fnt As Font
fnt = btnArray(i).Font
btnArray(i).Font = New Font(fnt.Name, 10, FontStyle.Bold)
FlowLayoutPanel1.Controls.Add(btnArray(i))
Next i
End Sub
Private Sub theButton_Click()
MsgBox("aha")
End Sub
First, correct the signature of your shared handler.
Private Sub theButton_Click(sender As Object, e As EventArgs)
End Sub
Once that is done getting the text of the button clicked is a simple matter.
Private Sub theButton_Click(sender As Object, e As EventArgs)
Dim textOfButtonClicked As String = DirectCast(sender, Button).Text
MessageBox.Show(textOfButtonClicked)
End Sub
The sender is the button that was clicked. Since signatures use objects for the sender the DirectCast 'changes' it to button and you then can access the .Text property of the button.
If there are more manipulations you want to perform on the clicked button you could do it this way
Private Sub theButton_Click(sender As Object, e As EventArgs)
Dim whBtn As Button = DirectCast(sender, Button) ' get reference to button clicked
Dim textOfButtonClicked As String = whBtn.Text
MessageBox.Show(textOfButtonClicked)
'e.g. change the color
whBtn.BackColor = Color.LightYellow
End Sub
I'm working on a proof of concept type situation that will eventually be tied to a scheduling database. As a Test I created this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'AddButton("test")
addLots()
End Sub
Private Sub AddLots()
Dim x As Integer
For x = 0 To 10
Dim b As New Button
Dim newLabel As New Label
newLabel.Location = New Point(100, x * 20)
newLabel.Name = x
newLabel.BorderStyle = BorderStyle.Fixed3D
newLabel.Text = newLabel.Name
Me.Controls.Add(newLabel)
Me.Controls.Add(b)
b.Location = New Point(20, x * 20)
b.Text = x
b.Tag = x
b.Name = x
AddHandler b.Click, AddressOf Button_Click
Next
End Sub
Private Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim B As Button = sender
MsgBox(B.Name)
End Sub
For this proof of concept, I simply want label 1 text to be updated when I press button 1 seems like a simple process but it's kicking my butt.
As for any object, to affect a Label you will need a reference to it. As it stands, the only reference you have is via the Controls collection of the parent control you added the Label to, i.e. the form itself. You could loop through the Controls of the form and as soon as you find a Label then you know you have the first one, or you could call OfType and First or FirstOrDefault. That assumes that there are no other Label controls on the form.
You might also consider using a dedicated parent control so that you know it will only contain the Label controls you created at run time. The obvious choice would be a TableLayoutPanel because it will handle the layout for you too.
If accessing the dynamic controls via a Controls collection is an issue then keep your own collection. Declare a member variable of type List(Of Label) and add each Label you create to it. You can then access your control from that collection and know that there are no other controls in there to get mixed up with.
By the way, if you're creating those controls at run time then they won't be automatically disposed when the form is. Make sure that you dispose them yourself and also use RemoveHandler for each AddHandler you used.
Actually, looking closer at your code, I just realised that there's a 1:1 correspondence between the Button and Label controls. It would make sense to use that. Two options are to assign the corresponding Label to the Tag of each Button or else use a Dictionary(Of Button, Label) assigned to a member variable to store the relationships. That way, you can then use the sender in the event handler, which will be the Button that was clicked, to get the corresponding Label.
Option 1.
Creating the Label:
Dim btn As New Button
Dim lbl As New Label
btn.Tag = lbl
In the event handler:
Dim btn = DirectCast(sender, Button)
Dim lbl = DirectCast(btn.Tag, Label)
Option 2.
At class level:
Private labelsByButton As New Dictionary(Of Button, Label)
Creating the Label:
Dim btn As New Button
Dim lbl As New Label
Me.labelsByButton.Add(btn, lbl)
In the event handler:
Dim btn = DirectCast(sender, Button)
Dim lbl = Me.labelsByButton(btn)
I added this to the button click event. Doesn't seem very efficient as I will eventually have 30-40 buttons and controls on the form but it works.
Private Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim B As Button = sender
Dim lblToChange As Integer = B.Name
For Each objCtrl As Control In Me.Controls
If TypeOf objCtrl Is Label Then
Dim Lbl As Label = DirectCast(objCtrl, Label)
If Lbl.Name = lblToChange Then
Lbl.Text = "This ONe"
End If
End If
Next
End Sub
I am working with a tabcontrol on which I create page one with the designer. I am creating new tab pages with controls on the pages programmatically. On each page is a several panels, each with two radiobuttons (one yes,another no). There is a panel nested inside the first panel with its visible property set to false. If the user selects yes, I want the nested panel's visible property set to true which will reveal several more radiobuttons from which they must make more choices.
My problem is in changing the nested panel's property on any page other than page one.. I can detect the radiobuttons, but I can't seem to find a way to make the nested panel visible.
Public Class ControlProgram
Dim pageindx as integer
Private Sub btnAddPrgm1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddPrgm1.Click
Dim newTab As New TabPage()
pageindx = (TabControl1.TabPages.Count + 1)
newTab.Text = "Step " & pageindx
'define fill panel controls
Dim newpnlFill As New Panel
Dim newlblFill As New Label
Dim newFillY As New RadioButton
AddHandler newFillY.CheckedChanged, AddressOf CheckforCheckedChanged
Dim newFillN As New RadioButton
AddHandler newFillN.CheckedChanged, AddressOf CheckforCheckedChanged
'add fill panel controls
With newTab.Controls
.Add(newpnlFill)
With newpnlFill
.Location = New System.Drawing.Point(6, 6)
.Size = New System.Drawing.Size(171, 137)
.BorderStyle = BorderStyle.FixedSingle
.Controls.Add(newlblFill)
With newlblFill
.Name = "Fill"
.Text = "Fill ?"
.Font = New Font(newlblFill.Font, FontStyle.Bold)
.Location = New Drawing.Point(5, 3)
End With
.Controls.Add(newFillY)
With newFillY
.Name = "FillY"
.Text = "Yes"
.Location = New Drawing.Point(23, 28)
.Size = New System.Drawing.Size(43, 17)
End With
.Controls.Add(newFillN)
With newFillN
.Name = "FillN"
.Text = "No"
.Location = New Drawing.Point(88, 28)
.Size = New System.Drawing.Size(39, 17)
End With
.Controls.Add(newpnlFill2)
With newpnlFill2
.Location = New System.Drawing.Point(2, 60)
.Size = New System.Drawing.Size(164, 68)
.BorderStyle = BorderStyle.FixedSingle
.Visible = False
End With
End With
End With
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name
End If
End Sub
End Class
I have since figured out a solution to my delima, using your suggestions as a starting point.
I added a few varribles:
Dim rb as Control
Dim bEvent as String
Dim booFillY as Boolean
Dim booFillN as Boolean
I also added the TabControl
TabControl1.TabPages.Add(newTab)
I also made these changes :
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
rb = sender
bEvent = CType(sender, RadioButton).Name
If bEvent = "FillY" Then
Dim newpnlFill2 As Panel = rb.Parent.Controls(3)
newpnlFill2.Visible = True
End If
If bEvent = "FillN" Then
Dim newpnlFill2 As Panel = rb.Parent.Controls(3)
newpnlFill2.Visible = False
End If
End If
End Sub
Now I can make the nested panel(newpnlFill2) visible or not visible by cicking the Yes or No radiobuttons on any of the tab pages created.
thanks for your help. I doubt I would have ever gotten there on my own.
Might not be quite what you were looking for, but should be helpful get you where you need to go.
When I create an application, I like to build a list of all the controls for a given page in the load event so I can access them at any point. This is helpful because WinForms can be very picky about showing you child controls within a tabpage or groupbox, etc.
'Declare this variable within the class for your form (whatever)
Public arrControlsRecursive As New List(Of Control)
'method to recursively check all controls and build to array
Private Sub BuildControlsArrayRecursive(ByVal InControl As Control)
For Each con As Control In InControl.Controls
If con.Controls.Count > 0 Then
BuildControlsArrayRecursive(con)
End If
If TypeOf con Is Control Then
arrControlsRecursive.Add(con)
End If
Next
End Sub
'Call from MyBase.Load Event
BuildControlsArrayRecursive(Form1)
You can also just assemble a list of all tabs, for example, by changing the If statement to Is TypeOf con Is TabPage
Now you can loop through this collection or query it with LINQ. Find a single control by calling the first or single method. Cast to the type you want and do anything to any control anywhere within your form.
I don't really understand what you want to access, you first talk about
changing the nested panel's property on any page other than page one
So I assume you want to access to the other tabs, then, you talk about:
I can't seem to find a way to make the panel visible
Anyway, here's the two solutions:
Access other panels:
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name '**Where is "bEvent" declared??**
Dim newpnlFill2 as Panel = bEvent.Parent.Controls(3), Panel)
newpnlFill2.Visible = bEvent.Checked
End If
End Sub
You access to Parent that will be newpnlFill, then access to Controls(3) that it should be newpnlFill2.
Access other tabs:
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name '**Where is "bEvent" declared??**
Dim TabControl as TabControl = bEvent.Parent.Parent.Parent, TabControl)
'Then, you can access all of the other tabs with:
'TabControl.TabPages(n)
End If
End Sub
This assume that somewhere you add your newTab to a TabControl.
I see that you never add newTab to any TabControl, so you'll never see it..
The first Parent will be newpnlFill, the second one will reference to newTab and the last one is the TabControl that hold the Tab.
Anyway, it's something really gross, cause it assumes that your Tab is always created in this manner. For example, if you will add another panel before newpnlFill, it will not be the 4th Control in the Panel anymore, so you need to change you access code.
My advice is to create your own UserControl that inherit from TabPage, in this way you can create private variables that will always reference to the Panels you want to change. Moreover, the btnAddPrgm1_Click event will be much more clear, moving the build of the page in your class constructor.
Something like:
Private Sub btnAddPrgm1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddPrgm1.Click
Dim newTab As New MyTabPage()
pageindx = (TabControl1.TabPages.Count + 1)
newTab.Text = "Step " & pageindx
TabControl1.TabPages.Add(newTab)
End Sub
I am populating a FlowLayout with Pictureboxes. As I populate i give each of them a tooltip. I have a seperate function to change the pictures how can I change the tooltip as well?
dim laytt as tooltip = new tooltip
For i = 1 To count
Dim newPic As PictureBox = New PictureBox()
newPic.Image = p.Image
newPic.Size = p.Size
newPic.SizeMode = p.SizeMode
laytt.SetToolTip(newPic, ttstring)
AddHandler newPic.Click, AddressOf LayoutComponent_Clicked
sys.Add(a_component)
LayoutFlowLayout.Controls.Add(newPic)
Next
later i have a function to change the pics in it I want to be able to change the tool tip
Private Sub LayoutComponent_Clicked(ByVal sender As Object, ByVal e As EventArgs)
Dim i As Integer = LayoutFlowLayout.Controls.IndexOf(sender)
If deleteModeOn Then
sys.components.RemoveAt(i)
LayoutFlowLayout.Controls.RemoveAt(i)
Exit Sub
End If
'get index in sys from layout?
If (sys.components.Item(i).GetType() = GetType(Transpositor)) Then
Form2.ShowDialog(Me)
sys.components.Item(i).divert = tempTranspositorDivert
'here I want to do something like this
laytt.RemoveAt(i) <--- THIS DOESN'T EXIST
End If
End Sub
TL;DR I want to remove/change only one tooltip text at a specific index
Since the sender parameter is the picture box control that was clicked, you can use that variable to specify which control you want to alter. For instance, this will remove the tool tip:
laytt.SetToolTip(sender, Nothing)
This will change it:
laytt.SetToolTip(sender, "new value")