Simple VB, handling many buttons - vb.net

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

Related

I'm trying to add pictureboxs to an array so I can reference them later as they are being created programmatically

So far all I have is this code...
Dim lineb As New PictureBox
lineb.Size = New Size(24, 24)
lineb.BackColor = Color.FromArgb(0, 192, 192)
lineb.Location = New Drawing.Point(xb, yb)
Controls.Add(lineb)
lineb.Name = "lineb" + CStr(creatorb)
creatorb += 1
This generates a infinite line of pictureboxs as it is inside a timer.tick event. Xb and Yb continuously move and this works. I need to figure out how to add each picturebox to an array or another way to reference them later. Each one gets created and renamed to lineb + 1...2...3...4... etc.
Here is an example of a way to keep track of dynamically created controls. It will be easier than adding every control to an array.
Dim pbox as New PictureBox
With pbox
.location = New Point(xb, yb)
.size = New Size(24, 24)
.Tag = "box1" 'choose a tag that will help identify it later
' and so on
Addhandler pbox.click, AddressOf pbox_click '' to add a click event handler
End With
Me.Controls.add(pbox)
Private Sub pbox_click(sender As Object, e As EventArgs)
Dim the_sender As PictureBox = DirectCast(sender, PictureBox)
Dim reference As String = DirectCast(the_sender.tag, String)
''' do whatever you need to do now that you know which picturebox was clicked.
End Sub
Alternatively you can always loop through your controls to find the one you are looking for:
For Each Ctrl As Control In Me.Controls
If TypeOf Ctrl Is PictureBox Then
If ctrl.tag = "box1" Then '''or whetever you are looking for
''' do your stuff here
End If
End If
Next

VB: How can I make buttons over a picture box transparent?

in my program I have a picture box with a specific width x and height y. Then the user can enter an elementsize (e.g. 10) and then the programm will create buttons with the width and height of the elementsize over the picture box. The thing is that these buttons need to be transparent so only their border is visible. That means that through the buttons you can see the content of the picture box.
So this is my code for creating these buttons when I click on my start button:
Imports System.Math
Public Class Form1
Dim x As Double
Dim y As Double
Dim elsize As Integer
Dim numberofbuttons As Integer
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
x = 400
y = 200
elsize = 20
numberofbuttons = Round(x / elsize) * Round(y / elsize)
Dim i As Integer
Dim j As Integer
For j = 1 To Round(y / elsize)
For i = 1 To Round(x / elsize)
Dim Btn As New Button
Btn.Width = elsize
Btn.Height = elsize
Btn.Location = New Point(elsize * (i - 1), elsize * (j - 1))
Btn.BackColor = Color.Transparent
PictureBox1.Controls.Add(Btn)
Next
Next
End Sub
End Class
I use
Btn.BringToFront()
to put these buttons in front of the picture box and I wanted to use
Btn.BackColor = Drawing.Color.Transparent
to make them transparent but this won't work. Has anybody any ideas how to do this? Also I wanted to put these buttons in the coordinate system of the picture box and not of the form1. I thought that this is possible through
Btn.Parent = PictureBox1
but the buttons always use the coordinate system of the form.
Thanks in advance for your help.
This:
Me.Controls.Add(Btn)
is setting the Parent of the Button to be the form. You don't Add to one parent and assign a different parent explicitly. Do one or the other. In short, get rid of that Add call and you should be good to go. Alternatively, get rid of the Parent assignmnent and Add to the Controls collection of the PictureBox instead. I'd suggest the latter.
Either way, DO NOT display the Button until you have configured it, i.e. set the Parent or call Controls.Add last.
EDIT: I tested this code and it worked as expected:
Dim btn As New Button
With btn
.Size = mySize
.Location = myLocation
.BackColor = Color.Transparent
.FlatStyle = FlatStyle.Flat
PictureBox1.Controls.Add(btn)
End With

Update text in dynamically created label

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

How to manage dynamically created controls in VB.NET?

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...

On button click fill next picture box

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