VBA Array of picture boxes - vba

I am making a game for my intro to comp programming class, I have a 6 by 6 board where you do something different on each tile. I am currently working on a mob collision sub where if the player collides with the mob the player has to battle. Right now I have an issue with creating multiple of the same time of mob. Here is my code
Public Sub creeperS()
' Dim creeper As New PictureBox
'This is now above so it can be used by other subs
Dim creepercount As Integer
creeper.Width = 32
creeper.Height = 32
creeper.BackColor = Color.LimeGreen
creepercount = rand.Next(0, 36)
If creepercount = 0 Then
Me.Controls.Add(creeper)
creeper.Top = 95
creeper.Left = 84
creeper.BringToFront()
ElseIf creepercount = 1 Then
Me.Controls.Add(creeper)
creeper.Top = 95
creeper.Left = 184
creeper.BringToFront()
ElseIf creepercount = 2 Then
Me.Controls.Add(creeper)
creeper.Top = 95
creeper.Left = 284
creeper.BringToFront()
ElseIf creepercount = 3 Then
It does this all the way to 36, Im wondering if you can make a picture box array so i can have several of a mob on the board.

You did not post your class code, so I will try showing you a generic method to create controls and keep them in an array. In fact a Dictionary object...
Create a variable on top of your form, at the declarations side:
Option Explicit
Private shDict As Object
Then put the next code in your Form Initialize event:
Dim i As Long, No As Long
Dim txtBox As MSForms.TextBox
No = 36 'your needed controls
Set shDict = CreateObject("Scripting.Dictionary")
For i = 84 To No * 100 Step 100
Set txtBox = Controls.Add("Forms.TextBox.1", "txt_" & i)
shDict.Add txtBox.Name, Array(txtBox, 95, i, True)
Next i
Not having your class, my example code creates text boxes and fills their Name, Top and Left properties in a Dictionary array. It can keep objects, arrays, strings etc. I tried to use your Top and Left logic, but big part of them will not fit on the form surface...
Place a button on the form (named btRepAll). It will preposition all created shapes according to the values previously input in shDict.
Private Sub btRepAll_Click()
Dim i As Long
For i = 84 To shDict.Count * 100 Step 100
shDict("txt_" & i)(0).top = shDict("txt_" & i)(1)
shDict("txt_" & i)(0).left = shDict("txt_" & i)(2)
Next i
End Sub
I wanted to ask some other preliminary questions, to clarify myself regarding your real need, but since you did not answered a simple one and I cannot stay to much at my office, I preferred to post a generic answer. You can load as many properties as you want. Please try building something similar, able to better fit your real need.

Related

Random images on buttons

Good evening made the following code, create an array of buttons and panels, now how do I insert random images to those buttons, can you help me please.
Public Sub crearBotonesPaneles(ByVal creaBoton(,) As Button, ByVal creaPanel As Panel)
Dim puntoLocacion As Point
puntoLocacion.X = 20
puntoLocacion.Y = 40
For filas As Integer = 0 To 1
For columnas As Integer = 0 To 3
If IsNothing(creaBoton(filas, columnas)) Then
creaBoton(filas, columnas) = New Button
creaBoton(filas, columnas).Location = puntoLocacion
creaBoton(filas, columnas).Width = 50
creaBoton(filas, columnas).Height = 50
creaPanel.Controls.Add(creaBoton(filas, columnas))
puntoLocacion.X = puntoLocacion.X + 50
End If
Next
puntoLocacion.X = 20
puntoLocacion.Y = puntoLocacion.Y + 50
Next
End Sub
Doing something random ALWAYS means generating one or more random numbers in an appropriate range and then using them in an appropriate manner. The way you determine the range is application-specific and the manner in which you use the number(s) is application-specific. In your case, you might get the paths of all the image files in a folder, use random numbers to order those paths randomly and then use the paths one by one to get the images from the files. E.g.
Private rng As New Random 'Random number generator
Private imagePaths As Queue(Of String)
Private Sub LoadImagePaths()
'Create a new queue of file paths sorted based on a random number mapped to each one.
imagePaths = New Queue(Of String)(Directory.EnumerateFiles(My.Computer.FileSystem.SpecialDirectories.MyPictures,
"*.jpg").
OrderBy(Function(s) rng.NextDouble()))
End Sub
Private Function GetNextImage() As Image
'Load the image paths is there is no queue or the current queue is empty.
If imagePaths?.Any() = False Then
LoadImagePaths()
End If
'Create an image from the next file in the queue.
Return Image.FromFile(imagePaths.Dequeue())
End Function
Based on that code, you just call GetNextImage each time you need a random image. There will be no repeats until the entire list is exhausted.

VB: Check if Object has “Class” then Execute Code

I am looking to find something in Visual Basic that basically states that if something collides with something with a certain class, it executes code. I may not be using correct terms, so when I say object I mean like a label or picture box, and a class is... well like a class in HTML, I guess, like an object with a trait.
The pseudocode would look something like this:
If object.Bounds.IntersectsWith([object with certain class]) Then execute code
Otherwise, the game's 'if' statements will overwhelm... Also I am new to Visual Basic language, so please keep it as simple as possible.
Here is what I have already:
If carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 1 Then
directiona = 0
carblue.Top += 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 2 Then
directiona = 0
carblue.Left += 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 3 Then
directiona = 0
carblue.Top -= 2
ElseIf carblue.Bounds.IntersectsWith(boundary1.Bounds) And directiona = 4 Then
directiona = 0
carblue.Left -= 2
End If
Where carblue is the object being controlled, boundary1 is the obstacle that stops the car from moving (on collision) and directiona is the value of the direction the car is travelling (1 is up, 2 is left, etc).
(Moved from S.A. Programmers site)
Without actual sample code to work with, it's not easy to provide a specific solution. I don't know how many controls you have moving around, is it 2? 10? 10,000? Assuming you have 10+ controls moving around, this is how I would handle it.
I would use a datatable to keep a record of the Bounds for each Control that moves or is collidable. After a control moves, update that row in the datatable, look for collisions, then check the object types to determine what kind of class the collided object is, and then run the code as needed.
Public MovingControls As DataTable
Sub Main()
'Build the DataTable
MovingControls = New DataTable("ControlBounds")
MovingControls.Columns.Add("Name", GetType(String))
MovingControls.Columns.Add("x1", GetType(Integer))
MovingControls.Columns.Add("x2", GetType(Integer))
MovingControls.Columns.Add("y1", GetType(Integer))
MovingControls.Columns.Add("y2", GetType(Integer))
End Sub
'Call this only when a control/object is created
Sub MovingControlAdded(sender As Control)
Dim Row As DataRow = MovingControls.NewRow
Row("Name") = sender.Name
Dim BoundsRect As Drawing.Rectangle = sender.Bounds
Row("x1") = sender.Bounds.Left
Row("x2") = sender.Bounds.Right
Row("y1") = sender.Bounds.Bottom
Row("y2") = sender.Bounds.Top
MovingControls.Rows.Add(Row)
End Sub
'Call this only when a control/object has moved
Sub MovingControlMoved(sender As Control)
'Update the location of this Control
Dim Row() As DataRow = MovingControls.Select("Name = '" & sender.Name & "'")
'Select returns an array of Rows but there should only be 1 row for each Control
Row(0)("x1") = sender.Bounds.Left
Row(0)("x2") = sender.Bounds.Right
Row(0)("y1") = sender.Bounds.Bottom
Row(0)("y2") = sender.Bounds.Top
'Collision check
Dim CollidedRows() As DataRow = MovingControls.Select("(" & sender.Bounds.Right & " >= x1)" &
"AND (" & sender.Bounds.Left & " <= x2)" &
"AND (" & sender.Bounds.Bottom & " <= y2)" &
"AND (" & sender.Bounds.Top & " >= y1)" &
"AND (Name <> '" & sender.Name & "'")
'Determine the object type and execute necessary code
For Each CollidedRow As DataRow In CollidedRows
Dim CollidedControl As Control = Me.Controls.Item(CollidedRow("Name"))
If CollidedControl.GetType = GetType(Label) Then
'Do stuff for labels
ElseIf CollidedControl.GetType = GetType(Button) Then
'Do stuff for buttons
End If
Next
End Sub
Caveat: This assumes 1 control is moving at a time. If Control A moves into Control B but Control B moves away at the same time, this code will still call a collision even though the collision was avoided. If you have multiple controls moving, you may want to split the MovingControlMoved method into 2 methods, one for updating the table and one for collision checks. Handle all movement first, then handle all collisions.
Depending on the complexity, you may want to create custom classes for collidable controls that inherit an interface for collisions. You can use System.Reflection to call RunMeOnCollision. This would eliminate the list of If statements.
Interface iCollidableBase
Sub RunMeOnCollision()
End Interface
Public Class CollidableLabel
Inherits Label
Implements iCollidableBase
Public Sub RunMeOnCollision() Implements iCollidableBase.RunMeOnCollision
Me.Text = "I have been collided"
End Sub
End Class
Public Class CollidableButton
Inherits Button
Implements iCollidableBase
Public Sub RunMeOnCollision() Implements iCollidableBase.RunMeOnCollision
Me.Text = "Ouch, that collision hurt!"
End Sub
End Class
Again, not knowing the full context here, I can't test my solution against your code. If you can post some additional details, I might be able to help more.
-E
I found what I needed from my programming teacher, using arrays.
In the public sub:
Dim walls(17) As PictureBox
Where 17 is the number of (In this case) boundaries that your form has. Also change PictureBox to the object you are using (for example labels).
Then in your form load:
For i = 1 To 17
walls(i) = Me.Controls("boundary" & i)
Next
I'm honestly not 100% sure about this, but the "boundary" string is a section of the name of my PictureBoxes, that are acting as boundaries. There names are boundary1, boundary2, etc. So you might change "boundary" to what your object's names are.
Next, when you want to check for something using the boundaries, declare
For i = 1 To 17
Then when you want to close checking for it,
Next
For the checking of collisions, use this:
If object.Bounds.IntersectsWith(walls(i).Bounds) Then
So in this case, the If statement would go between the For and Next lines.

Update text box with each click of a button

I am trying to set up a userform that will be used to take orders. e.g. each time you click the Cappuccino button it will increment the text box by one indicating that you are ordering 1, 2, 3 etc.
As far as I can get it is to only populate the text box one time. Each additional click does not appear to do anything. This is the Code I currently have for it. I tried declaring num as public. I thought that might be part of the problem but it did not seem to make a difference. Could it be a type casting issue since it is a "text" box and I am trying to treat it as in integer?
Private Sub Capuccino_Click()
If (Cap_qty.Value = Null) Then
Dim num As Integer
num = 1
Cap_qty.Value = Cap_qty.Value + num
ElseIf (Cap_qty.Value = IsNotNull) Then
num = num + 1
Cap_qty.Value = num
'Cap_qty.Value = num + 1
'num = Cap_qty.Value
End If
End Sub
Well, that makes a difference. I looked at something somewhere that told me to use Null, IsNotNull. I was able to get it working with the following which at the moment does not make sense to me I will have to figure out why it works this way. I guess there is some background action happening that is letting me do math with stings
Private Sub CommandButton1_Click()
If (TextBox1.Value = vbNullString) Then
TextBox1.Value = 1
Else
TextBox1.Value = TextBox1.Value + 1
End If
End Sub
​

Refer to and change the caption of a Label without using the Label's Name?

I want to Change the captions of several Label boxes. The Labels are sequentially named (DAY1, DAY2, DAY3...DAY14). I need help finding a way to do this;
DAY1.Caption = "1"
without implicitly using the label name...more like:
("DAY" & i).Caption = 1
Where i is an integer. I get a variety of error. My guess is that I don't know the proper object variables or syntax. Any ideas?
I assume your question is in the context of VBA (please edit your question to add the "VBA" tag if so). A nice solution would be to retain a reference to your labels as you create them. You can programmatically create a list of labels like this:
Dim label As Label
Dim dayLabels As New List(Of Label)
For i = 1 To 7
Set label = UserForm.Controls.Add("Forms.Label.1", "Day" & i, True)
dayLabels.Add(label)
With label
.Caption = "Day" & i
.Left = 10
.Width = 50
.Top = 10 * i
End With
Next
Note that you need to show your UserForm as vbModeless to use this code. Also note that the positioning of the labels is accomplished with the .Top and .Left fields; I am using the .Top to value to avoid overlapping of the controls (but you could also use .Left, for example, to distribute them horizontally).
Now that you have all label references stored in the list, you can simply refer to them by index like you were originally trying to do:
dayLabels(3).Text = "The text to appear on Day3 label"
I like where your heads at. I run into two problems. Access VBA registers a compile error for
Dim daylabels As New List(Of Label)
If I remove that line, VBA doesn't recognize any object(s) in line 4;
set label = Userform.Controls.Add("Forms.Label.1, "Day" & I, True
I realize I didn't specify that I was using VBA for Access. I tried playing with the wording. Excel has Userforms but Access treats forms differently...no luck. Here is the full Sub I am using;
Private Sub Command6_Click()
Dim DURATION As Integer
Dim i As Integer
Dim x As Label
DURATION = ((END_DATE.Value) - (START_DATE.Value)) + 1
i = 1
For i = 1 To DURATION
Set x = UserForm.Comtrols.Add("Forms.Label.1", "DAYx" & i, True)
With x
.Caption = "Day" & i
.Left = 10
.Width = 50
.Top = 30 + (10 * i)
End With
Next

Trouble with looping in VBA (access) to put items on a form and position them correctly

I've been trying to make a form with 68 items while positioning all the items on a form via a loop, but this loop isn't working :( Can someone please help me get this to work? I've tried looking everywhere but can't see what to do :/
Dim Items(67) As String
For x = 0 To 67
Items(x) = "Ctl" & x
Next
#It goes from 1 here as I manually set up 1 otherwise (x - 1) wont work.
For x = 1 To 67
Form_Home.Items(x).Top = 0
Form_Home.Items(x).Left = (Form_MainScreen.Items(x - 1).Left) + (Form_MainScreen.Items(x - 1).Width)
Form_Home.Items(x).Height = 225
Form_Home.Items(x).Width = 500
Next
Thank you everyone :)
What do you mean with Form_Home.Items? if you already have controls added to your forms, probably there is a Controls collection, so you can iterate trough it and set its properties, assuming each control has a name of the form Ctl0, Ctl1, Ctl2... then you can proceed as follow:
Dim ct_name as String
Dim ct_name_before as String
For x = 1 To 67
ct_name_before = "Ctl" & CStr(Cint(x-1))
ct_name = "Ctl" & CStr(x)
Form_Home.Controls(ct_name).Top = 0
Form_Home.Controls(ct_name).Left = (Form_MainScreen.Items(ct_name_before).Left) + _
(Form_MainScreen.Items(ct_name_before).Width)
Form_Home.Controls(ct_name).Height = 225
Next
Some version of VBA allow you to create array of controls sharing all the same name and having different index, then you can iterate through the array to get each control.