代码之家  ›  专栏  ›  技术社区  ›  Frosty840

根据鼠标位置调整控件大小时,尝试避免控件“抖动”

  •  0
  • Frosty840  · 技术社区  · 15 年前

    我在“FollowTableLayoutPanel1”中的一行中有一组控件,包含在“TableLayoutPanel2”中。如果鼠标的控制中心更大,则根据鼠标的位置调整鼠标的大小。因为FollowTableLayoutPanel1的anchor属性设置为“Top”,所以它在TableLayoutPanel2中重新居中。

    FollowTableLayoutPanel1的重新居中可能会使控件远离鼠标指针一个像素,从而导致控件收缩,从而导致FollowTableLayoutPanel1重新居中,使控件更靠近鼠标指针,从而导致控件增大,从而导致FollowTableLayoutPanel1重新居中,使控件远离鼠标光标等。 最终的结果是整个设置抖动和抖动,不断调整大小。

    有谁能给我一个办法来抑制这种犹豫不决吗?

    下面提供了完整的示例代码,可以直接粘贴到新项目的Form1中。定位鼠标光标以正确显示问题留给读者作为练习:P

    Public Class Form1
    Private Sub myInitializeComponent()
        Me.components = New System.ComponentModel.Container
        Me.TableLayoutPanel2 = New System.Windows.Forms.TableLayoutPanel
        Me.FollowTableLayoutPanel1 = New FollowTableLayoutPanel
        Me.Button9 = New System.Windows.Forms.Button
        Me.Button8 = New System.Windows.Forms.Button
        Me.Button7 = New System.Windows.Forms.Button
        Me.Button6 = New System.Windows.Forms.Button
        Me.Button5 = New System.Windows.Forms.Button
        Me.Button3 = New System.Windows.Forms.Button
        Me.Button1 = New System.Windows.Forms.Button
        Me.Button2 = New System.Windows.Forms.Button
        Me.Button4 = New System.Windows.Forms.Button
        Me.TableLayoutPanel2.SuspendLayout()
        Me.FollowTableLayoutPanel1.SuspendLayout()
        Me.SuspendLayout()
        '
        'TableLayoutPanel2
        '
        Me.TableLayoutPanel2.ColumnCount = 1
        Me.TableLayoutPanel2.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
        Me.TableLayoutPanel2.Controls.Add(Me.FollowTableLayoutPanel1, 0, 0)
        Me.TableLayoutPanel2.Location = New System.Drawing.Point(12, 115)
        Me.TableLayoutPanel2.Name = "TableLayoutPanel2"
        Me.TableLayoutPanel2.RowCount = 1
        Me.TableLayoutPanel2.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
        Me.TableLayoutPanel2.Size = New System.Drawing.Size(1194, 341)
        Me.TableLayoutPanel2.TabIndex = 2
        '
        'FollowTableLayoutPanel1
        '
        Me.FollowTableLayoutPanel1.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.FollowTableLayoutPanel1.AutoSize = True
        Me.FollowTableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
        Me.FollowTableLayoutPanel1.ColumnCount = 9
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button9, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button8, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button7, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button6, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button5, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button3, 2, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button1, 0, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button2, 1, 0)
        Me.FollowTableLayoutPanel1.Controls.Add(Me.Button4, 3, 0)
        Me.FollowTableLayoutPanel1.Location = New System.Drawing.Point(259, 0)
        Me.FollowTableLayoutPanel1.Margin = New System.Windows.Forms.Padding(0)
        Me.FollowTableLayoutPanel1.Name = "FollowTableLayoutPanel1"
        Me.FollowTableLayoutPanel1.RowCount = 1
        Me.FollowTableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle)
        Me.FollowTableLayoutPanel1.Size = New System.Drawing.Size(675, 50)
        Me.FollowTableLayoutPanel1.TabIndex = 1
        Me.FollowTableLayoutPanel1.Text = "{X=0,Y=0}" & Global.Microsoft.VisualBasic.ChrW(9) & Global.Microsoft.VisualBasic.ChrW(9) & "00:00:00.0090009"
        '
        'Button9
        '
        Me.Button9.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button9.Location = New System.Drawing.Point(225, 0)
        Me.Button9.Margin = New System.Windows.Forms.Padding(0)
        Me.Button9.Name = "Button9"
        Me.Button9.Size = New System.Drawing.Size(75, 50)
        Me.Button9.TabIndex = 5
        Me.Button9.Text = "{Width=75, Height=50}"
        Me.Button9.UseVisualStyleBackColor = True
        '
        'Button8
        '
        Me.Button8.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button8.Location = New System.Drawing.Point(300, 0)
        Me.Button8.Margin = New System.Windows.Forms.Padding(0)
        Me.Button8.Name = "Button8"
        Me.Button8.Size = New System.Drawing.Size(75, 50)
        Me.Button8.TabIndex = 4
        Me.Button8.Text = "{Width=75, Height=50}"
        Me.Button8.UseVisualStyleBackColor = True
        '
        'Button7
        '
        Me.Button7.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button7.Location = New System.Drawing.Point(375, 0)
        Me.Button7.Margin = New System.Windows.Forms.Padding(0)
        Me.Button7.Name = "Button7"
        Me.Button7.Size = New System.Drawing.Size(75, 50)
        Me.Button7.TabIndex = 3
        Me.Button7.Text = "{Width=75, Height=50}"
        Me.Button7.UseVisualStyleBackColor = True
        '
        'Button6
        '
        Me.Button6.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button6.Location = New System.Drawing.Point(0, 0)
        Me.Button6.Margin = New System.Windows.Forms.Padding(0)
        Me.Button6.Name = "Button6"
        Me.Button6.Size = New System.Drawing.Size(75, 50)
        Me.Button6.TabIndex = 2
        Me.Button6.Text = "{Width=75, Height=50}"
        Me.Button6.UseVisualStyleBackColor = True
        '
        'Button5
        '
        Me.Button5.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button5.Location = New System.Drawing.Point(75, 0)
        Me.Button5.Margin = New System.Windows.Forms.Padding(0)
        Me.Button5.Name = "Button5"
        Me.Button5.Size = New System.Drawing.Size(75, 50)
        Me.Button5.TabIndex = 1
        Me.Button5.Text = "{Width=75, Height=50}"
        Me.Button5.UseVisualStyleBackColor = True
        '
        'Button3
        '
        Me.Button3.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button3.Location = New System.Drawing.Point(525, 0)
        Me.Button3.Margin = New System.Windows.Forms.Padding(0)
        Me.Button3.Name = "Button3"
        Me.Button3.Size = New System.Drawing.Size(75, 50)
        Me.Button3.TabIndex = 0
        Me.Button3.Text = "{Width=75, Height=50}"
        Me.Button3.UseVisualStyleBackColor = True
        '
        'Button1
        '
        Me.Button1.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button1.Location = New System.Drawing.Point(150, 0)
        Me.Button1.Margin = New System.Windows.Forms.Padding(0)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(75, 50)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "{Width=75, Height=50}"
        Me.Button1.UseVisualStyleBackColor = True
        '
        'Button2
        '
        Me.Button2.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button2.Location = New System.Drawing.Point(450, 0)
        Me.Button2.Margin = New System.Windows.Forms.Padding(0)
        Me.Button2.Name = "Button2"
        Me.Button2.Size = New System.Drawing.Size(75, 50)
        Me.Button2.TabIndex = 0
        Me.Button2.Text = "{Width=75, Height=50}"
        Me.Button2.UseVisualStyleBackColor = True
        '
        'Button4
        '
        Me.Button4.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.Button4.Location = New System.Drawing.Point(600, 0)
        Me.Button4.Margin = New System.Windows.Forms.Padding(0)
        Me.Button4.Name = "Button4"
        Me.Button4.Size = New System.Drawing.Size(75, 50)
        Me.Button4.TabIndex = 0
        Me.Button4.Text = "{Width=75, Height=50}"
        Me.Button4.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(1218, 577)
        Me.Controls.Add(Me.TableLayoutPanel2)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.TableLayoutPanel2.ResumeLayout(False)
        Me.TableLayoutPanel2.PerformLayout()
        Me.FollowTableLayoutPanel1.ResumeLayout(False)
        Me.ResumeLayout(False)
    
    End Sub
    Friend WithEvents Button1 As System.Windows.Forms.Button
    Friend WithEvents Button2 As System.Windows.Forms.Button
    Friend WithEvents Button3 As System.Windows.Forms.Button
    Friend WithEvents Button4 As System.Windows.Forms.Button
    Friend WithEvents FollowTableLayoutPanel1 As FollowTableLayoutPanel
    Friend WithEvents TableLayoutPanel2 As System.Windows.Forms.TableLayoutPanel
    Friend WithEvents Button9 As System.Windows.Forms.Button
    Friend WithEvents Button8 As System.Windows.Forms.Button
    Friend WithEvents Button7 As System.Windows.Forms.Button
    Friend WithEvents Button6 As System.Windows.Forms.Button
    Friend WithEvents Button5 As System.Windows.Forms.Button
    
    Public Sub New()
        ' This call is required by the Windows Form Designer.
        InitializeComponent()
        myInitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
    End Sub
    
    Private Sub FollowTableLayoutPanel1_TimerTick() Handles FollowTableLayoutPanel1.TimerTick
        Me.Text = Me.FollowTableLayoutPanel1.Text
    End Sub
    
    Private Sub TableLayoutPanel1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles TableLayoutPanel2.MouseMove
        Me.FollowTableLayoutPanel1.parentMouseMove(e.Location)
    End Sub
    Private Sub TableLayoutPanel1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles TableLayoutPanel2.MouseLeave
        Me.FollowTableLayoutPanel1.parentMouseLeave()
    End Sub
    End Class
    Public Class FollowTableLayoutPanel
    Inherits TableLayoutPanel
    Public WithEvents animTimer As Timer
    Private Class ControlSize
        Private _sizing As Boolean
        Private _bigSize As Size
        Public ReadOnly Property BigSize() As Size
            Get
                Return _bigSize
            End Get
        End Property
        Private _smallSize As Size
        Public ReadOnly Property SmallSize() As Size
            Get
                Return _smallSize
            End Get
        End Property
        Private WithEvents _thisControl As Control
        Public ReadOnly Property ThisControl() As Control
            Get
                Return _thisControl
            End Get
        End Property
        Public Sub New(ByVal thisControl As Control)
            Me._sizing = False
            Me._thisControl = thisControl
            Me._bigSize = New Size(thisControl.Width * 2, thisControl.Height * 2)
            Me._smallSize = thisControl.Size
        End Sub
        Public Sub Resize(ByVal sizeTo As Size)
            Me._sizing = True
            Me._thisControl.Size = sizeTo
            Me._sizing = False
        End Sub
        Private Sub _thisControl_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles _thisControl.SizeChanged
            If Not Me._sizing Then
                Me._bigSize = New Size(ThisControl.Width * 2, ThisControl.Height * 2)
                Me._smallSize = ThisControl.Size
            End If
        End Sub
    End Class
    Private sizeDict As List(Of ControlSize)
    Public Sub New()
        MyBase.New()
        Me.sizeDict = New List(Of ControlSize)
        Me.DoubleBuffered = True
        Me.animTimer = New Timer()
        Me.animTimer.Interval = 10
        Me.animTimer.Start()
    End Sub
    Protected Overrides Sub OnControlAdded(ByVal e As System.Windows.Forms.ControlEventArgs)
        MyBase.OnControlAdded(e)
        e.Control.Text = String.Empty
        sizeDict.Add(New ControlSize(e.Control))
        AddHandler e.Control.MouseMove, AddressOf ControlIn_MouseMove
        AddHandler e.Control.MouseLeave, AddressOf ControlIn_MouseLeave
    End Sub
    Protected Overrides Sub OnControlRemoved(ByVal e As System.Windows.Forms.ControlEventArgs)
        MyBase.OnControlRemoved(e)
        For Each controlSizeIn As ControlSize In Me.sizeDict
            If controlSizeIn.ThisControl Is e.Control Then
                sizeDict.Remove(controlSizeIn)
                Exit For
            End If
        Next
        RemoveHandler e.Control.MouseMove, AddressOf ControlIn_MouseMove
        RemoveHandler e.Control.MouseLeave, AddressOf ControlIn_MouseLeave
    End Sub
    Public Event TimerTick()
    Private Sub animTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles animTimer.Tick
        Me.SuspendLayout()
        moveButton()
        Me.PerformLayout()
        Me.ResumeLayout()
        RaiseEvent TimerTick()
    End Sub
    Private Sub moveButton()
        Static lastTime As Long = DateTime.Now.Ticks
        If mouseLocation.X <> 0 Then
            For Each csIn As ControlSize In Me.sizeDict
                Dim controlIn As Control = csIn.ThisControl
                Dim controlCentrePoint As New Point(CInt(controlIn.Left + (controlIn.Width / 2)), CInt(controlIn.Top + (controlIn.Height / 2)))
    
                Dim differenceX As Integer = Math.Abs(controlCentrePoint.X - mouseLocation.X)
                Dim setDifferenceX As Integer = csIn.BigSize.Width - differenceX
                Dim setTargetX As Integer = If(setDifferenceX < csIn.SmallSize.Width, csIn.SmallSize.Width, setDifferenceX)
    
                Dim targetChangeX As Integer = CInt((Math.Abs(setTargetX - controlIn.Width)) / 5)
                Dim setChangeX As Integer = If(targetChangeX = 0, 1, targetChangeX)
                If setTargetX < controlIn.Width AndAlso controlIn.Width <= csIn.BigSize.Width Then
                    Dim setX As Integer = controlIn.Width - setChangeX
                    csIn.Resize(New Size(setX, CInt(csIn.SmallSize.Height * (setX / csIn.SmallSize.Width))))
                ElseIf setTargetX > controlIn.Width AndAlso controlIn.Width >= csIn.SmallSize.Width Then
                    Dim setX As Integer = controlIn.Width + setChangeX
                    csIn.Resize(New Size(setX, CInt(csIn.SmallSize.Height * (setX / csIn.SmallSize.Width))))
                End If
    
                controlIn.Text = controlIn.Size.ToString
            Next
        Else
            For Each csIn As ControlSize In Me.sizeDict
                Dim controlIn As Control = csIn.ThisControl
                If csIn.SmallSize.Width < controlIn.Width AndAlso controlIn.Width <= csIn.BigSize.Width Then
                    Dim targetChangeX As Integer = CInt((Math.Abs(csIn.SmallSize.Width - controlIn.Width)) / 5)
                    Dim setChangeX As Integer = If(targetChangeX = 0, 1, targetChangeX)
                    Dim setX As Integer = controlIn.Width - setChangeX
                    Dim setY As Integer = CInt(csIn.SmallSize.Height * (setX / csIn.SmallSize.Width))
                    csIn.Resize(New Size(setX, setY))
                End If
                controlIn.Text = controlIn.Size.ToString
            Next
        End If
        Dim nowTicks As Long = DateTime.Now.Ticks
        Dim ts As New TimeSpan(nowTicks - lastTime)
        lastTime = nowTicks
        Me.Text = Me.mouseLocation.ToString & vbTab & vbTab & ts.ToString
    End Sub
    Private mouseLocation As Point
    Private Sub Form1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.MouseLeave
        mouseLocation = New Point(0, 0)
    End Sub
    Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        mouseLocation = e.Location
    End Sub
    Private Sub ControlIn_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs)
        mouseLocation = New Point(CInt(e.X + CType(sender, Control).Left), CInt(e.Y + CType(sender, Control).Top))
    End Sub
    Private Sub ControlIn_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs)
        mouseLocation = New Point(0, 0)
    End Sub
    Public Sub parentMouseMove(ByVal mouseCoordinates As Point)
        mouseLocation = New Point(mouseCoordinates.X - Me.Left, mouseCoordinates.Y - Me.Top)
    End Sub
    Public Sub parentMouseLeave()
        mouseLocation = New Point(0, 0)
    End Sub
    End Class
    
    1 回复  |  直到 15 年前
        1
  •  1
  •   AlexEzh    15 年前

    你想实现一个鱼眼效果类似于OSX中的dock吗?不要在每个单独的控件级别上处理鼠标事件,而是尝试在父级处理鼠标。当父控件接收到鼠标移动时,计算所有控件的新位置并进行调整。

    我一直在使用以下方法来计算鱼眼效应的位置。基本上是根据鼠标的位置缩放/移动元素。pt是鼠标的坐标。目标位置是未标度坐标。ScaledPos是新的缩放坐标

    double dx = target.Pos.X + target.Pos.Width / 2 - pt.X;
    double scale = DistanceToScale(Math.Sqrt(dx * dx + dy * dy));
    double xRel = pt.X - target.Pos.X;
    double dxTarget = xRel * scale - xRel;
    
    target.ScaledPos = new Rect(target.Pos.X - dxTarget,
                                target.Pos.Y,
                                target.Pos.Width * scale,
                                target.Pos.Height);
    

    你必须计算所有元素的位置。DistanceToScale函数定义缩放从最大刻度(鼠标位于元素顶部时)到1(项目不缩放)的变化速度。功能应流畅,避免跳跃。

    double DistanceToScale(double distance)
    {
        double dblScale = _c1 / (1.0 + (distance * distance * _c2));
        if (dblScale < 1.0)
        {
            dblScale = 1.0;
        }
    
        return dblScale;
    }
    

    其中c1和c2

    _c1 = maximun_scale;
    _c2 = (c1 / scale_at_distance_dx) - 1 / (dx ^ 2);