Jovica
Jovica

Reputation: 444

Restrain form to the working area of the screen without flickering

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

Answers (2)

Paul Ishak
Paul Ishak

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

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

Related Questions