Picturebox Panning Boundary inside Panel - vb.net

My image is inside the panel, I set up a if-statement for the boundary which it can only be moved. When I tried to run it, it looks crappy when the mouse has panned it outside the boundary. Here is my code for panning:
If (mouse.Button = Windows.Forms.MouseButtons.Left) Then
Dim mousePosNow As Point = mouse.Location
Dim deltaX As Integer = mousePosNow.X - mouseDowns.X
Dim deltaY As Integer = mousePosNow.Y - mouseDowns.Y
Dim newX As Integer
Dim newY As Integer
If PictureBox1.Location.X <= Panel1.Location.X And PictureBox1.Location.Y <= Panel1.Location.Y And _
(PictureBox1.Location.X + PictureBox1.Width) >= (Panel1.Location.X + Panel1.Width) And _
(PictureBox1.Location.Y + PictureBox1.Height) >= (Panel1.Location.Y + Panel1.Height) Then
newX = PictureBox1.Location.X + deltaX
newY = PictureBox1.Location.Y + deltaY
End If
PictureBox1.Location = New Point(newX, newY)
End If

First of all, if you've got your PictureBox inside your Panel, then you don't need to account for the Panel's location, since the PictureBox's location will be zeroed at the top-left of the Panel.
This condition:
If PictureBox.Location.X <= Panel1.Location.X ...
should be changed to this condition:
If PictureBox.Location.X <= 0
Also, the problem you're running into is due to that fact that your event-handler is flipping between moving the PictureBox from 0,0 to moving the PictureBox to the delta location.
E.g:
When you drag the PictureBox towards the right such that it's left boundary goes past the Panel's left boundary (i.e. PictureBox.Location.X > 0) then the condition of your if-statement evaluates to False and the PictureBox's location is set to 0. However, since you've now changed its location, the MouseMove event is triggered again and this time the condition of your if-statement evaluates to True and the PictureBox's location is set to the delta location.
Once again the MouseMove event is triggered and the scenario repeats, flipping the PictureBox's location back and forth, causing a jittering effect.
You can fix this by changing your condition to rely on the new location of the PictureBox, instead of the current location:
This condition:
If PictureBox.Location.X <= 0 ...
should be changed to this condition:
If (PictureBox.Location.X + deltaX) <= 0 ...
This fixes the jittering problem but your code only takes care of the case where the PictureBox is dragged towards the right and bottom.
Instead of writing more conditions, you could simplify your code by moving the calculations into a separate function that handles each axis separately:
If (mouse.Button = Windows.Forms.MouseButtons.Left) Then
Dim mousePosNow As Point = mouse.Location
Dim deltaX As Integer = mousePosNow.X - mouseDowns.X
Dim deltaY As Integer = mousePosNow.Y - mouseDowns.Y
Dim newX As Integer = Clamp(PictureBox1.Location.X + deltaX, PictureBox1.Width, Panel1.Width)
Dim newY As Integer = Clamp(PictureBox1.Location.Y + deltaY, PictureBox1.Height, Panel1.Height)
PictureBox1.Location = New Point(newX, newY)
End If
...
Private Function Clamp(val As Integer, outerBound As Integer, innerBound As Integer) As Integer
Dim newVal As Integer = val
If newVal > 0 Then
newVal = 0
End If
If newVal + outerBound < innerBound Then
newVal = innerBound - outerBound
End If
Return newVal
End Function

Related

How do I "move" a picturebox image around a grid of pictureboxes?

I cannot figure out how to move an image from one picturebox to another inside a grid of pictureboxes (all of the pictureboxes are created in an 2 dimensional array, for x and y ) with WASD/arrow keys. Is there a way I can assign each picturebox with co ordinates where I can manipulate it to change in the image in the pictureboxes to give the illusion of movement?
For example: program starts with picturebox (3,3) to have the player image set and a variable called "CurrentPicBox = picturebox(Xpos,Ypos)" ; User presses W/Up arrow; this makes the picturebox (that has the player image set) to clear the image and makes "CurrentPicBox = picturebox(Xpos, Ypos + 1). Which then makes the picturebox(3,4) have the image set.
Would this work or is this logic incorrect?
Your logic should work just fine! Although I think it would be easier to manage if you'd use (0, 0) as the top-left corner and (4, 4) as the bottom-right one.
Moving the player would then be:
Up: (Xpos, Ypos - 1)
Down: (Xpos, Ypos + 1)
Left: (Xpos - 1, Ypos)
Right: (Xpos + 1, Ypos)
Here's an example class for creating and using a game grid:
Public NotInheritable Class GameBoard
'The size of each picture box.
Public Shared ReadOnly GameTileSize As New Size(16, 16)
'The 2D array holding our grid.
Private Grid As PictureBox(,)
Private PosX As Integer = 0
Private PosY As Integer = 0
Public Sub MovePlayer(ByVal Direction As MovementDirection)
Select Case Direction
Case MovementDirection.Left
If PosX - 1 < 0 Then Return 'Error checking. Cannot move outside grid.
Grid(PosX - 1, PosY).Image = Grid(PosX, PosY).Image 'Move image to the left.
Grid(PosX, PosY).Image = Nothing 'Clear the current picture box.
PosX -= 1
Case MovementDirection.Right
If PosX + 1 >= Grid.GetLength(0) Then Return
Grid(PosX + 1, PosY).Image = Grid(PosX, PosY).Image
Grid(PosX, PosY).Image = Nothing
PosX += 1
Case MovementDirection.Up
If PosY - 1 < 0 Then Return
Grid(PosX, PosY - 1).Image = Grid(PosX, PosY).Image
Grid(PosX, PosY).Image = Nothing
PosY -= 1
Case MovementDirection.Down
If PosY + 1 >= Grid.GetLength(1) Then Return
Grid(PosX, PosY + 1).Image = Grid(PosX, PosY).Image
Grid(PosX, PosY).Image = Nothing
PosY += 1
End Select
End Sub
Public Sub New(ByVal Container As Container, ByVal PlayerSprite As Image, ByVal TilesX As Integer, ByVal TilesY As Integer) As PictureBox(,)
'Initialize our array.
Grid = New PictureBox(TilesX - 1, TilesY - 1) {}
'Iterate every "coordinate" and add a game tile to it.
For x = 0 To TilesX - 1
For y = 0 To TilesY - 1
'Create a tile of the appropriate size and place it at a location that is a multiple of its size.
Dim Tile As New PictureBox() With {
.Size = GameBoard.GameTileSize,
.Location = New Point(x * GameBoard.GameTileSize.X, y * GameBoard.GameTileSize.Y),
.BorderStyle = BorderStyle.FixedSingle 'Add a border to the tile.
}
'Add the tile to our array.
Grid(x, y) = Tile
'Add the tile to the specified container.
Container.Controls.Add(Tile)
Next
Next
'Place the player at the initial coordinates.
Grid(PosX, PosY).Image = PlayerSprite
End Sub
Public Enum MovementDirection As Integer
Left = 0
Right
Up
Down
End Enum
End Class
Then you can use it like so:
'The variable holding our game board.
Dim Board As GameBoard
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Create the grid inside the form (Me), 4 tiles tall and wide.
Board = New GameBoard(Me, My.Resources.Player, 4, 4)
End Sub
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
'Handle movement.
Select Case e.KeyCode
Case Keys.Left, Keys.A : Board.MovePlayer(MovementDirection.Left)
Case Keys.Right, Keys.D : Board.MovePlayer(MovementDirection.Right)
Case Keys.Up, Keys.W : Board.MovePlayer(MovementDirection.Up)
Case Keys.Down, Keys.S : Board.MovePlayer(MovementDirection.Down)
End Select
End Sub
Replace My.Resources.Player with your actual player sprite.

Optimal sizes to display X picture boxes in an given space

I searched before posting but couldn't find anything close to my issue.
What I need to figure out is how to come with the optimal width and height of picture boxes (with a 4:3 ratio), given the required number of boxes to be displayed, and the available space.
Now, it's not as simple as a just dividing the available space by the number of required boxes, because the available space is not a uniform shape, but rather two rectangles of which size may vary (see this picture, it's the a+b space).
If fact, I have tried starting from there with the following code :
Private Sub LayoutSnapshots()
Dim lTotalSpace As Single, lSnapsize As Single, sXSize As Single, sYSize As Single
Dim I As Integer, J As Integer, X As Integer = 0, Y As Integer = 0, oPic As PictureBox
' bSnaps is the number of picture boxes to be displayed
If stSetting.bSnaps = 0 Then Exit Sub
' oSnaps is a List(Of PictureBoxe) to groupp the actual picture boxes
If oSnaps.Count > 0 Then
For Each oCtrl As PictureBox In oSnaps
Me.Controls.Remove(oCtrl)
Next
End If
oSnaps.Clear()
' Calculating the a+b space shown on the picture
lTotalSpace = ((Me.ClientSize.Height - MenuStrip1.Height) * Me.ClientSize.Width) - ((picPreview.Width + iMargin) * (picPreview.Height + iMargin))
If lTotalSpace < 1 Then
MsgBox("Window is too small. Please adjust one of these settings : Window size, Snapshots count, Live free view size.", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Exclamation Or MsgBoxStyle.OkOnly)
Exit Sub
End If
'calculating a single picture's size by dividing total space by the number of snaps
lSnapsize = Math.Truncate(lTotalSpace / stSetting.bSnaps)
'Calculating Height and Width, with 4:3 ratio
sXSize = Math.Truncate(Math.Sqrt((4 * lSnapsize) / 3))
sYSize = Math.Truncate(Math.Sqrt((3 * lSnapsize) / 4))
For I = 1 To stSetting.bSnaps
If oPic IsNot Nothing Then oPic = Nothing
oPic = New PictureBox
oPic.BackColor = Color.White
oPic.BorderStyle = BorderStyle.FixedSingle
oPic.Size = New Size(sXSize - 1, sYSize - 1)
oPic.Location = New Point(X * sXSize, (Y * sYSize) + MenuStrip1.Height)
oSnaps.Add(oPic)
' Layed them successively on screen, need to optimize this
If ((X + 2) * sXSize) > (Me.ClientSize.Width) Then
X = 0
Y += 1
Else
X += 1
End If
Next
For Each oCtrl As PictureBox In oSnaps
Me.Controls.Add(oCtrl)
Next
End Sub
But obviously with all the possibilities of windows resizing, I couldn't think of any practical way to optimize it.
I am pretty sure this has to do with "operation research", as I recall we did optimization problems like this back then when I was a student, but I'm not sure how to actually model this or even if it is solvable by linear programming.
I have figured this out. The solution is kind of a "brute force" technique, it doesn't always return the optimum BUT the error is merely a few pixels. I used the code below, it works but it might need further optimization in terms of spacing. I couldn't comment on everything since I have a time pressure right now, but still wanted to share the answer, so just take some time to analyze it :
Private Sub LayoutSnapshots()
Dim sA As Single, sB As Single, sTotal As Single, sSnap As Single, sWidth As Single, sHeight As Single
Dim iCount As Integer = stSetting.bSnaps, iFit As Integer, iX As Integer, iY As Integer, iYg As Integer, I As Integer
Dim rA As Rectangle, rB As Rectangle, oPic As PictureBox, lpLoc As New List(Of Point), pLoc As New Point
Static bWarn As Boolean
Dim gPic As Graphics
' bSnaps is the number of picture boxes to be displayed
If stSetting.bSnaps = 0 Then Exit Sub
' If controls already on form, remove them and start form scratch
If oSnaps.Count > 0 Then
For Each oCtrl As PictureBox In oSnaps
Me.Controls.Remove(oCtrl)
Next
End If
' oSnaps is a List(Of PictureBox) grooping the picture boxes. Clear it for now
oSnaps.Clear()
'sA, sB are the sizes of spaces A and B respectively
sA = (Me.ClientSize.Width * (Me.ClientSize.Height - (MenuStrip1.Height + picPreview.Height + iMargin)))
sB = ((Me.ClientSize.Width - (picPreview.Width + iMargin)) * (picPreview.Height + iMargin))
' Total free space
sTotal = sA + sB
' This condition is important. It ensures there is at least one solution
' before entering the loops bellow. Otherwise we might get stuck in an infinite loop
If (sTotal < (stSetting.bSnaps * stSetting.bSnaps)) Then
' bWarn is a static boolean. Since this Sub is called from Form_Resize event, we
' want to warn the user only once when there is no space.
' Otherwise it becomes annoying.
If bWarn Then MsgBox("Window is too small. Please adjust one of these settings : Window size, Snapshots count, Live free view size.", MsgBoxStyle.ApplicationModal Or MsgBoxStyle.Exclamation Or MsgBoxStyle.OkOnly)
bWarn = False
Exit Sub
End If
bWarn = True
Me.UseWaitCursor = True
Do
'rA, rB are the bounding rectangles of spaces A and B respectively
rA = New Rectangle(0, MenuStrip1.Height, Me.ClientSize.Width, Me.ClientSize.Height - (MenuStrip1.Height + picPreview.Height + iMargin))
rB = New Rectangle(0, picPreview.Top, Me.ClientSize.Width - (picPreview.Width + iMargin), picPreview.Height + iMargin)
' A single box's size
sSnap = Math.Truncate(sTotal / iCount)
' Width and Height with 4:3 aspect ratio.
sWidth = Math.Truncate(Math.Sqrt((4 * sSnap) / 3))
sHeight = Math.Truncate(Math.Sqrt((3 * sSnap) / 4))
' iFit keeps track of how many boxes we could fit in total
iFit = 0
iYg = 0
lpLoc.Clear()
' It would be a bit too long to explain the next block of code and I have a deadline to meet
' I'll comenting on that later
iX = 0
iY = 0
Do While (rA.Height >= ((sHeight * (iY + 1)) + 1))
If (((iX + 1) * sWidth) + 1) <= rA.Width Then
iFit += 1
lpLoc.Add(New Point(rA.X + ((iX * sWidth) + 1), rA.Y + ((iYg * sHeight) + 1)))
iX += 1
Else
iX = 0
iY += 1
iYg += 1
End If
Loop
'Add unused space from A to B.
rB.Height = rB.Height + (rA.Height - ((iYg * sHeight) + 1))
iX = 0
iY = 0
Do While (rB.Height >= ((sHeight * (iY + 1)) + 1))
If (((iX + 1) * sWidth) + 1) <= rB.Width Then
iFit += 1
lpLoc.Add(New Point(rB.X + ((iX * sWidth) + 1), rA.Y + ((iYg * sHeight) + 1)))
iX += 1
Else
iX = 0
iY += 1
iYg += 1
End If
Loop
Application.DoEvents()
iCount += 1
Loop While iFit < stSetting.bSnaps
' Add controls to form. Lay them one next to each other.
iX = 0
iY = 0
For I = 1 To stSetting.bSnaps
If oPic IsNot Nothing Then oPic = Nothing
oPic = New PictureBox
oPic.BackColor = Color.Cyan
oPic.BorderStyle = BorderStyle.FixedSingle
oPic.Size = New Size(sWidth - 1, sHeight - 1)
oPic.Location = lpLoc(I - 1)
' Just for debugging, displays index of each box inside it.
oPic.Image = New Bitmap(oPic.Width, oPic.Height)
gPic = Graphics.FromImage(oPic.Image)
gPic.DrawString(I, New Font("Arial", 10, FontStyle.Regular), Brushes.Red, New Point(0, 0))
oSnaps.Add(oPic)
Me.Controls.Add(oSnaps.Last)
Next
'Catch Ex As Exception
'Finally
Me.UseWaitCursor = False
'End Try
End Sub
P.S : Anyone please feel free to add more explanation to the code if you want.

Mouse Location + Amount IntersectsWith Button

I have the cursor invisible and when a user scrolls over a button it highlights. The problem is when a user is in between buttons, he does not know where his cursor is.
On the mouse leave event I want the cursor to jump to the closest button (not the one he just left)
Private Sub btnNumbers_Mouseleave(sender As System.Object, e As System.EventArgs) Handles btnAlef.MouseLeave, btnBkspc.MouseLeave, btnBack.MouseLeave, btnClearAll.MouseLeave, btnDeleteWord.MouseLeave, btnEditMenu.MouseLeave, btnUndo.MouseLeave, btnSpeak.MouseLeave, btnGimel.MouseLeave, btnZayin.MouseLeave, btnYud.MouseLeave, btnVav.MouseLeave, btnTzadik.MouseLeave, btnTuf.MouseLeave, btnTes.MouseLeave, btnSpace.MouseLeave, btnShin.MouseLeave, btnSamech.MouseLeave, btnReish.MouseLeave, btnQuestion.MouseLeave, btnPred5.MouseLeave, btnPred4.MouseLeave, btnPred3.MouseLeave, btnPred2.MouseLeave, btnPred1.MouseLeave, btnPeriod.MouseLeave, btnPercent.MouseLeave, btnOpenParen.MouseLeave, btnNun.MouseLeave, btnMem.MouseLeave, btnLetterPrediction2.MouseLeave, btnLetterPrediction1.MouseLeave, btnLamed.MouseLeave, btnKuf.MouseLeave, btnHey.MouseLeave, btnFey.MouseLeave, btnExclamation.MouseLeave, btnEnter.MouseLeave, btnEnderTzadik.MouseLeave, btnEnderNun.MouseLeave, btnEnderMem.MouseLeave, btnEnderFey.MouseLeave, btnEnderChaf.MouseLeave, btnDollar.MouseLeave, btnDaled.MouseLeave, btnCloseParen.MouseLeave, btnChes.MouseLeave, btnChaf.MouseLeave, btnBkspc.MouseLeave, btnBeis.MouseLeave, btnAyin.MouseLeave, btnApostrophe.MouseLeave, btn9.MouseLeave, btn8.MouseLeave, btn7.MouseLeave, btn6.MouseLeave, btn5.MouseLeave, btn4.MouseLeave, btn3.MouseLeave, btn2.MouseLeave, btn1.MouseLeave, btn0.MouseLeave, btnSavedPhrases5.MouseLeave, btnSavedPhrases4.MouseLeave, btnSavedPhrases3.MouseLeave, btnSavedPhrases2.MouseLeave, btnSavedPhrases1.MouseLeave, btnSettings.MouseLeave, btnPhrases.MouseLeave, btnNumbers.MouseLeave, btnMinimize.MouseLeave, btnHebrew.MouseLeave, btnExit.MouseLeave, btnCopy.MouseLeave, btnRightWord.MouseLeave, btnRightChar.MouseLeave, btnLeftWord.MouseLeave, btnLeftChar.MouseLeave, btnHome.MouseLeave, btnEnd.MouseLeave, btnT8.MouseLeave, btnT7.MouseLeave, btnT6.MouseLeave, btnT5.MouseLeave, btnT4.MouseLeave, btnT3.MouseLeave, btnT2.MouseLeave, btnT1.MouseLeave
Dim btn As Button = DirectCast(sender, Button)
btn.FlatStyle = FlatStyle.Standard
Dim GetCursorPos = Cursor.Position
If FormSettings.chbxBorderHover.Checked = True Then
For Each c As Control In Me.Controls.OfType(Of Button)()
If GetCursorPos.IntersectsWith(c.Bounds) Then
Cursor.Position = c.Location
Exit For
End If
Next
End If
End Sub
This is what I have so far, however intercectsWith does not work with a drawing point
Is there any way for my to check what the closest button is?
I think the interface will be hard to use (especially if the buttons are not large enough), but here's another implementation:
Private Sub btnNumbers_Mouseleave(sender As System.Object, e As System.EventArgs) Handles btnAlef.MouseLeave, btnBkspc.MouseLeave, btnBack.MouseLeave, btnClearAll.MouseLeave, btnDeleteWord.MouseLeave, btnEditMenu.MouseLeave, btnUndo.MouseLeave, btnSpeak.MouseLeave, btnGimel.MouseLeave, btnZayin.MouseLeave, btnYud.MouseLeave, btnVav.MouseLeave, btnTzadik.MouseLeave, btnTuf.MouseLeave, btnTes.MouseLeave, btnSpace.MouseLeave, btnShin.MouseLeave, btnSamech.MouseLeave, btnReish.MouseLeave, btnQuestion.MouseLeave, btnPred5.MouseLeave, btnPred4.MouseLeave, btnPred3.MouseLeave, btnPred2.MouseLeave, btnPred1.MouseLeave, btnPeriod.MouseLeave, btnPercent.MouseLeave, btnOpenParen.MouseLeave, btnNun.MouseLeave, btnMem.MouseLeave, btnLetterPrediction2.MouseLeave, btnLetterPrediction1.MouseLeave, btnLamed.MouseLeave, btnKuf.MouseLeave, btnHey.MouseLeave, btnFey.MouseLeave, btnExclamation.MouseLeave, btnEnter.MouseLeave, btnEnderTzadik.MouseLeave, btnEnderNun.MouseLeave, btnEnderMem.MouseLeave, btnEnderFey.MouseLeave, btnEnderChaf.MouseLeave, btnDollar.MouseLeave, btnDaled.MouseLeave, btnCloseParen.MouseLeave, btnChes.MouseLeave, btnChaf.MouseLeave, btnBkspc.MouseLeave, btnBeis.MouseLeave, btnAyin.MouseLeave, btnApostrophe.MouseLeave, btn9.MouseLeave, btn8.MouseLeave, btn7.MouseLeave, btn6.MouseLeave, btn5.MouseLeave, btn4.MouseLeave, btn3.MouseLeave, btn2.MouseLeave, btn1.MouseLeave, btn0.MouseLeave, btnSavedPhrases5.MouseLeave, btnSavedPhrases4.MouseLeave, btnSavedPhrases3.MouseLeave, btnSavedPhrases2.MouseLeave, btnSavedPhrases1.MouseLeave, btnSettings.MouseLeave, btnPhrases.MouseLeave, btnNumbers.MouseLeave, btnMinimize.MouseLeave, btnHebrew.MouseLeave, btnExit.MouseLeave, btnCopy.MouseLeave, btnRightWord.MouseLeave, btnRightChar.MouseLeave, btnLeftWord.MouseLeave, btnLeftChar.MouseLeave, btnHome.MouseLeave, btnEnd.MouseLeave, btnT8.MouseLeave, btnT7.MouseLeave, btnT6.MouseLeave, btnT5.MouseLeave, btnT4.MouseLeave, btnT3.MouseLeave, btnT2.MouseLeave, btnT1.MouseLeave
Dim btn As Button = DirectCast(sender, Button)
btn.FlatStyle = FlatStyle.Standard
If FormSettings.chbxBorderHover.Checked = True Then
Dim currentPos As Point = Me.PointToClient(Cursor.Position)
Dim closestButton = (From x In Me.Controls.OfType(Of Button)()
Where x IsNot btn
Order By PointToButtonDistance(currentPos, x) Ascending).FirstOrDefault
Cursor.Position = closestButton.PointToScreen(New Point(0, 0))
End If
End Sub
Private Function PointToButtonDistance(ByVal pt As Point, ByVal btn As Button) As Long
Dim center As New Point(btn.Location.X + btn.Width / 2, btn.Location.Y + btn.Height / 2)
Dim dx As Integer = Math.Max(Math.Abs(pt.X - center.X) - btn.Width / 2, 0)
Dim dy As Integer = Math.Max(Math.Abs(pt.Y - center.Y) - btn.Height / 2, 0)
Return dx * dx + dy * dy
End Function
Dim minDist As Double = Double.MaxVal
Dim closestCtrl as Control = Nothing
For Each c As Control In Me.Controls.OfType(Of Button)()
If c Is sender Then Continue For ' Skip the one we came from
Dim dist = ComputeDistance(Cursor.Position, c.Bounds)
If dist < minDist Then
minDist = dist
closestCtrl = c
End If
Next
...
' Compute distance between 2 points
Private Function ComputeDistance(x1 as Integer, y1 as Integer, x2 as Integer, y2 as Integer) as Double
Dim dx = (x1 - x2)
Dim dy = (y1 - y2)
Return Math.Sqrt(dx*dx + dy*dy)
End Function
' Compute distance between a point and the closest corner of a rectangle
' I'm going to do this a lazy way. There's probably a better one
Private Function ComputeDistance(pt as Point, rect as Rectangle) as Distance
Dim dist as New List(of Double)
dist.Add(ComputeDistance(pt.x, pt.y, rect.Left, rect.Top)
dist.Add(ComputeDistance(pt.x, pt.y, rect.Right, rect.Top)
dist.Add(ComputeDistance(pt.x, pt.y, rect.Left, rect.Bottom)
dist.Add(ComputeDistance(pt.x, pt.y, rect.Right, rect.Bottom)
Return dist.Min()
End Function

Runtime error message Index was outside the bounds of the array. for Visual Basic 2010

I am computing the ROI with a moving rectangle and extracting the ROI to compute the standard deviation, mean, area and Pixel value coordinates X and Y in a seperate form2 by clicking the mouse. At this juncture I am trying to pass a function from the main Form that loads the Image and displays the rectangle to another Form that has the displayed properties of the mean and standard deviation etc. However, I'm receiving errors in runtime in the function that contains the standard deviation. The error displayed is
Index was outside the bounds of the array.
It is displayed at the end of this portion of the code in the function StD, i.e at the end of the mean part'
SD(count) = Double.Parse(pixelcolor.R) + Double.Parse(pixelcolor.G) + Double.Parse(pixelcolor.B) - mean
what is this actually saying and how can I fix this situation. Any tips and ideas, thanks.
My code is at the bottom
enterPublic Function StD(ByVal image As Bitmap, ByVal mean As Double, ByVal meancount As Integer) As Double
Dim SD(SquareHeight * SquareWidth) As Double
Dim count As Integer = 0
For i = 0 To SquareWidth
For j = 0 To SquareHeight
Dim pixelcolor As Color = image.GetPixel(i, j)
SD(count) = Double.Parse(pixelcolor.R) + Double.Parse(pixelcolor.G) + Double.Parse(pixelcolor.B) - mean
count += 1
Next
Next
Dim SDsum As Double = 0
For i = 0 To count
SDsum = SDsum + SD(i)
Next
SDsum = SDsum / (SquareHeight * SquareWidth)
SDsum = ((SDsum) ^ (1 / 2))
Return SDsum
End Function code here
I would like to pass this using the code below
enterPrivate Sub PictureBox1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
Dim mean As Double = 0
Dim meancount As Integer = 0
Dim bmap As New Bitmap(400, 400)
bmap = PictureBox1.Image
Dim colorpixel As Color = bmap.GetPixel(e.X, e.Y)
' Dim pixels As Double = colorpixel.R + colorpixel.G + colorpixel.B
If e.Button = Windows.Forms.MouseButtons.Left AndAlso Rect.Contains(e.Location) Then
If (PictureBox1.Image Is Nothing) Or (PictureBox1.Height - (e.Y + SquareHeight) < 0) Or (PictureBox1.Width - (e.X + SquareWidth) < 0) Then
Else
Dim ROI As New Bitmap(400, 400)
Dim x As Integer = 0
Dim countx As Integer = 0
Dim county As Integer = 0
For i = e.X To (e.X + SquareWidth)
For j = (e.Y + x) To (e.Y + SquareHeight)
Dim pixelcolor As Color = bmap.GetPixel(i, j)
ROI.SetPixel(countx, county, pixelcolor)
mean = mean + pixelcolor.R + pixelcolor.G + pixelcolor.B
county += 1
meancount += 1
Next
county = 0
countx += 1
x = x + 1
Next
mean = mean / (meancount * 3)
Dim SD = mean - 75
Dim area As Integer = (SquareHeight * SquareWidth)
Dim anotherForm As Form2
anotherForm = New Form2(mean, StD(bmap, mean, meancount), area, 34)
anotherForm.Show()
End If
End If
' Catch ex As Exception
' MessageBox.Show(ex.Message())
' End Try
End Sub code here
To be displayed with this code
enter Public Sub New(ByVal mean As Double, ByVal StD As Double, ByVal Area As Integer, ByVal pixel As Double)
MyBase.New()
InitializeComponent()
TextBox1.Text = mean.ToString()
TextBox2.Text = StD.ToString()
TextBox3.Text = Area.ToString()
TextBox4.Text = pixel.ToString()
End Sub code here
The problem probably is because of these lines:
For i = 0 To SquareWidth
For j = 0 To SquareHeight
Try using this instead:
For i = 0 To SquareWidth - 1
For j = 0 To SquareHeight - 1

How to lock the form from further resizing with aspect ratio resizing

I currently use this code and it works flawlessly
My question is how do I modify the WndProc to stop at my preferred limit of Width and Height. Okay I solved that by setting the MinimumSize, but a new problem arises when the aspect ratio of the form reaches the limit of the windows desktop maximum right size it starts to mess up the aspect ratio starts streching instead of locking up.
Need to somehow fix the WndProc with SystemInformation.VirtualScreen.Width to stop increasing both sizes when the limit Width is hit.
I added this which works but it's still only for my resolution how do I make it universal to support all resolutions.
If r.right - r.left > SystemInformation.VirtualScreen.Width Then
r.bottom = 900 'quick fix (not good) how to calculate this value?
End If
source of code:
http://www.vb-helper.com/howto_net_form_fixed_aspect.html
Imports System.Runtime.InteropServices
...
Public Structure Rect
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
Protected Overrides Sub WndProc(ByRef m As _
System.Windows.Forms.Message)
Static first_time As Boolean = True
Static aspect_ratio As Double
Const WM_SIZING As Long = &H214
Const WMSZ_LEFT As Integer = 1
Const WMSZ_RIGHT As Integer = 2
Const WMSZ_TOP As Integer = 3
Const WMSZ_TOPLEFT As Integer = 4
Const WMSZ_TOPRIGHT As Integer = 5
Const WMSZ_BOTTOM As Integer = 6
Const WMSZ_BOTTOMLEFT As Integer = 7
Const WMSZ_BOTTOMRIGHT As Integer = 8
If m.Msg = WM_SIZING And m.HWnd.Equals(Me.Handle) Then
' Turn the message's lParam into a Rect.
Dim r As Rect
r = DirectCast( _
Marshal.PtrToStructure(m.LParam, _
GetType(Rect)), _
Rect)
' The first time, save the form's aspect ratio.
If first_time Then
first_time = False
aspect_ratio = (r.bottom - r.top) / (r.right - _
r.left)
End If
' Get the current dimensions.
Dim wid As Double = r.right - r.left
Dim hgt As Double = r.bottom - r.top
' Enlarge if necessary to preserve the aspect ratio.
If hgt / wid > aspect_ratio Then
' It's too tall and thin. Make it wider.
wid = hgt / aspect_ratio
Else
' It's too short and wide. Make it taller.
hgt = wid * aspect_ratio
End If
' See if the user is dragging the top edge.
If m.WParam.ToInt32 = WMSZ_TOP Or _
m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
m.WParam.ToInt32 = WMSZ_TOPRIGHT _
Then
' Reset the top.
r.top = r.bottom - CInt(hgt)
Else
' Reset the height to the saved value.
r.bottom = r.top + CInt(hgt)
End If
' See if the user is dragging the left edge.
If m.WParam.ToInt32 = WMSZ_LEFT Or _
m.WParam.ToInt32 = WMSZ_TOPLEFT Or _
m.WParam.ToInt32 = WMSZ_BOTTOMLEFT _
Then
' Reset the left.
r.left = r.right - CInt(wid)
Else
' Reset the width to the saved value.
r.right = r.left + CInt(wid)
End If
' Update the Message object's LParam field.
Marshal.StructureToPtr(r, m.LParam, True)
End If
MyBase.WndProc(m)
End Sub
You can find out user's desktop resolution with Screen.PrimaryScreen.Bounds.Bottom and Screen.PrimaryScreen.Bounds.Right