You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

VB.NET中Panel内PictureBox缩小时滚动条跳动问题及解决

解决Panel嵌套PictureBox缩小时滚动条跳动卡顿问题

场景复现

  • Panel嵌套PictureBox,Panel的AutoScroll=True,PictureBox的SizeMode=StretchImage
  • 通过鼠标滚轮实现图像缩放,放大效果流畅符合预期,但缩小时滚动条大幅跳动、卡顿
  • 滚动条仅用于图像平移,不参与缩放逻辑

已尝试无效方案

  • 调整尺寸前后保存/恢复滚动条设置(在PictureBox的SizeChanged事件及尺寸调整后操作)
  • 使用SuspendLayout()ResumeLayout()
  • 保存并恢复AutoScrollPosition

问题根源分析

.NET不会维护Panel的AutoScrollPosition

  • 图像尺寸变化时,AutoScrollPosition的X/Y值可能下溢变为负数
  • 滚动条最大值自动变更,但Value值保持不变
  • 缩小时滚动条Value与最大值的比例失衡,导致图像跳动,直到滚动条消失
  • 允许图像缩小至原始尺寸以下时,显示会恢复流畅

解决方案

通过修改MouseWheel事件和Panel.Paint事件修复,以下是完整代码:

原MouseWheel事件(存在问题)

Private Sub Form_MouseWheel(sender As System.Object, e As MouseEventArgs) Handles MS_Main.MouseWheel

    Dim eDelta As Integer = e.Delta

    _scaleDelta = Math.Sqrt(Me.pbInv_ShopArtsAndCrafts.Width *
                            Me.pbInv_ShopArtsAndCrafts.Height) *
                            _OriginalScaleDelta

    If eDelta < 0 Then
        If Me.pnlMTC_MagnifyPictureBox.VerticalScroll.Visible Then
            _scale -= _scaleDelta
        Else
            eDelta = 0
        End If

    ElseIf eDelta > 0 Then

        _scale += _scaleDelta

    End If

    If eDelta <> 0 Then
        pbInv_ShopArtsAndCrafts.Size = New Size(CInt(Math.Round(_originalSize.Width * _scale)),
                                                CInt(Math.Round(_originalSize.Height * _scale)))
    End If

End Sub

更新后的MouseWheel事件

Private Sub Form_MouseWheel(sender As System.Object, e As MouseEventArgs) Handles MS_Main.MouseWheel

    Me.pbInv_ShopArtsAndCrafts.MinimumSize = Me.pnlMTC_MagnifyPictureBox.MinimumSize

    Dim HoldInitialized As Boolean = m_Initialized

    Dim eDelta As Integer = e.Delta

    If m_Initialized Then

        m_Initialized = False

        Dim PreY As Integer = Math.Abs(Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.Y)
        Dim PreX As Integer = Math.Abs(Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.X)

        _scaleDelta = Math.Sqrt(Me.pbInv_ShopArtsAndCrafts.Width *
                                Me.pbInv_ShopArtsAndCrafts.Height) *
                                _OriginalScaleDelta

        If eDelta < 0 Then
            If Me.pbInv_ShopArtsAndCrafts.Size.Height <= _originalSize.Height OrElse
               Me.pbInv_ShopArtsAndCrafts.Image.Size.Width <= _originalSize.Width Then

                m_Initialized = HoldInitialized

                Exit Sub
            End If

            _scale -= _scaleDelta

        ElseIf eDelta > 0 Then

            _scale += _scaleDelta

        End If

        If eDelta <> 0 Then

            Me.pnlMTC_MagnifyPictureBox.SuspendLayout()
            Me.pbInv_ShopArtsAndCrafts.SuspendLayout()

            Me.pbInv_ShopArtsAndCrafts.Size = New Size(CInt(Math.Round(_originalSize.Width * _scale)),
                                                       CInt(Math.Round(_originalSize.Height * _scale)))

            If eDelta < 0 Then
                Dim nYVal As Integer = 0
                Dim nXVal As Integer = 0

                'If Math.Abs(Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.X) > 0 Then
                '   nXVal = PreX - (PreX / Math.Abs(Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.X)) 
                'End If

                If Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.Y > 0 Then

                    nYVal = PreY - (PreY / Math.Abs(Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition.Y))

                End If

                WaitMilliseconds(25)

                Me.pnlMTC_MagnifyPictureBox.AutoScrollPosition = New Point(nXVal, nYVal)

            End If

        End If

        Me.pbInv_ShopArtsAndCrafts.ResumeLayout()
        Me.pnlMTC_MagnifyPictureBox.ResumeLayout()

    End If

    m_Initialized = HoldInitialized

End Sub

Panel的Paint事件

Private Sub pnlMTC_MagnifyPictureBox_Paint(sender As Object, e As PaintEventArgs) Handles pnlMTC_MagnifyPictureBox.Paint

    pnlMTC_MagnifyPictureBox.SuspendLayout()

    If Not MouseButtons.HasFlag(MouseButtons.Left) Then
        If pnlMTC_MagnifyPictureBox.VerticalScroll.Value > 0 Then

            pnlMTC_MagnifyPictureBox.VerticalScroll.Value = 0

        End If
    End If

    pnlMTC_MagnifyPictureBox.ResumeLayout()

End Sub

方案说明

  • 限制图像缩小至原始尺寸即停止,避免触发滚动条比例失衡问题
  • 缩小时手动调整AutoScrollPosition,修正滚动条位置
  • Paint事件中在非拖动状态下重置滚动条Value,避免残留异常位置
  • 仅存在轻微闪烁,可满足业务需求

内容的提问来源于stack exchange,提问作者E Bell

火山引擎 最新活动