Reputation: 444
I have working code but when moving the form to the screen bounds I sometimes get form flickering. It doesn't look good.
Is there a better solution for this?
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
Dim p As Point
p = Me.Location
Dim screenWidth As Integer = Screen.PrimaryScreen.Bounds.Width
Dim screenHeight As Integer = Screen.PrimaryScreen.Bounds.Height
Dim TaskBarH As Integer = screenHeight - Screen.PrimaryScreen.WorkingArea.Height
If p.X < 0 Then
Me.Location = New System.Drawing.Point(0, p.Y)
ElseIf p.X > screenWidth - Me.Size.Width Then
Me.Location = New System.Drawing.Point(screenWidth - Me.Size.Width, p.Y)
End If
If p.Y < 0 Then
Me.Location = New System.Drawing.Point(p.X, 0)
ElseIf p.Y > screenHeight - Me.Size.Height - TaskBarH Then
Me.Location = New System.Drawing.Point(p.X, screenHeight - Me.Size.Height - TaskBarH)
End If
End Sub
Upvotes: 1
Views: 1635
Reputation: 1093
You can try restricting where the mouse can drag the form like this.
Option Strict On
Option Explicit On
Option Infer Off
Imports System.Runtime.InteropServices
Public Class Form1
Public Declare Function SetCursorPos Lib "user32.dll" (X As Integer, Y As Integer) As Boolean
Public Function AbsoluteFormMousePosition() As Point
Dim mousePos As Point = PointToClient(MousePosition)
Dim hBorderWidth As Integer = (Me.Width - Me.ClientRectangle.Width) \ 2
Dim vBorderWidth As Integer = hBorderWidth
Dim TitleHeight As Integer = (Me.Height - Me.ClientRectangle.Height) - vBorderWidth
Dim newPos As New Point(mousePos.X + hBorderWidth, mousePos.Y + TitleHeight)
Return newPos
End Function
Function TryOffest(range As Range, find As Integer, ByRef newValue As Integer) As Boolean
If Not range.Contains(find) Then
Dim diff As Integer
diff = If(find > range.Upper, find - range.Upper, range.Lower - find)
newValue += If(find > range.Upper, -(diff), diff)
Return True
End If
Return False
End Function
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
Dim mousePos As Point = MousePosition
Dim newX As Integer = mousePos.X
Dim newY As Integer = mousePos.Y
Dim vChanged, hChanged As Boolean
Dim hBorder As Integer = Me.Width - Me.ClientRectangle.Width
Dim vBorder As Integer = Me.Height - Me.ClientRectangle.Height
Dim adjX As Integer = Me.PointToClient(mousePos).X
Dim adjY As Integer = Me.PointToClient(mousePos).Y
Dim titleMousePosition As Integer = hBorder - Me.PointToClient(mousePos).Y
Dim verticalBounds As New Range(AbsoluteFormMousePosition.Y + 2, Screen.PrimaryScreen.WorkingArea.Height - (Me.Height - vBorder - adjY + 2))
Dim horizontalBounds As New Range(adjX + 2, Screen.PrimaryScreen.WorkingArea.Width - (Me.Width - hBorder - adjX + 2))
vChanged = TryOffest(verticalBounds, mousePos.Y, newY)
hChanged = TryOffest(horizontalBounds, mousePos.X, newX)
If vChanged OrElse hChanged Then SetCursorPos(newX, newY)
End Sub
End Class
Public Class Range
Public Lower, Upper As Integer
Sub New(lower As Integer, upper As Integer)
Me.Lower = lower : Me.Upper = upper
End Sub
Public Function Contains(number As Integer) As Boolean
If number >= Lower AndAlso number <= Upper Then Return True Else Return False
End Function
End Class
Upvotes: 1
Reputation: 1678
The reason your form is flickering when you are moving it around, is because every time the form is redrawn in a different location, the Move
event is fired. To see just how much, try this:
Option Strict On
Option Explicit On
Public Class Form1
Dim counter As Int64
Private Sub Form1_Move(sender As Object, e As EventArgs) Handles Me.Move
counter += 1
Console.WriteLine(counter)
End Sub
End Class
You'll notice that anytime the form is moved even just one pixel, the event is getting fired. You move the form 100 pixels to the left, that event is going to fire 100 times. That's a lot of work to be checking the location on every redraw of the form and then computing if each individual point is within the bounds that you want. All that constant computing of the points and rectangles and such is causing a huge overhead and the result is not enough resource time to redraw all of the controls in the form, hence the flickering of the form.
The better option would be to do all of that math only once when the form is done moving and correct the form if it is outside of the screen bounds. There is a Form.ResizeEnd
event that is fired anytime the user has finished re-sizing or moving a form. By utilizing this event, the code will only get called once after the user has finished their action and the form has redrawn itself on the screen. Then if the form is outside of the bounds that you want, you can move the form to a location that is.
Rather than hard-coding the use of Screen.PrimaryScreen
, I am dynamically getting the screen in which the form resides. This allows the user to move the form to a different screen in a multi-monitor environment.
Option Strict On
Option Explicit On
Public Class Form1
Private Sub Form1_resizeEnd(sender As Object, e As EventArgs) Handles Me.ResizeEnd
Dim screenArea As Rectangle = Screen.GetWorkingArea(Me.Location)
Dim formArea As Rectangle = Me.DesktopBounds
If Me.WindowState = FormWindowState.Normal AndAlso Not screenArea.Contains(formArea) Then
If formArea.Top < screenArea.Top Then Me.Top = 0
If formArea.Left < screenArea.Left Then Me.Left = 0
If formArea.Right > screenArea.Right Then Me.Left = Me.Left - (formArea.Right - screenArea.Right)
If formArea.Bottom > screenArea.Bottom Then Me.Top = Me.Top - (formArea.Bottom - screenArea.Bottom)
End If
End Sub
End Class
Upvotes: 1