Retrieving data on dynamic controls - vb.net

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

Related

Can I use variables to control which PictureBox I am using?

Is there a way that I can use a variable to control which PictureBox I am using in Visual Basic?
I.e.:
CurrentNumber = 1
PictureBox(CurrentNumber).backcolour = backcolour
You can use the Me.Controls(String) indexer. It lets you specify the name (as a string) of the control you want to access, thus you can dynamically access a picture box by concatenating the string "PictureBox" with a number.
Dim TargetPictureBox As PictureBox = TryCast(Me.Controls("PictureBox" & CurrentNumber), PictureBox)
'Verifying that the control exists and that it was indeed a PictureBox.
If TargetPictureBox IsNot Nothing Then
TargetPictureBox.BackColor = Color.Red
End If
Alternatively, to save processing power by avoiding looping through the entire control collection every time you can call the OfType() extension on Me.Controls, storing the result in an array sorted by the controls' names. That way it'd only have to iterate the control collection once.
'Class level - outside any methods (subs or functions).
Dim PictureBoxes As PictureBox() = Nothing
'Doesn't necessarily have to be done in a button, it's just an example.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If PictureBoxes Is Nothing Then
PictureBoxes = Me.Controls.OfType(Of PictureBox).OrderBy(Function(p As PictureBox) p.Name).ToArray()
End If
'NOTE: CurrentNumber - 1 is necessary when using an array!
PictureBoxes(CurrentNumber - 1).BackColor = Color.Red
End Sub
NOTE: This solution will only work properly if all your picture boxes are named "PictureBox1", "PictureBox2", etc. If you suddenly skip a number ("PictureBox3", "PictureBox5", "PictureBox6") then PictureBoxes(CurrentNumber - 1) for CurrentNumber = 5 would return PictureBox6 rather than PictureBox5.
What you really should do is create a PictureBox() and use that to reference your picture boxes via an index.
The best way to build your array is to create a method that builds the array from the references created by the designer. This lets you continue to use the designer to create your controls and it makes your code check for deleted controls at design-time. Using Me.Controls(...) suffers from run-time errors if controls you are looking for have been deleted.
Here's the code you need:
Private _PictureBoxes As PictureBox() = Nothing
Sub AssignPictureBoxesArray
_PictureBoxes = {PictureBox1, PictureBox2, PictureBox3}
End Sub
Then you access them like this:
Sub SomeMethod
Dim CurrentNumber = 1
Dim PictureBox = _PictureBoxes(CurrentNumber - 1)
PictureBox.BackColor = System.Drawing.Color.Red
End Sub

Working with too many PictureBoxes, any way to use a For?

Warning : Im completely new to VB.net and only know the most basic form of programming, maybe even less
Visual Basic 2010 Express
I have declared an Array Equipa(x,y) as integer.
I have 30 PictureBoxes ( PictureBox1 to PictureBox30)
In my mind, I assigned each PictureBox to a X and Y in the Array.
At load form, I want to change the image of PictureBox5 to PictureBox25.
For this I copy pasted 20 times PictureBox(x).Image = My.Resources.GreyHexagon
I would like to have a Loop that can do this without me copy pasting so much.
During the program, clicking on PictureBox5, for example, changes it to a different image, depending on the value, of the Array declared.
If Equipa(0, 0) = 0 Then
PictureBox4.Image = My.Resources.GreyHexagon
ElseIf Equipa(0, 0) = 1 Then
PictureBox4.Image = My.Resources.BlueHexagon
Else
PictureBox4.Image = My.Resources.RedHexagon
End If
The problem, again, is I have to repeat this code, for every Array position, since each PictureBox is assigned to each position.
So what I need is a Loop that can go through each PictureBox, I'm not asking you to make that code, I just don't know how to go through each PictureBox, for example PictureBox5 to PictureBox25.
PS . The following code changes EVERY PictureBox to the Image I wan't. But I do not understand any of this code, therefor can't change it to only go through PictureBox5 to PictureBox25.
Me.SuspendLayout()
For Each box As PictureBox In Me.Controls.OfType(Of PictureBox)()
box.Image = My.Resources.RedHexagon2
Next box
Me.ResumeLayout()
What I tried once before: In the declarations,
Dim pic(29) As PictureBox
And then in Public Sub New, I wrote picarray() and then made a Private Sub picarray. In this sub I dimmed each picture to the array
pic(0) = picturebox1
pic(1) = picturebox2
pic(2) = picturebox3
...
pic(29) = picturebox30
In my case, I wanted to move all 30 at once, and I had a timer, so I did:
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _Timer1.Tick
For i = 0 To 29
pic(i).Left = pic(i).Left + 3
Next
End Sub
To move them all to the right at once. I used the same way to move them back if the last one on the left hits the form's edge.
Me.SuspendLayout()
For Each box As PictureBox In Me.Controls.OfType(Of PictureBox)()
box.Image = SomeNewPicture
Next box
Me.ResumeLayout()
If you want to be able to look at specific images, you can still just say things like PictureBox4.Image = SomeNewImage. But if you really want to access them by index, first define this variable as a member of the form class:
Private PictureBoxes() As PictureBox
And then do this in your form's Load event:
PictureBoxes = Me.Controls.OfType(Of PictureBox)().ToArray()
Now you can reference each PictureBox by index:
PictureBoxes(4).Image = SomeNewPicture
PictureBoxes(10).Image = SomeOtherNewPicture
You could also do something like this:
Me.SuspendLayout()
For i As Integer = 5 to 25
PicturesBoxes(i) = My.Resources.RedHexagon2
Next i
Me.ResumeLayout()
Finally, to address the If/Then code in your edited question, I'd do something like this:
'Note this Array shorthand requires a more recent version of Visual Studio
' I used it for brevity in the response. You should be able to convert it on your own
Dim Images() As Image = {My.Resources.GreyHexagon, My.Resources.BlueHexagon, My.Resources.RedHexagon}
Dim ImageIndex As Integer = Equipa(0, 0)
If ImageIndex < 0 OrElse ImageIndex > 1 Then ImageIndex = 2
PictureBox4.Image = Images(ImageIndex)

How to Handle events from a control array VB.net

To start, hi to everyone im new to stackoverflow, and also new to programing (on 1ยบ year).
I've been searching but ive found nothing that answer my question, or maybe im just to newbie to understand the answers, so im sorry if its too simple, i cant see it!
/* my native lenguage is not english*/
Here is my problem, i'm making a VB form whit 200 pictureboxes that have to change or interact on click
i've made a control array whit all of them, like this:
Dim control(199) As PictureBox = Controles(control, 0)
Function Controles(ByRef control As Array, ByVal cont As Integer)
For Each pic As PictureBox In Me.Controls
control(cont) = pic
cont += 1
Next
Return control
End Function
this should asociate each picturebox to an array position, my problem now is how i can set the event handler to watch at control().click so no matter what box you click the event onclick will proc.
the only way i know is to create a click handler for each box manually.
hope i can find some answers
Using the Addhandler statement you can wire them all to the same routine. Then cast the sender object to interact with the PB that was clicked. OfType function.
Private Sub LoadME() Handles Me.Load
For Each pb As PictureBox In Me.Controls.OfType(Of PictureBox)()
'add all PB click events to a event sub
AddHandler pb.Click, AddressOf pb_Click
Next
End Sub
Private Sub pb_Click(sender As Object, e As EventArgs)
Dim pb = DirectCast(sender, PictureBox)
'this is the PB that was clicked
End Sub

How to change only one tooltip?

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")

Removing a collection of controls

I have a form (Form1) and it has 30 controls.
When I hit a button, I want to remove those 30 buttons and put other controls on the form.
Now, my problem is that this is to slow.
I have this list with controls I want to delete and I run through them with a For Each.
Private Sub ClearControls()
'removing the controls from Me.Controls
For Each Control As Control In ListToDelete
Me.Controls.Remove(Control)
Next
ListToDelete = New List(Of Control)
End Sub
Now, if you watch the form, you see the controls getting deleted 1 by 1. This action takes about 0.4 seconds (timed with the build-in stopwatch) and that's too long.
Are there any solutions to delete the controls in a faster way or is it only possible to delete the controls 1 by 1?
Maybe an important fact is that everything is connected with a database.
The controls are created by a class I defined myself (TableDrawer) and it creates a rectangle or circle (depends on info from the database).
I add the selfmade controls to the form and when I want to delete them, it takes 0.4 seconds to get other controls on the form - also with information out of my database.
Hopefully this clears some things up and I hope you can help me out... It really has to go a bit faster (I hope to get 0.1s or lower)
Hiding the Panel first seems to make the controls disappear quicker than just clearing the Panel. See this code:
Option Strict On
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Panel1.Visible = False
If Not Panel1.Controls.OfType(Of Button).Any() Then
For x As Integer = 1 To 10
For y As Integer = 1 To 10
Dim btn As New Button()
btn.Size = New Size(45, 45)
btn.Location = New Point((x - 1) * 45, (y - 1) * 45)
btn.Text = (x * y).ToString()
Panel1.Controls.Add(btn)
btn.Visible = True
Next
Next
End If
Panel1.Visible = True
End Sub
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Panel1.Visible = False
Panel1.Controls.Clear()
Panel1.Visible = True
End Sub
End Class
This code has 2 buttons, and a Panel. Button1 generates 100 buttons, places them on a Panel. Button2 hides the panel before removing them. Perhaps you can experiment with this idea.
It's not the deletion that tends to take the time - it's redrawing the form each time. Try surrounding your deletion code with calls to SuspendLayout and ResumeLayout
Private Sub ClearControls()
'removing the controls from Me.Controls
Me.SuspendLayout()
For Each Control As Control In ListToDelete
Me.Controls.Remove(Control)
Next
Me.ResumeLayout()
ListToDelete = New List(Of Control)
End Sub
Put the controls in a panel container control. Removing the panel container removes all child controls.
kindly never used remove and panel.removeat to remove any controls. It won't be able to delete last control in panel layout.Especially for panel.removeat will return out of index error once delete the last end control in panels. I'm also wondering need to know why this is problem appears?
Stored all the control name in string array, find those controls in panel and delete them, Try below code will help you delete all control in panel for one short. Try using with find and removeBykey function will make your task easier.
Dim ctrllist() as string
Dim counts = 0
For each control in Me.panel1.controls
redim Preserve ctrllist(0 to counts)
ctrllist(counts)=control.name
counts+=1
Next
For counts=Lbound(ctrllist) to Ubound(ctrlllist)
If me.panel1.controls.find(ctrllist(counts),True).Length>0 then
me.panel1.controls.removeBykey(ctrllist(counts))
End If
Next
Hope it will help.
Thanks user1884888! The technique helps me.
If I use Me.ScrollPanelControl.Controls.Clear() then the application goes unresponsive and there's no choice to terminate it from Task Manager but using this technique helps me.
This code is to help some one having the same problem.
While (True)
Dim count = Me.ScrollPanelControl.Controls.Count
If count <= 0 Then
Exit While
End If
Dim firstCtrl = CType(Me.ScrollPanelControl.Controls(0), MyControl)
If Not firstCtrl.IsMoving Then
If Me.ScrollPanelControl.Controls.Find(firstCtrl.Name, True).Length > 0 Then
Me.ScrollPanelControl.Controls.RemoveByKey(firstCtrl.Name)
End If
ElseIf count > 1 Then
firstCtrl = CType(Me.ScrollPanelControl.Controls(1), MyControl)
If Me.ScrollPanelControl.Controls.Find(firstCtrl.Name, True).Length > 0 Then
Me.ScrollPanelControl.Controls.RemoveByKey(firstCtrl.Name)
End If
Else
Exit While
End If
End While