将窗体限制在屏幕的工作区域,不闪烁
Restrain form to the working area of the screen without flickering
我有工作代码,但是当将表单移动到屏幕边界时,我有时会出现表单闪烁。不好看。
有更好的解决方案吗?
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
表单在移动时闪烁的原因是每次在不同位置重新绘制表单时,都会触发 Move
事件。要查看多少,试试这个:
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
您会注意到,只要表单移动了一个像素,事件就会被触发。您将窗体向左移动 100 个像素,该事件将触发 100 次。在每次重绘表单时检查位置,然后计算每个点是否在您想要的范围内,这需要大量工作。所有这些点和矩形等的持续计算导致巨大的开销,结果是没有足够的资源时间来重绘表单中的所有控件,因此表单闪烁。
更好的选择是在表单完成移动时只执行一次所有这些数学运算,如果表单超出屏幕边界则更正表单。有一个 Form.ResizeEnd
事件会在用户完成调整大小或移动表单时触发。通过利用这个事件,代码只会在用户完成他们的操作并且表单在屏幕上重新绘制后被调用一次。然后,如果表单在您想要的范围之外,您可以将表单移动到一个位置。
我没有对 Screen.PrimaryScreen
的使用进行硬编码,而是动态获取表单所在的屏幕。这允许用户将表单移动到多显示器环境中的不同屏幕。
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
你可以试试像这样限制鼠标拖动表单的位置。
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
我有工作代码,但是当将表单移动到屏幕边界时,我有时会出现表单闪烁。不好看。
有更好的解决方案吗?
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
表单在移动时闪烁的原因是每次在不同位置重新绘制表单时,都会触发 Move
事件。要查看多少,试试这个:
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
您会注意到,只要表单移动了一个像素,事件就会被触发。您将窗体向左移动 100 个像素,该事件将触发 100 次。在每次重绘表单时检查位置,然后计算每个点是否在您想要的范围内,这需要大量工作。所有这些点和矩形等的持续计算导致巨大的开销,结果是没有足够的资源时间来重绘表单中的所有控件,因此表单闪烁。
更好的选择是在表单完成移动时只执行一次所有这些数学运算,如果表单超出屏幕边界则更正表单。有一个 Form.ResizeEnd
事件会在用户完成调整大小或移动表单时触发。通过利用这个事件,代码只会在用户完成他们的操作并且表单在屏幕上重新绘制后被调用一次。然后,如果表单在您想要的范围之外,您可以将表单移动到一个位置。
我没有对 Screen.PrimaryScreen
的使用进行硬编码,而是动态获取表单所在的屏幕。这允许用户将表单移动到多显示器环境中的不同屏幕。
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
你可以试试像这样限制鼠标拖动表单的位置。
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