使用PictureBox时面板闪烁问题

3
我需要一种避免面板闪烁的方法,我在这里和全世界各地向专家提出了许多类似的问题,尝试了许多技巧,例如扩展面板、创建参数、控制样式等,浪费了很多时间,学习了无用的东西...我已经陷入了数月的困境。
...所有这些技巧都没有达到预期效果,我发现的最好的“减少闪烁”的方法是“Createparams”重写子程序,但是该方法会使任何窗体/应用程序的操作变慢20倍,如果这意味着失去应用程序性能的话,我不想失去闪烁(至少不想以CreateParams相同的负面性能为代价)。
在这个视频中,您可以看到我的测试表单,其中有一个50%透明的面板,在其中有一些设置为“缩放”层的背景图像的图片框,当我向上或向下滚动时,会出现很多闪烁。 http://www.youtube.com/watch?v=zIBDTMjrDd4&feature=youtu.be 我使用了“CreateParams”方法,如果不使用“CreateParams”,你将看不到我的面板闪烁的情况,真的很可怕。
这是没有闪烁的面板: enter image description here 而这是面板出现闪烁的时候: enter image description here 这是完整的类:
It is a Windows Form proyect
VS2012
Framework 3.5
On Windows 7 x64
Application Visual Styles is ON
Double Buffer is ON
Panel and pictureboxes are default controls

我想不需要说,我已经尝试了所有可能的视觉和环境配置,人们告诉我永远忘记闪烁问题。

Public Class Form1

    Dim Scroll_Position As Int32 = 0
    Dim Button_Down_Is_Pressed As Boolean = False
    Dim Button_Up_Is_Pressed As Boolean = False
    Dim WithEvents Progressive_Scroll_Timer As New Timer
    Dim SmallChange As Int32 = 5
    Dim Largechange As Int32 = 10

    ' Sub which reduces the Flickering, but this sub makes x20 times slower any operation of any Form/Application.
    Protected Overrides ReadOnly Property CreateParams() As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H2000000
            Return cp
        End Get
    End Property 'CreateParams

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Me.BackColor = Color.FromArgb(255, 0, 0, 0)
        ' Me.TransparencyKey = Color.FromArgb(255, 0, 0, 0)
        Panel1.VerticalScroll.Maximum = 999999999
        Progressive_Scroll_Timer.Interval = 50
        Panel1.BackColor = Color.FromArgb(150, 0, 0, 0)
    End Sub

    Private Sub Panel_MouseHover(sender As Object, e As EventArgs) Handles Panel1.MouseHover
        sender.focus()
    End Sub

    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Progressive_Scroll_Timer.Tick
        If Button_Down_Is_Pressed Then
            Scroll_Down(SmallChange)
        ElseIf Button_Up_Is_Pressed Then
            Scroll_Up(SmallChange)
        Else
            sender.stop()
        End If
    End Sub

    Private Sub Scroll_Up(ByVal Change As Int32)
        Scroll_Position -= Change
        Panel1.SuspendLayout()
        Try : Panel1.VerticalScroll.Value = Scroll_Position : Catch : Scroll_Position += Change : End Try
        Panel1.ResumeLayout()
    End Sub

    Private Sub Scroll_Down(ByVal Change As Int32)
        Scroll_Position += Change
        Try : Panel1.VerticalScroll.Value = Scroll_Position : Catch : Scroll_Position -= Change : End Try
    End Sub

    Private Sub Button_Down_MouseDown(sender As Object, e As MouseEventArgs) Handles Button2.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            Button_Down_Is_Pressed = True
            Progressive_Scroll_Timer.Start()
        End If
    End Sub

    Private Sub Button_Up_MouseDown(sender As Object, e As MouseEventArgs) Handles Button1.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            Button_Up_Is_Pressed = True
            Progressive_Scroll_Timer.Start()
        End If
    End Sub

    Private Sub Button_Down_MouseUp(sender As Object, e As MouseEventArgs) Handles Button2.MouseUp
        Button_Down_Is_Pressed = False
    End Sub

    Private Sub Button_Up_MouseUp(sender As Object, e As MouseEventArgs) Handles Button1.MouseUp
        Button_Up_Is_Pressed = False
    End Sub

    Private Sub Form_MouseWheel(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Panel1.MouseWheel
        If Panel1.Focused Then
            Select Case Math.Sign(e.Delta)
                Case Is > 0 : Scroll_Up(Largechange)
                Case Is < 0 : Scroll_Down(Largechange)
            End Select
        End If
    End Sub

End Class

请上传您在YouTube上展示的演示解决方案的完整演示版本。 - Ark-kun
您提供的代码缺少重要部分。 - Ark-kun
@Ark-kun,我已经发布了完整的类,解决方案中没有更多的代码(只有图像资源和其他内容),无论如何,这是完整的解决方案:http://elektrostudios.tk/WindowsApplication4.rar 感谢您的评论。 - ElektroStudios
@Oliver,这是WS_CLIPCHILDREN窗口样式常量。https://msdn.microsoft.com/en-us/library/windows/desktop/ms632600%28v=vs.85%29.aspx - ElektroStudios
4个回答

5

PanelAutoScroll属性设置为true。如果不这样做,滚动将无法正常工作。 添加DoubleBufferedPanel类,该类继承自Panel类并将.DoubleBuffered属性设置为true:

Public Class DoubleBufferedPanel
    Inherits Panel

    Public Sub New()
        DoubleBuffered = True
    End Sub
End Class

现在前往隐藏的InitializeComponent子过程(右键单击Panel1变量并单击转到定义)。在需要的地方(两个位置)将Panel类型替换为DoubleBufferedPanel
Me.Panel1 = New WindowsApplication4.DoubleBufferedPanel()
....
Friend WithEvents Panel1 As WindowsApplication4.DoubleBufferedPanel

闪烁的问题应该会停止(尽管仍然存在其他一些效果)。去掉CreateParams以提高速度。
另外,总的来说,这不是一个很好的想法(移动复杂的半透明图片)。为什么不使用像ListView这样的东西?为什么不自己移动图片而不使用Panel?如果您想要最快的速度,只需使用BitmapGraphics 类在您的表单上绘制图像(.BackgroundImage)即可。
此外,似乎有一些严重的问题与编程方式滚动 Panel.AutoScroll = true。我不得不两次分配滚动值,以防止严重的抖动。我已经隔离了这个情况,并将提交一个缺陷报告给微软。

谢谢,但这对我来说不是可行的解决方案,就像我一开始说的那样,我尝试了很多“双缓冲面板”和其他“技巧”,但实际上并没有什么作用。使用createparams只是一个例子,我想展示一下,但对我来说,createparams的使用无效,因为它会导致应用程序的性能下降(我在几个月内进行了很多速度测试)。 - ElektroStudios
当然我已经尝试过,使用和不使用createparams,不使用createparams时闪烁非常恐怖,也许你没有像我一样在相同的情况下进行测试(面板上有pictureboxes,每个picturebox内都有一个背景图像)。 - ElektroStudios
我已经测试了你上传的项目。我首先做的是删除CreateParams。面板闪烁了很多,就像你的截图一样。然后我编写了DoubleBufferedPanel,并在InitializeComponent中替换了Panel。闪烁完全停止了。虽然有一些“果冻效应”(PictureBox的透明部分移动,保留下来的底层背景),但它不像闪烁那样严重,因为没有白色或灰色——只有稍微移位的绿色背景。 - Ark-kun
那么也许我在按照你的步骤操作时做错了什么,你能上传这个项目吗? - ElektroStudios
我猜我漏说了我将 Panel.AutoScroll 设置为 true。还有一个问题是鼠标滚轮滚动(它不受 .LargeChange 的限制)。我也解决了这个问题。无论如何,通过编程方式滚动“面板”似乎存在一些严重的错误。我已经找到了解决方法并更新了我的答案。这是完整的无闪烁项目:https://skydrive.live.com/redir?resid=E4ECA4072D08CEE5!2873&authkey=!AGhzXoxqSt7eFPs 。有一些抖动,但我认为你不能仅使用常规控件来修复它,除非你自己绘制所有内容。 - Ark-kun
显示剩余4条评论

3

使用一个复杂的 class 来表示 panel 以及它的内容,然后使用 GDI+ 将其绘制到 Picturebox 上会呈现出很好的效果。我已经完成了几个类似的项目。


谢谢你的回答,但是你能详细解释一下你所说的所有事情吗?实际上,我在VBNET编程方面并不是很高级,如果没有示例,我无法完成所有这些操作,但是你说过“我已经完成了几个类似的项目”,也许你可以从其中一个项目中发布代码示例?无论如何,再次感谢。 - ElektroStudios
一个只是另一个对象,这一次你定义它的属性。然后,在你的情况下,你将需要该类的集合 - List(Of T)。在你的窗体中,你可以在绘制事件中迭代集合。至于滚动部分,你甚至可以检查你的类对象的Location.y,如果太高或太低就不绘制它。矩形是用于绘制边框或包裹图像的很好的结构。当使用GDI+时,我甚至会绘制滚动条。很难在一个帖子中告诉你所有你需要的东西。 - OneFineDay

3

您可以将表单的DoubleBuffered属性设置为True

编辑:

我认为您不需要计时器。 因此,您修改后的代码可能如下所示:

Public Class Form1

    Dim Scroll_Position As Int32 = 0
    Dim SmallChange As Int32 = 5
    Dim Largechange As Int32 = 10

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Me.BackColor = Color.FromArgb(255, 0, 0, 0)
        ' Me.TransparencyKey = Color.FromArgb(255, 0, 0, 0)
        Panel1.VerticalScroll.Maximum = 999999999
        Panel1.BackColor = Color.FromArgb(150, 0, 0, 0)
    End Sub

    Private Sub Panel_MouseHover(sender As Object, e As EventArgs) Handles Panel1.MouseHover
        sender.focus()
    End Sub

    Private Sub Scroll_Up(ByVal Change As Int32)
        Try
            Scroll_Position -= Change
            Panel1.VerticalScroll.Value = Scroll_Position 
        End Try
    End Sub

    Private Sub Scroll_Down(ByVal Change As Int32)
        Try
            Scroll_Position += Change
            Panel1.VerticalScroll.Value = Scroll_Position 
        End Try
    End Sub

    Private Sub Button_Down_MouseDown(sender As Object, e As MouseEventArgs) Handles Button2.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            scrollDown(smallChange)
        End If
    End Sub

    Private Sub Button_Up_MouseDown(sender As Object, e As MouseEventArgs) Handles Button1.MouseDown
        If e.Button = Windows.Forms.MouseButtons.Left Then
            scrollUp(SmallChange)
        End If
    End Sub

    Private Sub Form_MouseWheel(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Panel1.MouseWheel
        If Panel1.Focused Then
            Select Case Math.Sign(e.Delta)
                Case Is > 0 : Scroll_Up(Largechange)
                Case Is < 0 : Scroll_Down(Largechange)
            End Select
        End If
    End Sub

End Class

这可能不是解决方案,但这是我尽力而为的努力。


谢谢,但它充满了错误/异常,你尝试编辑后再使用吗?无论如何还是谢谢。 - ElektroStudios
对不起,我忘记删除一些行。现在应该可以工作了。而且这段代码没有经过测试。尝试清理错误,如果您有其他问题,请随时问。 - Vishal

1
将此代码放在您的表单加载事件中。
' to remove flick
Dim aProp As PropertyInfo = GetType(Panel).GetProperty("DoubleBuffered", BindingFlags.NonPublic Or BindingFlags.Instance)
        aProp.SetValue(Panel7, True, Nothing)

将Panel7替换为您带有背景图像的控件的名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接