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
Related
i have a flowlayoutpanel in my winform,
i want to add some button into the flowlayoutpanel when the form is loaded,
the number of button will depend on how many item(row) exist in my dataset
dataset
Private Sub temp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.ItemInfoTableAdapter1.Fill(DataSet11.ItemInfo)
End Sub
Private Sub FlowLayoutPanel1_Paint(sender As Object, e As PaintEventArgs) Handles FlowLayoutPanel1.Paint
Dim btn As New Button
Dim table as datatable = Dataset11.Tables("ItemInfo")
For 'i think this will need a looping, but i have no idea how to write'
With btn
.Text = ?? 'text will be the itemName'
.Tag = ?? 'tag will be the itemPrice'
End With
Me.FlowLayoutPanel1.Controls.Add(btn)
Next
End Sub
i have no idea what to do next, please help.
The problem here aside from adding a control is iterating from a datatable. Datatables contain DataRows. So that is what you want to get to create the the buttons based on the content of your datatable.
Try this:
Dim btn As New Button
For Each drow As DataRow In table.Rows
btn = New Button
btn.Text = drow.Item("itemName").ToString
btn.Tag = drow.Item("itemPrice").ToString
FlowLayoutPanel1.Controls.Add(btn)
Next
Also, you might take into consideration changing the event in which you are adding the controls in the FlowLayoutPanel. You can use a Form_Load event for example.
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 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...
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")