On button click fill next picture box - vb.net

I'm writing a little program in VB and I'm stuck at a point where I want to add a specific image on a button click to a picturebox. The tricky part for me is, that each time I click the button, I want the image (from same location, e.g "C:\Test.jpg") to appear in the next picture box.
I tried to use a variable in the picturebox name and increase it on each click but it kept giving errors (must have used it wrong, obviously).
So to make it more clear:
I click Button1
image from location "C:\Test.jpg" appears in PictureBox1
I click Button1 again
image from location "C:\Test.jpg" appears in PictureBox2
etc.
As you can imagine, I'm not an expert in VB.NET so if you good people have any suggestions, thank you in advance :D
Vahur

Here's another approach using Controls.Find():
Public Class Form1
Private counter As Integer = 0
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
counter = counter + 1
Dim matches() As Control = Me.Controls.Find("PictureBox" & counter, True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is PictureBox Then
Dim pb As PictureBox = DirectCast(matches(0), PictureBox)
Using fs As New System.IO.FileStream("C:\Test.jpg", IO.FileMode.Open)
pb.Image = New Bitmap(Image.FromStream(fs))
End Using
End If
End Sub
End Class

Form level:
Private mPList As New List(of PictureBox)
Private pIndex as Integer = 0
Form Shown:
With mPlist
.Add(PictureBox1)
.Add(PictureBox2)
... continue as needed
Ens With
Button Click
' remove image from last picbox (????)
mPlist(pIndex).Image = Nothing
pIndex += 1
if pIndex > mPlist.Count-1 then pIndex = 0
mPlist(pIndex).Image.FromFile(filename)

The issue, I'm sure, is that you are trying to figure out how to increment a number and get a handle of the picture box to set the picture on. I am going to make a couple assumptions here, in that you are making a card game and you will have a set number of players. You have a couple options:
For the number, you can either set a PRIVATE variable in your form for holding the next number and on each button click, increment it by 1.
Private Player1NextImage as byte = 1
Private Player2NextImage as byte = 1
Private Player3NextImage as byte = 1
Private Player4NextImage as byte = 1
In the button click, for the specific player, you would want to get their picturebox that is next:
You can either keep the list of pictureboxes in a list, as show by #Plutonix, or you can just use a SELECT CASE statement and hardcode the pictureboxes.
Select Case Player1NextImage
Case 1
Player1Card1PictureBox.Image = ...
Case 2
Player1Card2PictureBox.Image = ...
Etc...
'Then increment the number
Player1NextImage += 1

There are multiple ways to do this. Let's say your variable is called Counter, and (assuming) that the parent of your pictureboxes is the main form / control, add this to your button click event:
Counter += 1
Dim Pic As PictureBox = CType(Me.Controls("PictureBox" & Counter.ToString), PictureBox)
If Pic Is Nothing Then
MsgBox("Could not find PictureBox")
Else
Pic.Image = Image.FromFile("C:\Test.jpg")
End If

Related

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)

Finding control in a Panel within a panel

I have a form in vb.net like so...
There is an outside panel named "pnlResults", Within that panel i have a further 10 panels.
As shown there is label with the text as "name" in each of these panels.
I would like to access these labels through a loop however I have tried the following without success.
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is Label Then
If ctrl.Name.StartsWith("lblName") Then
'Found the labels
End If
End If
Next
The names of all the labels I want to find start with "lblName", they are then identified individually buy a number from 1 to 10 following "lblname" i.e. "lblName1" etc all the way to 10
I believe this is due to the fact that a panel is its own container thus excluded from the loop of 'me.controls'. How do I get around this problem?
Using recursion
Private Sub findingAcontrol(ByRef panelx As Panel)
For Each Control As Control In panelx.Controls
If TypeOf Control Is Panel Then
Me.findingAcontrol(Control)
Exit Sub
End If
If Control.Name = "Button3" Then
MessageBox.Show(Control.Text)
End If
Next
End Sub
Add a button and call your function. For example:
Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
For Each Control As Control In Me.Controls
If TypeOf Control Is Panel Then
Me.findingAcontrol(Control)
Exit Sub
End If
Next
End Sub
Here is another solution. You are correct in thinking that since the inner panels are also containers that your code isn't finding controls within them.
This solution just simply goes one level and once it finds an inner panel, finds the label based on how many inner panels you've gone through.
Update: I had assumed this was a web application. Here is an example of something you could do with a windows form.
Dim i As New Integer
i = 0
For Each ctr As Control In pnlResults.Controls
If TypeOf ctr Is Panel Then
i += 1
Dim lblName As New Label
lblName = ctr.Controls.Find("lblName" + i.ToString(), False)(0)
'Do something
End If
Next
Leaving the code for a web application just in case:
Dim i As New Integer
i = 0
For Each ctr As Control In pnlResults.Controls
If TypeOf ctr Is Panel Then
i += 1
Dim lblName As New Label
lblName = ctr.FindControl("lblName" + i.ToString())
'Do something
End If
Next
Search the panel's control collection for panels only.
For Each pl As Panel In pnlResults.Controls.OfType(Of Panel)()
For i As Integer = 1 To 10
Dim lb As Label = pl.Controls("lblName" & i.ToString) 'is the label
Next
Next

Convert a string to

For example i have a program with 10 buttons and when i press a random one its name is saved to a string and i want to add 1 to the button's name for example if i pressed button1 ill alter the string to say button2 but now i cant use that string because it cant convert string to system.windows.forms.buttons, i have already tried the Me.Controls bu it didnt work for me.
Example:
dim stringy as string
dim integr as integer
dim buton as button
sub procedureee
stringy = stringy.remove(0,6)
integr = val(stringy) + 1
stringy = "Button" & integr
button.backcolor = white
end sub
Button1_Click
stringy = button1
procedureee
/* EDIT */
Im sorry i think i didnt make my sefl clear, everything in this code works for me except "stringy = button1" it says that string cannot be converted to system.windows.forms.button but thats exactly what i want to do, i have a program with 100 buttons and when any button is pressed it sets the value of ("Dim local as button") the variable local= the button pressed, and it works so i take that button.name and remove 1 from it so i get the value of the button above(PS: i have the buttons on a grid and vertically its from 1 to 10 and if i remove 1 il get the name of the button abore ex: button1gA3 becomes Button1gA2) but when i try to do this local2 = stringy it gives me that message(string cannot be converted to system.windows.forms.button) anyone knows how to solve this?
Thanks.
Here you go ...
Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.click
Dim FullButtonName As String = sender.text
Dim ButtonsNumber As String = FullButtonName.Replace("Button", "").Trim
Dim NewButtonNumber As Integer = CType(ButtonsNumber, Integer) + 1
sender.text = "Button " & NewButtonNumber.ToString
End Sub
You can use FindControl() to find a control by name. Note that it is not recursive, and so you'll need to call this method on the direct parent that contains your buttons.

Simple VB, handling many buttons

In my VB solution I have a lot of buttons arranged in a grid. When the program is loaded one of the buttons is randomly set to be the right one (You have to click that one to win). Can anybody point my in the right direction? Because there must be a better way than manually coding every single button, and creating an event handler for every single button.
You don't have to give me a working example, just an overall idea of how this is done.
Thanks.
If you want to arrange the buttons in a grid, either use the TableLayoutPanel or add the buttons directly to the form and calculate their positions. The TableLayoutPanel is useful if you want to arrange the buttons automatically when the form resizes, otherwise adding the buttons directly seems easier to me.
Add the buttons to an array defined at form level to make them easily accessible
Public Const NColumns As Integer = 5, NRows As Integer = 4
Private buttons As Button(,) = New Button(NColumns - 1, NRows - 1) {}
You can add the buttons easily in loops
For ix As Integer = 0 To NColumns - 1
For iy As Integer = 0 To NRows - 1
Dim btn = New Button()
btn.Text = String.Format("{0:d2}{1:d2}", ix, iy)
btn.Location = New Point(leftMargin + ix * xDistance,
topMargin + iy * yDistance)
btn.Size = New Size(buttonWidth, buttonHeight)
AddHandler btn.Click, Addressof Button_Clicked
buttons(ix, iy) = btn
Controls.Add(btn)
Next
Next
You can determine the winning button with a random generator. Define it as form member, not as local variable.
Private randomGenrator As System.Random = New System.Random()
Determine the coordinates
Dim xWins = randomGenrator.Next(NColumns) 'Returns a number between 0 and NColumns-1
Dim yWins = randomGenrator.Next(NRows)
The click handler looks like this
Private Sub Button Button_Clicked(sender As Object, e As EventArgs)
If sender = buttons(xWins, yWins) Then
'You win
Else
'You loose
End
End Sub
First, you said you want a grid of buttons, so you have to have a FlowLayoutPanel control in your form in order to let the buttons you want to add, to be arranged automatically.
Second, you have to make use of a for loop, r any kind of look, in order to add the buttons to be added to previously added 'FlowLayoutPanel'.
class Answers
Dim strAnswerText as string
Dim AnswerFlag as Boolean
End Class
Sub LoadForm(byval a_Answers as Answer())
Dim i as Integer = 0
Dim b as Button
For(i=0;i<NUM_OF_BUTTONS;i++)
b = New Button()
b.Text = "Choice -" & i & "- " & a_Answers(i).strAnswerText
b.Tag = a_Answers(i).AnswerFlag
'Supposing that the FlowLayoutPanel control name is fl
AddHandler b.Click, Addressof Clicked
fl.controls.Add(b)
End For
End Sub
Sub Button Clicked(sender as object, e as EventArgs)
if sender.Tag = True
'True answer
else
'Wrong answer
end if
End Sub

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