How to handle multiple click events with same Sub - vb.net

I'm making a game for my visual basic course. I have multiple picture boxes that when clicked will reveal a hidden image individually. The point of the game is to find the matching pictures (simple enough).
On the easiest level, I have 16 picture boxes. The number of picture boxes increases as the difficulty increases.
For each picture box, I currently have an event handler as follows (default created by visual studio):
Private Sub pictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles pictureBox1.Click
Inside, I plan to use this to change the image in the picture box, as follows:
pictureBox1.Image = (My.Resources.picture_name)
I would like to know if there is a way to have one Sub handle ALL the button clicks, and change the appropriate picture box, instead of having 16 separate handlers. For example:
Private Sub pictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles pictureBox1.Click, pictureBox2.Click, pictureBox3.Click, ... pictureBox16.Click
And do the following:
' Change appropriate picture box
Here's what it looks like (for now):

To find out which PictureBox was clicked you just have to look at the sender variable. Obviously you have to convert it from the Object type to the PictureBox type:
Dim ClickedBox As PictureBox
ClickedBox = CType(sender, PictureBox)

Personally what I would do would be to attach your common EventHandler to your PictureBox, give each PictureBox a Tag for an index, unless you want to do your selection on the name. Then you do something like this.
Private Sub PictureBox1_Click(sender As System.Object, e As System.EventArgs) Handles PictureBox1.Click, PictureBox2.Click, ...
Dim pb As PictureBox = CType(sender, PictureBox)
Select Case CInt(pb.Tag)
Case 0
pb.Image = My.Resources.PictureName1
Case 1
pb.Image = My.Resources.PictureName2
...
End Select
End Sub

According to what I've read, DirectCast is preferred over CType
DirectCast can be combined with 'With/End With' as shown here:
Private Sub PictureBox1_Click(sender As System.Object, e As System.EventArgs) Handles PictureBox1.Click, PictureBox2.Click, ...
With DirectCast(sender, PictureBox)
Select Case CInt(.Tag)
Case 0
.Image = My.Resources.PictureName1
Case 1
.Image = My.Resources.PictureName2
...
End Select
End With
End Sub
I've tried the following also but this causes strange problems (controls disappearing).
Using cbMe as CheckBox = DirectCast(sender, CheckBox)
cbMe.Checked = True
End Using

Iterate through all controls for example
For Each ctr As Control In Me.Controls
If TypeOf ctr Is PictureBox Then
If ctr Is ActiveControl Then
' Do Something here
End If
End If
Next

Related

Why doesn't Me.Controls.OfType work in VB.NET?

I'm using .NET Framework 4.7.2 for reference.
I'm using Me.Controls.OfType for automated handles in an event in my form.
Sub AddTextBoxes_TextChanged()
Dim textboxes = Me.Controls.OfType(Of TextBox)()
Console.WriteLine(textboxes.Count)
For Each txt In textboxes
AddHandler txt.TextChanged, AddressOf AllTextBoxes_TextChanged
Next
End Sub
Private Sub SampleForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddTextBoxes_TextChanged()
End Sub
Private Sub AllTextBoxes_TextChanged(sender As Object, e As EventArgs)
' ...
End Sub
However, the For loop doesn't work, so I checked if there are actual textbox controls within textboxes with Console.WriteLine(textboxes.Count). Well, the result is 0. I've checked multiple times in the Form Design for textboxes, and they exist. Why doesn't Controls.OfType(Of TextBox)() work?
Place your handler in the code for the form.
In design view select the one of the text boxes.
In the Properties window select the lightning bolt to display all the events available for a TextBox. Choose the TextChanged event and the drop down box arrow. Your AllTextBoxes_TextChanged method will be listed because the signature matches. Select your method and the Handles code will be added to the method.
Do the same for each text box you wish to use this method.
Of course you can always type the extended Handles clause.
However, I don't see what is wrong with your code.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim textboxes = Controls.OfType(Of TextBox)() '.ToList
For Each txt In textboxes
AddHandler txt.TextChanged, AddressOf AllTextBoxes_TextChanged
Next
End Sub
Private Sub AllTextBoxes_TextChanged(sender As Object, e As EventArgs)
Dim tb = DirectCast(sender, TextBox)
MessageBox.Show($"The text changed event fired by {tb.Name}")
End Sub
Works for me.

VB.NET - How to add a large amount of events to a single handle?

Recently I've been working on a program that has a few TextBoxes, CheckBoxes, ComboBoxes, etc., and I found that making one function handle multiple events is pretty simple, you just separate the events with a comma and the code recognizes the inidvidual events.
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click, Button2.Click
MsgBox("Hello World!")
End Sub
However, when you start to have a large number of events that you want handled by the same function, it gets a bit messy.
Private Sub Checks_CheckedChanged(sender As Object, e As EventArgs) Handles chkInput1.CheckedChanged, chkInput2.CheckedChanged, chkInput3.CheckedChanged, chkInput4.CheckedChanged, checkInput5.CheckedChanged, chkOutput.CheckedChanged
MsgBox("Checks Changed!")
End Sub
You can use the line continuation character _ to make it look a little better.
Private Sub Checks_CheckedChanged(sender As Object, e As EventArgs) Handles _
chkInput1.CheckedChanged, chkInput2.CheckedChanged, chkInput3.CheckedChanged, _
chkInput4.CheckedChanged, checkInput5.CheckedChanged, chkOutput.CheckedChanged
MsgBox("Checks Changed!")
End Sub
But you still end up with a nasty block of text. Is there a more clean/concise way of doing this? What I have in mind is that it would be really nice to give an array of object events as an argument but I don't think that's possible.
You could do this by using the
AddHandler ObjectName.EventName, AddressOf EventHandlerName
syntax
It's simple enough to write a Sub that takes an array of object and loops over them to add the handler for each event.
For checkboxes:
Public Sub AddHandlerSub(PassedArray As CheckBox())
For Each item As CheckBox in PassedArray
AddHandler Item.CheckedChanged, AddressOf EventHandlerName
next
End Sub
You can simply iterate the controls in the controls collection and not fuss with an array at all. You can also do further conditions if you want to exclude/add any given control, such as in the example of the TextBox Case in the following example.
Private Sub DataTables_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each Ctrl As Control In Panel1.Controls
Select Case Ctrl.GetType
Case GetType(CheckBox)
AddHandler DirectCast(Ctrl, CheckBox).CheckedChanged, Sub(S As Object, EA As EventArgs)
Dim ChkBox As CheckBox = DirectCast(S, CheckBox)
'do something with ChkBox
End Sub
Case GetType(TextBox)
Select Case Ctrl.Name
Case "TextBox1", "TextBox2" 'Add handle only to these contrls
'Or you could add Case Else and put the below handle within it
'Then this becomes an exclusion case
AddHandler DirectCast(Ctrl, TextBox).TextChanged, Sub(S As Object, EA As EventArgs)
Dim TxtBox As TextBox = DirectCast(S, TextBox)
'do something with TxtBox
End Sub
End Select
End Select
Next
End Sub
Additional Information: You can select procedures as Event Handlers by selecting the control. Then in the Properties window click the lightning bolt to display Events. Selected the event you wish to assign a handler and then the drop down arrow to the right. The resulting list will display all the Subs that match the signature of that event. Select the one you want and the designer will write or append the control to the Handles clause.
Add a procedure to the Form with a signature that matches the event.
Private Sub MultipleButtons(sender As Object, e As EventArgs)
End Sub
In the dropdown the list contains all Subs that match the signature of the event.
The designer writes the Handles clause
Private Sub MultipleButtons(sender As Object, e As EventArgs) Handles Button5.Click
End Sub

viewing image in picturebox using listview in vb.net 2008

I am working in vb.net 2008 with listview control. I added some images in listview.items using imagelist. now I want to show that image in picture box when I clicked on button. I had tried some time for this but getting error everytime.
Here is the code :
Public Class Form1
Private Sub ListView1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseClick
PictureBox1.Image = Bitmap.FromFile(ListView1.Items.Item(1).ToString)
End Sub
End Class
If you want the picture to show when you click a button then I think you want to use Button_Click instead of ListView_Click
Private Sub MyButton_Click(sender As Object, e As EventArgs) Handles MyButton.Click
'Also, might be easier just to do this:
PictureBox1.Image = ImageList1.Images(0)
End Sub
Then you can set different buttons for your different pictures

Graphics.DrawRectangle not working in control events

First of all thank you for taking the time out of your busy schedule to assist me.
I am developing a project (Win Application) with a Form and 3 textboxes (TextBox1, TextBox2 and TextBox3).
I need draw a rectangle around the textbox when focused this.
The code is:
Private Sub TextBox123_Enter(sender As Object, e As System.EventArgs) Handles TextBox1.Enter, TextBox2.Enter, TextBox3.Enter
Using g As Graphics = Me.CreateGraphics
Dim r As Rectangle = sender.Bounds
r.Inflate(4, 4)
g.DrawRectangle(Pens.Blue, r)
End Using
End Sub
The problem is the following:
The first time the textbox1 gains focus rectangle is not drawn.
The first time the textbox2 gains focus rectangle is not drawn.
Why not the rectangle is drawn when the first two events enter are fired?
Drawing with CreateGraphics is almost always not the correct approach. If you notice also, when you move from one box to another, the old rectangle is not being erased. You need to use the Form_Paint event to get it to work right. Or...perhaps simpler would be to create a UserControls which is 1-2 pixels larger than a child TextBox and set the backcolor of the UserControl canvas, draw your rectangle when the control gets the focus.
For form paint:
Public Class Form1
Private HotControl As Control
If you are only going to do TextBoxes, you can declare it As TextBox. This way it allows you to do the same for other control types. Set/clear the tracker:
Private Sub TextBox3_Enter(sender As Object, e As EventArgs) Handles TextBox3.Enter,
TextBox2.Enter, TextBox1.Enter
HotControl = CType(sender, TextBox)
Me.Invalidate()
End Sub
Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave,
TextBox2.Leave, TextBox3.Leave
HotControl = Nothing
Me.Invalidate()
End Sub
The Me.Invalidate tells the form to redraw itself, which happens in Paint:
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
If HotControl IsNot Nothing Then
Dim r As Rectangle = HotControl.Bounds
r.Inflate(4, 4)
e.Graphics.DrawRectangle(Pens.Blue, r)
End If
End Sub
You should also turn on Option Strict.
Try this in the click event handler
Private Sub TextBox1_Click(sender As Object, e As EventArgs) Handles TextBox1.Click
Using g As Graphics = Me.CreateGraphics()
Dim rectangle As New Rectangle(TextBox1.Location.X - 1, _
TextBox1.Location.Y - 1, _
TextBox1.Size.Width + 1, _
TextBox1.Size.Height + 1)
g.DrawRectangle(Pens.Blue, rectangle)
End Using
End Sub

Drop onto FlowLayoutPanel

Hi guys Hope all is well
I am wondering(struggling) the following:
I have 5 flowLayoutPanels and 5 PictureBoxes i want to be able to move anyone of the picture boxes over anyone the FLP at run time and have the layout panel add it to FLP.controls.Add()....
I've been at it for Hours and now ill swallow my pride -
I have done the following To get it working, but here i have to manually specify which PixBox intersects with which FLP and i dont want 25 if statements
Private Sub cpbPic1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cpbPic1.MouseUp
If (flpDock1.HasChildren = False) Then 'Test to see if panel is filled
If CBool(CustomPictureBox.IntersectingObjects(cpbPic1, flpDock1)) Then
flpDock1.Controls.Add(cpbPic1) 'Add Pic to Panel
End If
End Sub
cpb: CustomPictureBox
you could always do this:
Private Sub cpbPic1_MouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles cpbPic1.MouseUp, cpbPic2.MouseUp, cpbPic3.MouseUp,cpbPic4.MouseUp,cpbPic5.MouseUp
If Not flpDock1.HasChildren Then 'Test to see if panel is filled
If CBool(CustomPictureBox.IntersectingObjects(TryCast(sender,CustomPictureBox), flpDock1)) Then
flpDock1.Controls.Add(TryCast(sender,CustomPictureBox)) 'Add Pic to Panel
End If
End Sub
This will reduce the amount of code you'll have to write significantly, you can further reduce this amount if you think about how to utilize the fact that the event handler passes the Object which raises the flag, like I did here.
Also you can use arbitrary big amount (i think) of objects in a handler as long as they raise the same event
well this can be a work around for what you want to do.
you also have to enable allowdrop to the flowpanels
Private Function FindControl(ByVal ControlName As String, ByVal CurrentControl As Control) As Control
' get the control you need
Dim ctr As Control
For Each ctr In CurrentControl.Controls
If ctr.Name = ControlName Then
Return ctr
Else
ctr = FindControl(ControlName, ctr)
If Not ctr Is Nothing Then
Return ctr
End If
End If
Next ctr
End Function
Private Sub me_DragEnter(sender As Object, e As DragEventArgs) Handles FLP1.DragEnter,FLP2.DragEnter,FLP3.DragEnter
' call the copy effect
If (e.Data.GetDataPresent(DataFormats.Text)) Then
e.Effect = DragDropEffects.Copy
End If
End Sub
Private Sub me_DragDrop(sender As Object, e As DragEventArgs) Handles FLP1.DragDrop,FLP2.DragDrop,FLP3.DragDrop
' get the FLp you're gonna drop the control onto
Dim c As control =FindControl(e.Data.GetData(DataFormats.Text), me)
sender.Controls.Add(c)
end sub
Private Sub Pictureboxs_MouseDown(sender As Object, e As MouseEventArgs) Handles Label1.MouseDown, PB.MouseDown
sender.DoDragDrop(sender.Name, DragDropEffects.Copy)
End Sub
hope that this helps you :) (sorry for my bad english)