How to create an array of PictureBoxes and use it? - vb.net

I thought "brickn()" will creat an array and "NoOfBrick" will be the array length and all array will store in it
Dim brickWidth as Integer = 0
Dim Brickn() As PictureBox
dim NoOfBrick as Integer ' array length
Public Function CreateBrick() As PictureBox
Dim myBrickn As New PictureBox
With myBrickn
.Size = Brick.Size
.Left = BrickWidth
.Top = 0
.Image = Brick.Image
.SizeMode = PictureBoxSizeMode.StretchImage
.BackColor = Color.Black
End With
Return myBrickn
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i = -75 To Me.Width
Brickn(NoOfBrick) = CreateBrick() 'and also how to add array/list here
Me.Controls.Add(Brickn(NoOfBrick))
BrickWidth += 170 'increasing brick.left on every new brick is created
i += 170 ' increasing looop count according to brick needed
NoOfBrick +=1
Next
End Sub
But this code is throwing error on "Me.Controls.Add(Brickn(NoOfBrick))"
this error
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I thought if I will get array of PictureBoxes then I can access control on them for this
Private Sub Boll_control_Tick(sender As Object, e As EventArgs) Handles Boll_control.Tick
If Ball.Bounds.IntersectsWith(brickn(NoOfBrick).Bounds) Then
Me.Controls.Remove(brickn(NoOfBrick))
End If
End Sub

Change:
Dim Brickn() As PictureBox
To:
Dim Brickn As New List(Of PictureBox)
Then, when you want to Add something to "Brickn", you'd do:
Brickn.Add(CreateBrick())
You can still access "Brickn" with array syntax, such as:
Brickn(NoOfBrick).Bounds
The difference is that a List will automatically grow or shrink in size as you add/remove things to it, while an Array has a fixed size (you never actually created the array in your code and gave it a size).
You can do hit testing with something like:
Dim hit = Brickn.Where(Function(brick) Ball.Bounds.IntersectsWith(brick.Bounds))
For Each brick As PictureBox In hit
Me.Controls.Remove(brick)
Brickn.Remove(brick)
Next

Related

How to use properties of dynamically created picturebox in vb.net

I was trying to remove this "brickn" when ball intersect with this brick
but i am facing problem that "brickn" is not declared any helps?
there is code
Dim brickWidth as Integer = 0
Public Function CreateBrick() As PictureBox
Dim Brickn As New PictureBox
Me.Controls.Add(Brickn)
Brickn.Size = Brick.Size
Brickn.Left = BrickWidth
Brickn.Top = 0
Brickn.Image = Brick.Image
Brickn.SizeMode = PictureBoxSizeMode.StretchImage
Brickn.BackColor = Color.Black
Return Brickn
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i= -75 To Me.Width
CreateBrick()
BrickWidth += 170 ' increasing brick.left on every new brick is created
i += 170 ' increasing looop count according to brick needed
Next
Private Sub Boll_control_Tick(sender As Object, e As EventArgs) Handles Boll_control.Tick
If Ball.Bounds.IntersectsWith(brickn.Bounds) Then
Me.Controls.Remove(brickn)
End If
End Sub
why this "brickn" is not saying not declared in "boll control tick " timer
You are instantiating brickn withing CreateBrick. You are returning it as the result of that function, but not assigning it to anything. So it's scope is limited to the CreateBrick fuction only, which is why it's not accessible from Boll_control_Tick.
Also you are then trying to create multiple instances of it with using a single object.
This code will allow you to create one or more. You will then need to rework your Boll_control_Tick to work out whether it intersects with any. You may want to create a list or array of PictureBox objects as Brickn instead of one.
Dim brickWidth as Integer = 0
Dim Brickn As PictureBox ' This may be better as a list or an array
Public Function CreateBrick() As PictureBox
Dim myBrickn As New PictureBox
'Note - no idea what Brick is here or where it comes from
Brickn.Size = Brick.Size
Brickn.Left = BrickWidth
Brickn.Top = 0
Brickn.Image = Brick.Image
Brickn.SizeMode = PictureBoxSizeMode.StretchImage
Brickn.BackColor = Color.Black
Return myBrickn
End Function
and then in your Form_Load method:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i= -75 To Me.Width
Brickn = CreateBrick() ' or add to your List/Array here
Me.Controls.Add(Brickn)
BrickWidth += 170 ' increasing brick.left on every new brick is created
i += 170 ' increasing looop count according to brick needed
Next
That will add multiple picture boxes to your controls and, if you want, create a list or array of them for easy access. You will need to loop through those in Boll_control_Tick to see if ball bounds intersects with any and then remove that specific one only

How to add infinite components when a button is clicked

I have a social media WinForm. I have a function that basically makes a new picture box when a button is clicked
Public Sub NewPost()
picture as new picturebox
picture.Width = 208
picture.Height = 264
picture.Image = Form2.PictureBox1.Image
picture.Location = New Point(258, 60)
End Sub
The thing is it only generates 1 new picture box because I have to make a new variable each time I want to add a picturebox, and eachtime I have to have a new name. I know my question Is a bit confusing but help would be nice thanks
If you want to trap events for your dynamic PictureBoxes, then you'll have to abandon the WithEvents model and move to using AddHandler.
Here's a quick example where the name of the PictureBox is displayed when it is clicked. Note that I am not setting a Location since they are being added to a FlowLayoutPanel which takes care of the placement for you:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
NewPost()
End Sub
Public Sub NewPost()
Dim picture As New PictureBox
picture.Width = 208
picture.Height = 264
picture.BorderStyle = BorderStyle.FixedSingle
' ...etc...
Dim index As Integer = FlowLayoutPanel1.Controls.Count + 1
picture.Name = "pb" & index
AddHandler picture.Click, AddressOf picture_Click
FlowLayoutPanel1.Controls.Add(picture)
End Sub
Private Sub picture_Click(sender As Object, e As EventArgs)
Dim pb As PictureBox = DirectCast(sender, PictureBox)
Debug.Print(pb.Name)
End Sub
End Class
because I have to make a new variable each time
Not necessarily. You just want to keep a reference to the object. That reference doesn't need to be its own variable, it can just as easily be an element in a list. For example, suppose on your form you have a list of PictureBox objects as a class-level member:
Dim pictureBoxes As New List(Of PictureBox)()
Then in your method you can just add to that list:
Public Sub NewPost()
Dim pictureBox As New PictureBox
pictureBox.Width = 208
pictureBox.Height = 264
pictureBox.Image = Form2.PictureBox1.Image
pictureBox.Location = New Point(258, 60)
Me.pictureBoxes.Add(pictureBox)
End Sub
In this case the pictureBox variable is local to the NewPost method and gets re-created each time. But pictureBoxes is a class-level member and keeps track of the growing list of PictureBox objects that you're creating.
You can use a for while loop to create n number of objects
You can use the existing ControlCollection
Public Function NewPost() As String
Dim picture As New PictureBox
'your code
picture.Name = "Pb" & Form2.Controls.OfType(Of PictureBox).Count
Form2.Controls.Add(picture)
Return picture.Name
End Function
then you can retrive it
DirectCast(Form2.Controls(NewPost), PictureBox).Image = Form2.PictureBox1.Image
'OR
DirectCast(Form2.Controls("Pb12"), PictureBox).Image = Form2.PictureBox1.Image

setting every value in a list to something in vb

Let's say I want to set the property of every item in a list like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim test As New List(Of PictureBox)
For q = 1 To 25
Dim picbox As New PictureBox
test.Add(picbox)
Next
timer tick
test.Item(everything in list).Top -= 3
End Sub
Can I do it all at once instead of iterating and setting each value separately?
You should just update the color before adding it to your list:
For q = 1 To 25
picBox.BackColor = Color.AliceBlue
test.Add(picbox)
Next
As an aside, do you realize that you are adding the same item 25 times? If you want different instances you'd need to create a new one in your loop:
For q = 1 To 25
picBox = New PictureBox
picBox.BackColor = Color.AliceBlue
test.Add(picbox)
Next
Just for fun, you could rewrite your entire snippet like so to get a list of 25 PictureBoxes:
Dim test = Enumerable.Range(1,25) _
.Select(Function(i) New PictureBox With {.BackColor = Color.AliceBlue}) _
.ToList

How can I respond to events raised by programmatically created UI elements?

I'm creating a board game for a piece of coursework. For the board, I'm using some nested For loops running through a 2D array to generate a "Space" object at each square.
The Space object contains a picturebox and some data about that space.
How can I handle events caused by clicking on the generated picturebox without having to hard-code it for each space?
I noticed this question seems to address this, but it's in C# and I couldn't translate it to VB.Net.
Edit:
This is how the board is generated
Dim board(23, 24) As Space
Private Sub GenerateBoard()
Dim spaceSize As New Size(30, 30)
Dim spaceLocation As New Point
Dim validity As Boolean
For Y = 0 To 24
For X = 0 To 23
spaceLocation.X = 6 + (31 * X)
spaceLocation.Y = 6 + (31 * Y)
If validSpaces(Y).Contains(X + 1) Then
validity = True
Else
validity = False
End If
board(X, Y) = New Space(validity, spaceSize, spaceLocation)
Me.Controls.Add(board(X, Y).imageBox)
board(X, Y).imageBox.BackColor = Color.Transparent
board(X, Y).imageBox.BringToFront()
Next
Next
End Sub
Space Class:
Public Class Space
Dim _active As Boolean
Dim _imageBox As PictureBox
Public Sub New(ByVal activeInput As Boolean, ByVal size As Size, ByVal location As Point)
_active = activeInput
_imageBox = New PictureBox
With _imageBox
.Size = size
.Location = location
.Visible = False
End With
End Sub
Property active As Boolean
Get
Return _active
End Get
Set(value As Boolean)
_active = value
End Set
End Property
Property imageBox As PictureBox
Get
Return _imageBox
End Get
Set(value As PictureBox)
_imageBox = value
End Set
End Property
Public Sub highlight()
With _imageBox
.Image = My.Resources.Highlighted_slab
.Visible = True
End With
End Sub
End Class
First all controls created by designer(textbox, label...) a generated by code too, but VisualStudio write this for you. If you open Designer file(yourForm.Designer.vb), then you can see all code how to generate a controls.
If you want a create event handler for your pictureBox , then:
//Initialize control
Private WithEvents _imageBox as PictureBox
Then create a event handler method:
Private Sub imageBox_Click(sender as Object, e as EventArgs)
//Your code
End Sub
Then in VB.NET you can assign a Event handler to the Event in two ways
first: In class constructor after you created a pictureBox( New PictureBox()) add
AddHandler Me._imageBox, AddressOf Me.imageBox_Click
second: On line we you created a event handler add next:
Private Sub imageBox_Click(sender as Object, e as EventArgs) Handles _imageBox.Click
//Your code
End Sub
And remember add your pictureBox to form controls YourForm.Controls.Add(spaceInstance.ImageBox)

Remove LineShape from Windows Form, LineShape and ShapeContainer Arrays

I am using the code below to add multiple LineShape controls to a Windows Form. Note the globally declared mLineShapes() and mShapeContainter() arrays (at bottom of code) which store each new LineShape object once it's created.
At present, I have been unsuccessful at removing a given LineShape control from the form (even if I know its array index), and also cannot removing an array element without causing a Nothing for the removed element. Obviously, once I remove the element from these arrays, it requires that all the remaining elements with greater indices are copied to lower values to fill in the Nothing voided element. Given these circumstances, can lists be used instead of the mLineShapes() and mShapeContainer() arrays?
enter code here' create new ShapeContainer
Dim sSCTemp As New ShapeContainer
' add ShapeContainer to Form
sSCTemp.Parent = Me
' create new LineShape
Dim sLSTemp As New LineShape
sLSTemp.BorderColor = Color.Black
sLSTemp.BorderWidth = 2
sLSTemp.Cursor = Cursors.Cross
' add LineShape to ShapeContainer
sLSTemp.Parent = sSCTemp
' set starting and ending coordinates for the line
sLSTemp.StartPoint = New System.Drawing.Point(siSCCount * 20, 60 + siSCCount * 60)
sLSTemp.EndPoint = New System.Drawing.Point(100 + siSCCount * 20, 110 + siSCCount * 60)
' set new LineShape to top of z-order
sLSTemp.BringToFront()
sSCTemp.BringToFront()
' connect ContextMenuStrip to LineShape
sLSTemp.ContextMenuStrip = mLsCtm1
' add new LineShape to arrays
ReDim Preserve mLineShapes(siSCCount)
ReDim Preserve mShapeContainer(siSCCount)
mLineShapes(siSCCount) = sLSTemp
mLineShapes(siSCCount).Name = "LineShape" & siSCCount
mShapeContainer(siSCCount) = sSCTemp
mShapeContainer(siSCCount).Name = "ShapeContainer" & siSCCount
In addition to the above, the endpoints of each
LineShape are selected from the arrays so that they can be moved. An example is below:
Dim siSCId As Integer
Dim myShapeContainer As ShapeContainer
myShapeContainer = CType(sender, ShapeContainer)
Dim myLineShape As LineShape
' get index of the actual ShapeContainer in ShapeContainer array
siSCId = Array.IndexOf(mShapeContainer, sender)
If siSCId > -1 Then
myLineShape = mLineShapes(siSCId)
If MouseIsNearBy(myLineShape.EndPoint) Then
myLineShape.BorderColor = Color.Red
NearLineEndPoint = True
End If
If MouseIsNearBy(myLineShape.EndPoint) = False Then
myLineShape.BorderColor = Color.Black
NearLineEndPoint = False
End If
If (dragStartPoint) Then
myLineShape.StartPoint = New Point(oldStartPoint.X + e.X - oldMouseX, oldStartPoint.Y + e.Y - oldMouseY)
End If
End If
Therefore, If I simply add a new LineShape to the form controls without using the mLineShapes() ans mShapeControl() arrays, how can I modify the above code (which finds the LineShape in the storage arrays) so that the line can be modified? I think that if I click on a LineShape, I can get its name using .sourcecontrol or .parent?
UPDATE 5/9/2019
After right clicking on a control on Form1 and selecting the "Link" command from a ContextMenuStrip, the following method (ctmsconnect) is fired to draw a new LineShape control that the user then drags and drops on to the next control in the workflow. Question is, is the list of LineShapes ("Lines") not needed?
(in Form1 class declarations):
Dim SC As New ShapeContainer
Dim Lines As New List(Of LineShape)
Private Sub ctmsconnect_Click(sender As System.Object, e As System.EventArgs) Handles ctmsconnect.Click
mLineWidth = 1
Dim myItem As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim cms As ContextMenuStrip = CType(myItem.Owner, ContextMenuStrip)
Dim x As Integer = cms.SourceControl.Right - 2
Dim y As Integer = cms.SourceControl.Top + (cms.SourceControl.Height / 2 - 12)
Dim LS As New LineShape
NumLineShapes += 1
LS.Name = "LineShape" & NumLineShapes
LS.BorderColor = Color.Black
LS.BorderWidth = 2
'Set starting and ending coordinates for the line
LS.StartPoint = New Point(x, y)
LS.EndPoint = New Point(x + 80, y - 5)
'Set new LineShape to top of z-order
LS.BringToFront()
Dim nxgContextMenuStrip As New ContextMenuStrip
LS.ContextMenuStrip = nxgContextMenuStrip
LS.Tag = "LineShape" & NumLineShapes & "_Delete"
'Attach an event handler for the ContextMenuStrip control's Opening event.
AddHandler nxgContextMenuStrip.Opening, AddressOf cms_Opening
numconnectedlineendpoints += 1
Dim myValues As New List(Of String)
myValues.Add(cms.SourceControl.Name)
DropLineOriginalObjectName = cms.SourceControl.Name
OrigDropControl = cms.SourceControl
myValues.Add(LS.Name)
myValues.Add("linestart")
dicGUIControls.Add(numconnectedlineendpoints, myValues)
Lines.Add(LS)
SC.Shapes.Add(LS)
Me.Refresh()
End Sub
You shouldn't need the arrays, just use the controls collection. Instead of setting the parent of the controls, you should probably add them to the collection:
'sSCTemp.Parent = Me
Me.Controls.Add(sSCTemp)
To remove them, you can reference them by the name property:
If Me.Controls.ContainsKey("ShapeContainer1") Then
Me.Controls.RemoveByKey("ShapeContainer1")
End If
The shape controls inside the ShapeContainer have to be accessed through the Shape collection:
If Me.Controls.ContainsKey("ShapeContainer1") Then
Dim sc As ShapeContainer = DirectCast(Me.Controls("ShapeContainer1"), ShapeContainer)
If sc.Shapes.ContainsKey("LineShape2") Then
sc.Shapes.RemoveAt(sc.Shapes.IndexOfKey("LineShape2"))
End If
End If
Example of reading the StartPoint and EndPoint properties:
Dim sb As New StringBuilder
For Each ls As LineShape In Me.ShapeContainer1.Shapes
sb.AppendLine(ls.StartPoint.ToString & " - " & ls.EndPoint.ToString)
Next
MessageBox.Show(sb.ToString)
Note: ShapeContainer Class makes a special note:
Be careful that you do not create more than one ShapeContainer for each form or container; doing this may introduce unexpected behavior. If you add a design-time line or shape control to a form or container after you write code to create one programmatically, you should modify that code to use the ShapeContainer created by the designer.
Public Class Form1
Dim canvas As New Microsoft.VisualBasic.PowerPacks.ShapeContainer
' Set the form as the parent of the ShapeContainer.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
canvas.Parent = Me
' Set the ShapeContainer as the parent of the LineShape.
End Sub
Private Sub Form1_MouseClick(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
If RadioButton1.Checked = True Then
Dim line1 As New Microsoft.VisualBasic.PowerPacks.LineShape
line1.Parent = canvas
' Set the starting and ending coordinates for the line.
line1.StartPoint = New System.Drawing.Point(Me.Width / 2, 0)
line1.EndPoint = New System.Drawing.Point(e.X, e.Y)
TextBox1.Text = canvas.Shapes.Count.ToString
line1.Name = "MyShape"
canvas.Shapes.Add(line1)
AddHandler line1.Click, AddressOf LineClick
End If
End Sub
Private Sub LineClick(sender As Object, e As EventArgs)
' Here is where we take the object that is sender from the arguments and cast it to its specific control
If RadioButton2.Checked = True Then
' I could just as easily use
CType(sender, PowerPacks.LineShape).Dispose()
TextBox1.Text = canvas.Shapes.Count
End If
End Sub
End Class