I am creating a game for my Visual Basic class.
So far I have been successful, except for movement on my label grid. I have a 16, 21 Label grid that I am using for the main map.
The X axis is numeric 1-21 and the Y axis is letters A-P. So the upper left Label is named A1 and the bottom right Label is named P21.
The player starts on Label P11 and has an image of an arrow indicating their location.
I also have an up, down, left, right buttons as well. When I press the up Button, I want the image to move itself to O11, or the above Label.
I have a solution, but it is very code extensive, and the up Button alone is 1600+ line of code, which I think is a little excessive.
My variables that I declared and the initial starting Label:
Public Letters As New List(Of String)
Public Shared x = 15
Public Shared locationLetter As String
Public Shared locationNumber As Integer = 11
Public Shared locationPlayer As String
'Put player's ship in starting grid P11
P11.Image = My.Resources.Arrow
This code loops through each Label and then finds the one that has the image and then sets its Image property to nothing.
It also changes the players location to what it should be, in this case I want the image to go from P11 to O11.
Dim nextMove As String
Controls.Find(locationPlayer)
For Each lbl As Label In Controls.OfType(Of Label)
If lbl.Image IsNot Nothing And x >= 0 Then
x -= 1
lbl.Image = Nothing
locationLetter = Letters.Item(x)
locationPlayer = CStr(locationLetter & locationNumber)
If lbl.Name = locationPlayer Then
lbl.Image = My.Resources.Arrow
End If
End If
Next
This line of code adds the appropriate letters to the Letters list, so that I can call up it to concatenate to find the current position the player should be in:
Letters.Add("A") ' 0 position
Letters.Add("B") ' 1 position
Letters.Add("C") ' 2 position
Letters.Add("D") ' 3 position
Letters.Add("E") ' 4 position
Letters.Add("F") ' 5 position
Letters.Add("G") ' 6 position
Letters.Add("H") ' 7 position
Letters.Add("I") ' 8 position
Letters.Add("J") ' 9 position
Letters.Add("K") ' 10 position
Letters.Add("L") ' 11 position
Letters.Add("M") ' 12 position
Letters.Add("N") ' 13 position
Letters.Add("O") ' 14 position
Letters.Add("P") ' 15 position
locationLetter = Letters.Item(15)
The code that I have now that is working, but is way excessive is:
If P1.Tag = "player" Then
O1.Tag = "player"
O1.Image = My.Resources.Arrow
P1.Tag = ""
P1.Image = Nothing
btnDOWN.Enabled = True
btnLEFT.Enabled = False
ElseIf P2.Tag = "player" Then
O2.Tag = "player"
O2.Image = My.Resources.Arrow
P2.Tag = ""
P2.Image = Nothing
btnDOWN.Enabled = True
ElseIf P3.Tag = "player" Then
O3.Tag = "player"
O3.Image = My.Resources.Arrow
P3.Tag = ""
P3.Image = Nothing
btnDOWN.Enabled = True
'[...]
End If
And so on. I would have to do this for every single Button, so that's 336 blocks x4 Buttons, or roughly 6,720 lines of code to move an image to another box.
My pseudo code for this is:
If playerlocation = (some grid number, like P11 for example)
Find the label with the name = to playerlocation and add image to label
i.e.
so if playerlocation = D4
find the label with the name D4 and add the image to the label
Procedural programming is good!
Some semi-OOP:
A description of what you said you need:
A Board
- it has Dimensions;
- contains a array of, lets say, Cells (which have their own properties);
- has to allow the movement of a dummy player over its Cells;
A Player:
- It has a position
- a picture which is the visual expression of its position;
- an action range: can move and only inside the range of the Cells that
the Board defines
Building a Board object (of course):
Public Class GameBoard
Private _BoardSize As New Size 'Board size
Private _CellsArray As BoardCell(,) 'The Cells array
Private _PlayerDummy As PlayerDummy
Private _Cells As BoardCell
Private _cell As BoardCell
Private _Location As Point
Private _Container As Control
Private _PlayerPosition As Point 'Current or default position of the player
Private _PlayerImage As Image 'Player dummy Image
Private _Initialized As Boolean = False
'The BoardSize defaults to 21x15
Public Sub New()
Me.New(New Size(0, 0))
End Sub
Public Sub New(_size As Size)
Me._BoardSize = _size
Me._cell = New BoardCell
Me._cell.Size = New Size(50, 50)
Me._PlayerDummy = New PlayerDummy
End Sub
Friend Property BoardSize() As Size
Get
Return Me._BoardSize
End Get
Set(ByVal value As Size)
Me._BoardSize = value
End Set
End Property
Friend Property Cell() As BoardCell
Get
Return Me._cell
End Get
Set(ByVal value As BoardCell)
Me._cell = value
End Set
End Property
Friend ReadOnly Property Cells(_id As Point) As BoardCell
Get
Return Me._CellsArray(_id.X, _id.Y)
End Get
End Property
Public Property Container() As Control
Get
Return _Container
End Get
Set(ByVal value As Control)
_Container = value
Me._PlayerDummy.Parent = value
End Set
End Property
Public Property Location() As Point
Get
Return _Location
End Get
Set(ByVal value As Point)
_Location = value
End Set
End Property
Public Property PlayerPosition() As Point
Get
Return Me._PlayerPosition
End Get
Set(value As Point)
If Me._Initialized = True Then
'If a player position changes, move the dummy image in the new Cell
If Me._PlayerPosition <> value Then
Me._PlayerPosition = value
Me._PlayerDummy.Location = Me._CellsArray(value.X, value.Y).Location
End If
End If
End Set
End Property
Public Property PlayerImage() As Image
Get
Return Me._PlayerImage
End Get
Set(value As Image)
Me._PlayerImage = New Bitmap(value)
Me._PlayerDummy.Image = Me.PlayerImage
End Set
End Property
'Dimension (0, 0) is used to show Rows/Columns headers
Public Sub Initialize(_size As Size)
Me._BoardSize = _size
'Defines the number of Cells
Me._CellsArray = New BoardCell(_size.Width, _size.Height) {}
'Add Cells classes per dimensions(x, y)
Dim x As Integer = 0
While x <= _BoardSize.Width
Dim y As Integer = 0
While y <= _BoardSize.Height
Me._CellsArray(x, y) = CreateBoardCell()
y += 1
End While
x += 1
End While
'Paint the Board
For x = 0 To Me._BoardSize.Width
For y = 0 To Me._BoardSize.Height
Dim _position As Point = New Point(x, y)
If x > 0 And y = 0 Then
Me.Cells(_position).Text = x.ToString
Me.Cells(_position).BackColor = Color.FromArgb(32, 32, 32)
Me.Cells(_position).ForeColor = Color.White
End If
If y > 0 And x = 0 Then
Me.Cells(_position).Text = Chr(y + 64).ToString
Me.Cells(_position).BackColor = Color.FromArgb(32, 32, 32)
Me.Cells(_position).ForeColor = Color.White
End If
Me.Cells(_position).Location = New Point(Me._Location.X + x * Me.Cell.Size.Width, _
Me._Location.Y + y * Me.Cell.Size.Height)
Me.Cells(_position).Parent = Me.Container
Next
Next
Me.Cells(New Point(0, 0)).BorderStyle = BorderStyle.None
Me.Cells(New Point(0, 0)).BackColor = Me.Container.BackColor
Me._Initialized = True
End Sub
Private Function CreateBoardCell() As BoardCell
Dim _boardcell As BoardCell = New BoardCell
_boardcell.Size = Me._cell.Size
_boardcell.BackColor = Me._cell.BackColor
_boardcell.BorderStyle = Me._cell.BorderStyle
Me._PlayerDummy.Size = New Size(Me._cell.Size.Width - 1, Me._cell.Size.Height - 1)
Return _boardcell
End Function
'A class defining a Cell object. Inherits from Label.
'May be a Panel gives more options. Do not use PictureBoxes.
Public Class BoardCell
Inherits Label
Public Sub New()
'Setup default properties
Me.AutoSize = False
Me.TextAlign = ContentAlignment.MiddleCenter
Me.Visible = True
End Sub
End Class
Friend Class PlayerDummy
Inherits PictureBox
Private _Image As Image
Private _Parent As Control
Public Sub New()
Me.SizeMode = PictureBoxSizeMode.Zoom
Me.BorderStyle = Windows.Forms.BorderStyle.Fixed3D
Me.Visible = True
End Sub
Public Shadows Property Image() As Image
Get
Return Me._Image
End Get
Set(ByVal value As Image)
MyBase.Image = value
Me._Image = value
End Set
End Property
Public Shadows Property Parent() As Control
Get
Return _Parent
End Get
Set(ByVal value As Control)
_Parent = value
MyBase.Parent = value
End Set
End Property
End Class
End Class
To create a new Board, instantiate it and definine its properties
MyGameBoard = New GameBoard
'Starting position to draw this GameBoard
MyGameBoard.Location = New Point(50, 50)
MyGameBoard.Cell.Size = New Size(50, 50)
MyGameBoard.Cell.BackColor = Color.Wheat
MyGameBoard.Cell.BorderStyle = BorderStyle.FixedSingle
'Define the container class (Form, Panel, PictureBox...) that will contain this Board
MyGameBoard.Container = Me
'Assign an Image to the new player object and Position it inside its Board Cell
MyGameBoard.PlayerImage = New Bitmap(My.Resources.horse2)
'Paint the Board giving it desired size
MyGameBoard.Initialize(New Size(10, 10))
Now, the Player
Public Class Player
Public Enum Direction 'Enumerates this player allowed directions
Up = 0 'Maybe it could also move diagonally
Down
Left
Right
End Enum
Private _Position As Point 'Player Position
Private _Boundaries As New Rectangle 'The Boundaries of its movements
Public Sub New()
Me.New(Nothing)
End Sub
Public Sub New(_boundaries As Rectangle)
Me._Boundaries = New Rectangle(1, 1, _boundaries.Width - 1, _boundaries.Height - 1)
End Sub
Public Property Position() As Point
Get
Return Me._Position
End Get
Set(value As Point)
'Evaluates whether the position being set violates the
'constraints imposed by the Boundaries
Me._Position.X = If(value.X > Me._Boundaries.Right, Me._Boundaries.Right, value.X)
Me._Position.X = If(value.X < Me._Boundaries.Left, Me._Boundaries.Left, value.X)
Me._Position.Y = If(value.Y > Me._Boundaries.Bottom, Me._Boundaries.Bottom, value.Y)
Me._Position.Y = If(value.Y < Me._Boundaries.Top, Me._Boundaries.Top, value.Y)
End Set
End Property
Public Property Boundaries() As Rectangle
Get
Return Me._Boundaries
End Get
Set(ByVal value As Rectangle)
Me._Boundaries = value
End Set
End Property
'Move of the Player. Evaluates if the requested action violates Boundaries
Public Function Move(_direction As Direction) As Point
Select Case _direction
Case Direction.Up
Me.Position = New Point(Me.Position.X, If(Me.Position.Y > Me._Boundaries.Top, Me.Position.Y - 1, Me.Position.Y))
Exit Select
Case Direction.Down
Me.Position = New Point(Me.Position.X, If(Me.Position.Y < Me._Boundaries.Bottom, Me.Position.Y + 1, Me.Position.Y))
Exit Select
Case Direction.Left
Me.Position = New Point(If(Me.Position.X > Me._Boundaries.Left, Me.Position.X - 1, Me.Position.X), Me.Position.Y)
Exit Select
Case Direction.Right
Me.Position = New Point(If(Me.Position.X < Me._Boundaries.Right, Me.Position.X + 1, Me.Position.X), Me.Position.Y)
Exit Select
End Select
Return Me._Position
End Function
End Class
Create a new player with movement Boundaries = to the board Size
MyPlayer = New Player(New Rectangle(New Point(1, 1), MyGameBoard.BoardSize))
Starting position:
MyPlayer.Position = New Point(10, 10)
Place the Player Dummy
MyGameBoard.PlayerPosition = MyPlayer.Position
To move it just use the Move method and let the Board know about:
MyPlayer.Position = MyPlayer.Move(Player.Direction.Up)
MyGameBoard.PlayerPosition = MyPlayer.Position
Place some controls to let the actual human player move the dummy.
Well, using labels as fields is a bit weird. However, you could put all the labels into an 2D-Array and remember the player's position. Then, when a movement button is pressed you first check if the player's new position is out of range. if not, you clear the old label and set the new won remembering the new position.
I haven't tested it but something like this should work:
Private playfield as Label() = {{P11,P12,P13,...},{P21,P22,P23,...},...}
Private playerX as Integer = 11
Private playerY as Integer = 14
Public Sub btnDOWN_Clicked() Handles btnDOWN.Clicked
If playerY+1 < 14 Then
playfield(playerX, playerY).Image = Nothing
playerY = playerY + 1
playfield(playerX, playerY).Image = My.Ressources.Arrow
End If
End Sub
Repeat the stuff in btnDown_Clicked for btnUP, btnLEFT and btnRIGHT and change the related variables. Remember to check if your move is valid before you do it or you might get out of range exceptions when walking out of screen.
The Problem: I'm programmatically generating labels but am having trouble referencing them in code because they don't exist at runtime.
The Context: For a game, I've generated a 10x10 grid of labels with the following:
Public lbl As Label()
Dim tilefont As New Font("Sans Serif", 8, FontStyle.Regular)
Private Sub Lucror_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim i As Integer = 0
Dim a As Integer = 0
Dim height As Integer
Dim width As Integer
height = 30
width = 30
ReDim lbl(99)
For i = 0 To 99
lbl(i) = New Label
lbl(i).Name = i
lbl(i).Size = New System.Drawing.Size(30, 30)
lbl(i).Location = New System.Drawing.Point((width), height)
lbl(i).Text = i
lbl(i).Font = tilefont
Me.Controls.Add(lbl(i))
width = width + 30
a = a + 1 'starting new line if required
If (a = 10) Then
height = height + 30
width = 30
a = 0
End If
Next
End Subenter code here
This worked fine but the labels function as tiles in the game and game tiles need to store 2-3 integers each as well as be able to be referenced through event handlers. I figured a possible way to store integers would be to generate 100 arrays, each named after a label and each holding the 2-3 integers, but that seems very redundant.
What I need:
On click and on hover event handlers for every label
An array (or dictionary?) to store 2-3 integers for every label
Labels have to reference each others names ie. do something to label with name (your name + 1).
The Question: Is there a simple way to achieve these three things with the current way I generate labels (and if so, how?) and if not, how else can I generate the 100 labels to make achieving these things possible?
Any help is much appreciated.
Your labels do exist at runtime, but not at compile time. Attaching events is a little different at runtime, you must use AddHandler.
Below is some sample code that should illustrate everything you're asking for. I've introduced inheritance as a way of saving data that is pertinent to each tile. The GameTile type behaves exactly as a label, and we've added some functionality for storing integers and naming the control.
Public Class Form1
Dim tilefont As New Font("Sans Serif", 8, FontStyle.Regular)
Public Property GameTiles As List(Of GameTile)
Private Sub Lucror_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim a As Integer = 0
Dim xPosition As Integer = 30
Dim yPosition As Integer = 30
GameTiles = New List(Of GameTile)
For i = 0 To 99
Dim gt As New GameTile(i.ToString)
gt.Size = New System.Drawing.Size(30, 30)
gt.Location = New System.Drawing.Point((yPosition), xPosition)
gt.Font = tilefont
gt.Integer1 = i + 1000
gt.Integer2 = i + 2000
gt.Integer3 = i + 3000
Me.Controls.Add(gt)
AddHandler gt.Click, AddressOf TileClickHandler
GameTiles.Add(gt)
yPosition = yPosition + 30
a = a + 1 'starting new line if required
If (a = 10) Then
xPosition = xPosition + 30
yPosition = 30
a = 0
End If
Next
End Sub
Private Sub TileClickHandler(sender As Object, e As EventArgs)
Dim gt = CType(sender, GameTile)
MsgBox("This tile was clicked: " & gt.Text &
Environment.NewLine & gt.Integer1 &
Environment.NewLine & gt.Integer2 &
Environment.NewLine & gt.Integer3)
End Sub
End Class
Public Class GameTile
Inherits Label
'this class should be in a separate file, but it's all together for the sake of clarity
Public Property Integer1 As Integer
Public Property Integer2 As Integer
Public Property Integer3 As Integer
Public Sub New(NameText As String)
MyBase.New()
Name = NameText
Text = NameText
End Sub
End Class
I am developing an application with a custom title bar. The form design requires it to be resizable. Keeping in mind that it has a custom title bar, using the normal route to remove the title bar in the custom form is not working. It appears that the Windows 10 API is forcing a small white "chunk" to remain at the top of the screen above the title bar.
My question to you is this: Has anyone encountered this issue, and do you know a fix or a work-around so that we can get the forms to look correct in Windows 10?
Here is my current code:
Dim testform As New Form
testform.Width = 350
testform.Height = 100
testform.FormBorderStyle = FormBorderStyle.Sizable
testform.ControlBox = False
testform.Text = String.Empty
testform.Show()
We tested for the API issue due to a suggestion by another support forum that has since seemed to be exhausted as far as help. I compiled our existing code and ran the executable on a Windows 7 machine. On the Windows 7 machine the form opens properly with 0 space between the top of the ClientRectangle and the Form.
Here's an example of how to do (something) like what you want. My VB is really rusty, so make sure you test and debug this carefully.
First Add a Form to your project. For this example I've left the name Form1 but you probably want to change that to something meaningful. On that form I set the BorderStyle property to None. (That's the only change I made)
The code for Form1 is as follows:
Public Class Form1
Public Const WM_NCHITTEST As Integer = &H84
Public Const BorderSize As Integer = 10
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WM_NCHITTEST Then
Dim x As Integer = m.LParam.ToInt32 And &HFFFF
Dim y As Integer = (m.LParam.ToInt32 >> 16) And &HFFFF
Dim p As Point = New Point(x, y)
If Me.Bounds.Contains(p) Then
Dim top As Boolean = Math.Abs(Me.Bounds.Y - y) < BorderSize
Dim bottom As Boolean = Math.Abs(Me.Bounds.Bottom - y) < BorderSize
Dim left As Boolean = Math.Abs(Me.Bounds.X - x) < BorderSize
Dim right As Boolean = Math.Abs(Me.Bounds.Right - x) < BorderSize
If top And left Then
m.Result = NCHITTEST.HTTOPLEFT
ElseIf top And right Then
m.Result = NCHITTEST.HTTOPRIGHT
ElseIf bottom And left Then
m.Result = NCHITTEST.HTBOTTOMLEFT
ElseIf bottom And right Then
m.Result = NCHITTEST.HTBOTTOMRIGHT
ElseIf top Then
m.Result = NCHITTEST.HTTOP
ElseIf bottom Then
m.Result = NCHITTEST.HTBOTTOM
ElseIf left Then
m.Result = NCHITTEST.HTLEFT
ElseIf right Then
m.Result = NCHITTEST.HTRIGHT
Else
m.Result = NCHITTEST.HTCAPTION
End If
Exit Sub
End If
End If
MyBase.WndProc(m)
End Sub
End Class
Additionally, somewhere in that makes sense in your project include the following enum. (I've stripped out the values I'm not using, but they are all available in the documentation) For the purposes of this demonstration I placed it directly beneath the End Class for Form1.
Public Enum NCHITTEST
HTBOTTOM = 15
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17
HTCAPTION = 2
HTLEFT = 10
HTRIGHT = 11
HTTOP = 12
HTTOPLEFT = 13
HTTOPRIGHT = 14
End Enum
The way it's implemented, there is an invisible 10px border where the mouse will change to the resize cursor. This is controlled by the BorderSize constant. You can move the form by clicking and dragging anywhere in the background. (That's what HTCAPTION does.)
TheB had the best answer, and I had to amend it slightly to work with my coding. Thank you for all of the help, sir!
So, when altering this code to work with a dynamically-created control a few changes needed to be made. Here is how I got it to work:
Create a new class specifically named: SubclassHWND.vb
(As gathered from Microsoft)
Public Class SubclassHWND
Inherits NativeWindow
Public Const WM_NCHITTEST = &H84
Public Const BorderSize As Integer = 10
Public frm As Form = Nothing
Public Sub setFrm(ByVal sender As Form)
frm = sender
End Sub
Protected Overloads Overrides Sub WndProc(ByRef m As Message)
if m.Msg = WM_NCHITTEST Then
Dim x As Integer = m.LParam.ToInt32 And &HFFFF
Dim y As Integer = (m.LParam.ToInt32 >> 16) And &HFFFF
Dim p As Point = New Point(x, y)
If frm.Bounds.Contains(p) Then
Dim top As Boolean = Math.Abs(frm.Bounds.Y - y) < BorderSize
Dim bottom As Boolean = Math.Abs(frm.Bounds.Bottom - y) < BorderSize
Dim left As Boolean = Math.Abs(frm.Bounds.X - x) < BorderSize
Dim right As Boolean = Math.Abs(frm.Bounds.Right - x) < BorderSize
If top And left Then
m.Result = NCHITTEST.HTTOPLEFT
ElseIf top And right Then
m.Result = NCHITTEST.HTTOPRIGHT
ElseIf bottom And left Then
m.Result = NCHITTEST.HTBOTTOMLEFT
ElseIf bottom And right Then
m.Result = NCHITTEST.HTBOTTOMRIGHT
ElseIf top Then
m.Result = NCHITTEST.HTTOP
ElseIf bottom Then
m.Result = NCHITTEST.HTBOTTOM
ElseIf left Then
m.Result = NCHITTEST.HTLEFT
ElseIf right Then
m.Result = NCHITTEST.HTRIGHT
Else
m.Result = NCHITTEST.HTCAPTION
End If
Exit Sub
End If
End If
Debug.WriteLine(m.ToString())
MyBase.WndProc(m)
End Sub
End Class
Public Enum NCHITTEST
HTBOTTOM = 15
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17
HTCAPTION = 2
HTLEFT = 10
HTRIGHT = 11
HTTOP = 12
HTTOPLEFT = 13
HTTOPRIGHT = 14
End Enum
This is basically the same exact code that #TheB supplied me with for the fix earlier with a couple of changes. We went ahead and created a new Public frm variable. We set this in the next set of code so we can reference the dynamic form that the WndProc evaluation will Override.
In the form generation code we added these lines to create the re-sizable window that has no title bar API in Windows 10:
newForm.FormBorderStyle = FormBorderStyle.FixedToolWindow
newForm.ControlBox = False
newForm.Text = String.Empty
Dim s As SubclassHWND = New SubclassHWND()
s.setFrm(newForm)
s.AssignHandle(newForm.Handle)
Now every dynamically-created form in the project uses the Overrides that we specified in SubclassHWND.vb! Thanks go out to everyone and all of their valuable input.
I am using a spin button to cycle through dates of a phase. When I call an item from a collection called customtextboxcollection with its index value, I get an "Object Required" error. Both the spin button and the text box whose value changes are dynamically created controls displayed on a UserForm called UserForm1.
The sub to create the items in customtextbox collection run before the spin button is clicked:
Dim customtextboxcollection As Collection
Dim spinbuttoncollection As Collection
Public Sub ComboBox1_Click() 'When a person is selected to enter hours for an employee from a combobox, it triggers the creation of the controls
Sheet1.Activate
CommandButton1.Enabled = True 'Enable the OK and Apply buttons when personnel title is selected.
UserForm1.Label2.Visible = True
UserForm1.ratebox.Visible = True
QuantityLabel.Visible = True
quantitybox.Visible = True
'The variables below are to access the table where I store saved information regarding the project phases I will add hours to.
Dim counter As Integer
counter = 6 'The index of the first row for phases
Dim phasecolumn As Integer
phasecolumn = 3 'The index of the column containing the phases
Dim checkboxnumber As Integer
checkboxnumber = 1 'This is the number needed to distinguish between the checkboxes that appear/disappear.
phasestartcolumn = 4
phaseendcolumn = 5
Dim customtextboxHandler As cCustomTextBoxHandler
Set customtextboxcollection = New Collection 'Sets the previously created collection
Dim spinbuttonHandler As cSpinButtonHandler 'This is my spin button handler class
Set spinbuttoncollection = New Collection 'Sets the previously created collection
'This Do-Loop locates a row on the table with saved information
Do
If (Sheet3.Cells(savedpersonnelrow, savedpersonnelcolumn) = ComboBox1.Value) Then
storagerow = savedpersonnelrow
lastcomboboxvalue = ComboBox1.Value
Exit Do
End If
savedpersonnelrow = savedpersonnelrow + 1
Loop Until (savedpersonnelrow = 82)
Sheet1.Activate
'These sections create the controls depending on the number of phases saved.
Set spin = UserForm1.Controls.Add("Forms.SpinButton.1")
With spin
.name = "SpinButton" & checkboxnumber
.Left = 365
.Top = topvalue + 6
.Height = 15
.Width = 40
'.Value = Sheet3.Cells(storagerow, savedphasecolumn + checkboxnumber)
'Sheet1.Activate
Dim phasestart As Date
phasestart = Sheet1.Cells(counter, phasestartcolumn).Value
Dim phaseend As Date
phaseend = Sheet1.Cells(counter, phaseendcolumn).Value
spin.Min = phasestart
spin.Max = phaseend
spin.Orientation = fmOrientationVertical
'Do
'.AddItem Format(phasestart, "mmm-yy")
'phasestart = DateAdd("m", 1, phasestart)
'Loop Until (Month(phaseend) = Month(phasestart) And Year(phaseend) = Year(phasestart))
Set spinbuttonHandler = New cSpinButtonHandler
Set spinbuttonHandler.spin = spin
spinbuttoncollection.Add spinbuttonHandler
End With
Set ctext = UserForm1.Controls.Add("Forms.TextBox.1")
With ctext
.name = "CustomTextbox" & checkboxnumber
.Left = 470
.Top = topvalue + 6
.Height = 15
.Width = 40
.Value = phasestart
Set customtextboxHandler = New cCustomTextBoxHandler
Set customtextboxHandler.ctext = ctext
customtextboxcollection.Add customtextboxHandler
End With
topvalue = topvalue + 15
counter = counter + 1
checkboxnumber = checkboxnumber + 1
Loop Until counter = 14
End Sub
In my class called cSpinButtonHandler, I reference these customtextboxcollection object associated with it's corresponding spin button:
Public WithEvents spin As MSForms.SpinButton
Private Sub spin_Click()
UserForm1.CommandButton3.Enabled = True
End Sub
Private Sub spin_SpinDown()
x = 0
Do
x = x + 1
Loop Until spin.name = "SpinButton" & x
Dim spindate As Date
spindate = customtextboxcollection.Item(x).ctext.Value 'The error occurs here.
customtextboxcollection.Item(x).ctext.Value = DateAdd("m", -1, spindate)
End Sub
Why is this reference generating an error? What is the correct way to reference it?
This is not an answer to your real question, but a suggestion for an alternate approach which might be easier to manage.
Instead of using two separate collections and two different classes, you could create a single class which would handle each pair of controls (one spin and one text box). That would be easier to handle in terms of hooking events between each pair.
clsSpinText:
Option Explicit
Public WithEvents txtbox As MSForms.TextBox
Public WithEvents spinbutn As MSForms.SpinButton
Private Sub spinbutn_Change()
'here you can refer directly to "txtbox"
End Sub
Private Sub txtbox_Change()
'here you can refer directly to "spinbutn"
End Sub
When adding your controls create one instance of clsSpinText per pair, and hold those instances in a single collection.
I have this labels in my project
num1.Hide()
num2.Hide()
num3.Hide()
.
.
.
num20.Hide()
What's the best way to hide/show them? I'm thinking of loop but i can't make it work.
You should put all all your labels in a list, Then to hide or show your elements, just iterate the list in a function to do your action on all elements.
One solution:
Create an array of labels at runtime then you can loop through them to make them invisible:
'define the array
Dim labelArray(5) As Label
Private Sub createLabels
'add them to the form
For i As Integer = 0 To labelArray.GetUpperBound(0)
initLabel(i, New Point(i * 30, i * 30), i.ToString)
Next
'now hide them
For i As Integer = 0 To labelArray.GetUpperBound(0)
labelArray(i).Visible = False
Next
End Sub
Private Sub initLabel(ByVal index As Integer, location As System.Drawing.Point, caption As String)
labelArray(index) = New Label
With labelArray(index)
'set some default properties
.Name = "LabelArray" + index.ToString
.Width = 300
.Height = 100
.Location = location
.Text = caption
End With
Me.Controls.Add(labelArray(index))
End Sub