No Title Bar Issue and Windows 10 - vb.net

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.

Related

Create and manipulate On-demand collections of runtime controls

This is my first post. I've tried to find a similar topic but could not find any.
I am fairly new to VBA and I am learning as I try to create a file that helps plan cutting parts of a major piece.
Since Excel VBA does not allow to draw shapes or lines, I am using labels with a border to create rectangles.
The rectangles represent the cuts to be made.
My main form has this look:
Main Form
As you can see in the image, in the area signaled with a red rectangle, the big piece with 1600 mm (in this example) will have seven 60 mm cuts.
My problem started when I tried to add different cuts to my cutting planning.
As I accept a cut, it goes to the cutting queue, and a new cut can be defined, as shown in the below image:
Second cut
The problem is that the first cut should stay there. I realized that I have to use Collections and most probably Classes for that.
This is especially important as I want, in the queue, to be able to move each line up and down the queue or even erase a line (and reflect it in my "drawing").
The code for now is far too extensive to add it here, but I managed to put some in functions that will go bellow. Some names are in portuguese, but I don't think it presents a problem.
Here I create the cuts defined by Largura: and Cortes reais:
Option Explicit
Public iCuts As Integer
Public Labels As Collection
Public newLabel As Object
Public bRecalculate As Boolean
Sub DrawCuts(NCuts As Integer, CutWidth As Double, TotalWidth)
Dim OriginX, OriginY As Integer
Dim labelCounter As Long
Dim labelCollection As New Collection
OriginX = 372
OriginY = 24
CutWidth = Multiplier(CutWidth, TotalWidth)
For labelCounter = 0 To NCuts - 1
Set newLabel = frmPlanning.Controls.Add("Forms.Label.1", "Corte" & labelCounter, True)
With newLabel
.ControlTipText = .Name 'labelCounter + 1
.Left = OriginX + CutWidth * labelCounter
.Width = CutWidth
.Height = 48
.Top = OriginY
.BackColor = &HFFFFFF
.BorderStyle = 1
.TextAlign = 2
.Font.Size = 6
.Caption = iCuts
End With
iCuts = iCuts + 1
Next
iCuts = iCuts - 1
End Sub
And in the next SUB I adapt the cuts to the size of the main piece, defined by Larg. bobine:
Sub Dim_Labels(Cuts As Integer, CutWidth As Double, RollWidth As Double, RollLeft As Double)
With frmPlanning.lCutWidth
.Caption = CutWidth * Cuts
.Width = Cuts * Multiplier(CutWidth, RollWidth)
End With
With frmPlanning.lCutLeft
.Caption = RollLeft
.Left = 372 + Cuts * Multiplier(CutWidth, RollWidth)
.Width = 320 - Cuts * Multiplier(CutWidth, RollWidth)
End With
frmPlanning.lRollWidth = RollWidth
End Sub
I have tried to put this in a collection but not only I receive all sorts of errors but I also cannot create different collections for each set of cuts on order to move each set independently.
I know this has to due with my lack of understanding of how collections and classes work, but I really am stuck and cannot go forward with this and need some help if you can give it.
I couldn't find a way, but I can provide the excel file to help you better understand the problem, if there is a way.
Thank you.
Júlio
So, I think this is what you're after. Note that it's not the cleanest code, but it does the drawing bits on a userform in isolation.
First, I stored the OriginX and OriginY in the Userform itself - After all, it should control where the drawing should start. Userform code:
Public OriginX As Integer
Public OriginY As Integer
Private Sub UserForm_Initialize()
OriginX = 20
OriginY = 20
End Sub
Next, I created a class "BigBox" for the red rectangle you had. It has a Height, a Width, and on initialization it will add it's label to the Userform.
(Note that putting the label on the form in this way is bad practice - The class shouldn't have to be aware of where to draw it. However - for answering your question this isn't immediately relevant.)
BigBox Class:
Private p_width As Integer
Private p_height As Integer
Private p_label As MSForms.Label
Public Property Let Width(value As Integer)
p_width = value
p_label.Width = p_width
End Property
Public Property Get Width() As Integer
Width = p_width
End Property
Public Property Let Height(value As Integer)
p_height = value
p_label.Height = p_height
End Property
Public Property Get Height() As Integer
Height = p_height
End Property
Public Property Get Label() As MSForms.Label
Set Label = p_label
End Property
Private Sub Class_Initialize() 'This bit is bad practice, but it works:
Set p_label = frmPlanning.Controls.Add("Forms.Label.1", "BigBox", True)
p_label.Left = frmPlanning.OriginX
p_label.Top = frmPlanning.OriginY
p_label.BorderColor = Red
p_label.BorderStyle = 1
End Sub
Next, I created a class "Cut" that can be used in a collection with the cuts, so when you need to redraw, you have them stored / they don't get forgotten:
Cut class:
Private p_width As Integer
Private p_height As Integer
Public Property Let Width(value As Integer)
p_width = value
End Property
Public Property Get Width() As Integer
Width = p_width
End Property
Public Property Let Height(value As Integer)
p_height = value
End Property
Public Property Get Height() As Integer
Height = p_height
End Property
Next, I isolated the "Cuts" and the "Labels" collections, since the labels need to be deleted and redrawn when you add a second batch. The following routine
Makes sure the Cuts collection and the labels collections exist
Shows the form (modeless, so code execution continues)
Creates the BigBox and sets the height and width. All cuts will take the height from here.
Adds cuts a couple of times.
Has the "add cut" routine also execute the drawing routine.
Module1 code:
Option Explicit
Public bb As BigBox
Public cuts As Collection
Public cutLabels As Collection
Public totalCutsWidth As Integer
Public piece As Cut
Sub test2()
If cuts Is Nothing Then
Set cuts = New Collection
End If
If cutLabels Is Nothing Then
Set cutLabels = New Collection
End If
frmPlanning.Show vbModeless
Set bb = New BigBox
bb.Height = 100
bb.Width = 500
AddCuts 5, 20
AddCuts 10, 10
AddCuts 7, 50
End Sub
Sub AddCuts(numberOfCuts As Integer, widthOfCuts As Integer)
Dim i As Integer
If numberOfCuts <= 0 Then Exit Sub
For i = 1 To numberOfCuts
Set piece = New Cut
piece.Width = widthOfCuts
piece.Height = bb.Height
totalCutsWidth = totalCutsWidth + widthOfCuts
If totalCutsWidth <= bb.Width Then
cuts.Add piece
End If
Next i
DrawCuts
End Sub
Sub DrawCuts()
Dim i As Integer
Dim OffsetX As Integer
Dim newLabel As MSForms.Label
OffsetX = 0
For i = cutLabels.Count To 1 Step -1
frmPlanning.Controls.Remove "Corte" & i
cutLabels.Remove i
Next i
i = 0
OffsetX = frmPlanning.OriginX
For Each piece In cuts
i = i + 1
Set newLabel = frmPlanning.Controls.Add("Forms.Label.1", "Corte" & i, True)
With newLabel
.ControlTipText = .Name
.Left = OffsetX
.Width = piece.Width
.Height = piece.Height
.Top = frmPlanning.OriginY
.BackColor = &HFFFFFF
.BorderStyle = 1
.TextAlign = 2
.Font.Size = 6
.Caption = i
OffsetX = OffsetX + piece.Width
End With
cutLabels.Add newLabel
Next piece
End Sub
Note that add cuts makes sure that the cuts still fit within the big box, and that the drawing of the cuts is separated from that. Also, if the next piece wouldn't fit in the box anymore it won't be added. I.e. if the big box has width 500, and you add 10 cuts of width 25, and then 11 cuts of width 30, it will only add the first 8 of the second batch (10*25=250, 8*30=240, 240+250=490, so the 9th, 10th and 11th do not fit in the 500 total width, therefore will not be added.
Hope this helps and is enough information to merge into your existing solution.

Trying to change an image in a Label, when a certain Button is clicked

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.

Problems with generating labels

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

Trouble with Timer_tick not stopping

I'm very new to programming and vb.net, trying to self teach more so as a hobby, as I have an idea for a program that I would find useful, but I am having trouble getting past this issue and I believe it is to do with the timer.
I have a form of size.(600,600) with one button of size.(450,150) that is set location(100,50) on the form. When clicked I want to move down it's own height, then add a new button in it's place. The code included below works as desired for the first two clicks, but on the third click the button keeps moving and the autoscroll bar extends. I initially thought it was the autoscroll function or the location property, but realised that as the button keeps moving, the timer hasn't stopped. I am aware that the code is probably very clunky in terms of achieving the outcome, and that there are a few lines/variables that are currently skipped over by the compiler (these are from older attempts to figure this out).
I have looked around and can't find the cause of my problem. Any help would be greatly appreciated. Apologies if the code block looks messy - first go.
Public Class frmOpenScreen
Dim intWButtons, intCreateButtonY, intCreateButtonX 'intTimerTick As Integer
Dim arrWNames() As String
Dim ctrlWButtons As Control
Dim blnAddingW As Boolean
Private Sub btnCreateW_Click(sender As System.Object, e As System.EventArgs) Handles btnCreateW.Click
'Creates new Button details including handler
Dim strWName, strWShort As String
Dim intCreateButtonY2 As Integer
Static intNumW As Integer
Dim B As New Button
strWName = InputBox("Please enter the name name of the button you are creating. Please ensure the spelling is correct.", "Create W")
If strWName = "" Then
MsgBox("Nothing Entered.")
Exit Sub
End If
strWShort = strWName.Replace(" ", "")
B.Text = strWName
B.Width = 400
B.Height = 150
B.Font = New System.Drawing.Font("Arial Narrow", 21.75)
B.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
B.Anchor = AnchorStyles.Top
B.Margin = New Windows.Forms.Padding(0, 0, 0, 0)
'Updates Crucial Data (w name array, number of w buttons inc Create New)
If intNumW = 0 Then
ReDim arrWNames(0)
Else
intNumW = UBound(arrWNames) + 1
ReDim Preserve arrWNames(intNumW)
End If
arrWNames(intNumW) = strWShort
intNumW = intNumW + 1
intWButtons = WButtonCount(intWButtons) + 1
'updates form with new button and rearranges existing buttons
intCreateButtonY = btnCreateW.Location.Y
intCreateButtonX = btnCreateW.Location.X
‘intTimerTick = 0
tmrButtonMove.Enabled = True
‘Do While intTimerTick < 16
‘ 'blank to do nothing
‘Loop
'btnCreateW.Location = New Point(intCreateButtonX, intCreateButtonY + 150)
B.Location = New Point(intCreateButtonX, intCreateButtonY)
Me.Controls.Add(B)
B.Name = "btn" & strWShort
intCreateButtonY2 = btnCreateW.Location.Y
If intCreateButtonY2 > Me.Location.Y Then
Me.AutoScroll = False
Me.AutoScroll = True
Else
Me.AutoScroll = False
End If
'MsgBox(intCreateButtonY)
End Sub
Function WButtonCount(ByRef buttoncount As Integer) As Integer
buttoncount = intWButtons
If buttoncount = 0 Then
Return 1
End If
Return buttoncount
End Function
Public Sub tmrButtonMove_Tick(sender As System.Object, e As System.EventArgs) Handles tmrButtonMove.Tick
Dim intTimerTick As Integer
If intTimerTick > 14 Then
intTimerTick = 0
End If
If btnCreateW.Location.Y <= intCreateButtonY + 150 Then
btnCreateW.Top = btnCreateW.Top + 10
End If
intTimerTick += 1
If intTimerTick = 15 Then
tmrButtonMove.Enabled = False
End If
End Sub
End Class
So my current understanding is that the tick event handler should be increasing the timertick variable every time it fires, and that once it has hits 15 it should diable the timer and stop the button moving, but it is not doing so.
Thanks in advance.
IntTimerTick is initialized to 0 at the beginning of every Tick event. This won't happen if you declare it to be static:
Static Dim intTimerTick As Integer

Adding a custom button in title bar VB.NET

I was just wondering if there was a possible way to add a custom button into the title bar using VB.NET. I've seen many such questions on Stack Overflow but failed to get a sure-shot and a working answer.
Can anyone help me around with this issue? I've checked on Google and other website too but it fails to render. I want the code to work on Windows XP, Windows Vista and Windows 7.
I would be grateful if you will be able to give a working code and the button must even be able to accept click events and post it to the form it is on for some action.
Thanks in advance
If you mean winforms, I can think of two ways to do this:
Hide the titlebar and replace it with your own, which I don't recommend.
Build the button as a very small form that you keep docked in the correct position every time your window moves.
Here is an example with some working code:
http://www.dreamincode.net/forums/topic/69215-2008-custom-title-bar/
Basically, you need to create a form with no border, then roll your own "Titlebar" which will basically be an area at the top that you can customize however you want. This is a difficult solution to fully implement properly, but it is probably the way that will best accomplish this.
As Matthew Scharley writes in his answer here:
The following will work in XP, I have no Vista machine handy to test
it, but I think you're issues are steming from an incorrect hWnd
somehow. Anyway, on with the poorly commented code.
I think this doesn't show up graphically in Vista and 7. The translated version of Matthew's code is as follows:
' The state of our little button
Private _buttState As ButtonState = ButtonState.Normal
Private _buttPosition As New Rectangle()
<DllImport("user32.dll")> _
Private Shared Function GetWindowDC(hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(hWnd As IntPtr, ByRef lpRect As Rectangle) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As Integer
End Function
Protected Overrides Sub WndProc(ByRef m As Message)
Dim x As Integer, y As Integer
Dim windowRect As New Rectangle()
GetWindowRect(m.HWnd, windowRect)
Select Case m.Msg
' WM_NCPAINT
' WM_PAINT
Case &H85, &Ha
MyBase.WndProc(m)
DrawButton(m.HWnd)
m.Result = IntPtr.Zero
Exit Select
' WM_ACTIVATE
Case &H86
MyBase.WndProc(m)
DrawButton(m.HWnd)
Exit Select
' WM_NCMOUSEMOVE
Case &Ha0
' Extract the least significant 16 bits
x = (CInt(m.LParam) << 16) >> 16
' Extract the most significant 16 bits
y = CInt(m.LParam) >> 16
x -= windowRect.Left
y -= windowRect.Top
MyBase.WndProc(m)
If Not _buttPosition.Contains(New Point(x, y)) AndAlso _buttState = ButtonState.Pushed Then
_buttState = ButtonState.Normal
DrawButton(m.HWnd)
End If
Exit Select
' WM_NCLBUTTONDOWN
Case &Ha1
' Extract the least significant 16 bits
x = (CInt(m.LParam) << 16) >> 16
' Extract the most significant 16 bits
y = CInt(m.LParam) >> 16
x -= windowRect.Left
y -= windowRect.Top
If _buttPosition.Contains(New Point(x, y)) Then
_buttState = ButtonState.Pushed
DrawButton(m.HWnd)
Else
MyBase.WndProc(m)
End If
Exit Select
' WM_NCLBUTTONUP
Case &Ha2
' Extract the least significant 16 bits
x = (CInt(m.LParam) << 16) >> 16
' Extract the most significant 16 bits
y = CInt(m.LParam) >> 16
x -= windowRect.Left
y -= windowRect.Top
If _buttPosition.Contains(New Point(x, y)) AndAlso _buttState = ButtonState.Pushed Then
_buttState = ButtonState.Normal
' [[TODO]]: Fire a click event for your button
' however you want to do it.
DrawButton(m.HWnd)
Else
MyBase.WndProc(m)
End If
Exit Select
' WM_NCHITTEST
Case &H84
' Extract the least significant 16 bits
x = (CInt(m.LParam) << 16) >> 16
' Extract the most significant 16 bits
y = CInt(m.LParam) >> 16
x -= windowRect.Left
y -= windowRect.Top
If _buttPosition.Contains(New Point(x, y)) Then
m.Result = DirectCast(18, IntPtr)
Else
' HTBORDER
MyBase.WndProc(m)
End If
Exit Select
Case Else
MyBase.WndProc(m)
Exit Select
End Select
End Sub
Private Sub DrawButton(hwnd As IntPtr)
Dim hDC As IntPtr = GetWindowDC(hwnd)
Dim x As Integer, y As Integer
Using g As Graphics = Graphics.FromHdc(hDC)
' Work out size and positioning
Dim CaptionHeight As Integer = Bounds.Height - ClientRectangle.Height
Dim ButtonSize As Size = SystemInformation.CaptionButtonSize
x = Bounds.Width - 4 * ButtonSize.Width
y = (CaptionHeight - ButtonSize.Height) \ 2
_buttPosition.Location = New Point(x, y)
' Work out color
Dim color As Brush
If _buttState = ButtonState.Pushed Then
color = Brushes.LightGreen
Else
color = Brushes.Red
End If
' Draw our "button"
g.FillRectangle(color, x, y, ButtonSize.Width, ButtonSize.Height)
End Using
ReleaseDC(hwnd, hDC)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs)
_buttPosition.Size = SystemInformation.CaptionButtonSize
End Sub