Winforms ProgressBar需要时间渲染

时间:2011-04-19 09:18:01

标签: .net vb.net visual-studio-2010 .net-4.0 progress-bar

我在使用PorgressBar时已经注意到了。如果我将值设置为x,则显示的值不会立即更新,因为条形图从其当前值设置为新值,所以需要花费少量时间来绘制它。

在以下代码中很容易看到:

Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Label1.Text = ""
    Dim progressHandler = New Progress(Of Integer)(Sub(value) ProgressBar1.Value = value)
    Dim progress = CType(progressHandler, IProgress(Of Integer))
    Await Task.Run(Sub()
                       For i = 1 To 100
                           progress.Report(i)
                           Thread.Sleep(10)
                       Next
                   End Sub)
    Label1.Text = "Value Now at 100%"
    Await Task.Delay(650) 'it takes this long for the bar to be rendered
    Label1.Text += " - Finished drawing"
End Sub

您会注意到运行此代码,Value Now at 100%在条形实际达到100%之前出现了很长时间。

当条形图完成渲染后,我有什么方法可以检测到吗?

7 个答案:

答案 0 :(得分:3)

我刚试过这个,可以确切地看出你的意思。不幸的是,花了一点时间看看进度条上的DrawToBitmap功能是否有帮助,我已经做空了。

下一步是创建一个自定义进度条,用于在渲染完成时公开事件。

有关如何创建自定义进度条的合理示例,请尝试此处: http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbarrenderer(v=VS.100).aspx

对代码的快速扫描看起来应该能够在“DrawHorizo​​ntalChunks”(或“DrawVerticalChunks”)调用的周围插入“OnRendered”事件或类似内容。

可能不是你追求的答案,但如果你追求它,至少可以给你控制吗?

注意:我自己没有尝试过,所以如果你整天花在这上面,请不要给我发送仇恨邮件,以便找到相同的结果......

祝你好运!

编辑:

对我的回答不满意,似乎有点懒惰......下面使用我描述的自定义进度条。它有几个基本属性,用于设置最大/最小值,执行步骤和直接设置值。我已经通过将睡眠间隔更改为不同的量来测试这一点,在所有情况下,表单在关闭之前将进度条显示为已满。请注意新的OnRendered事件。

Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.VisualStyles

Public Class Form1
    Inherits Form
    Private WithEvents bar1 As ProgressBarWithRender = New ProgressBarWithRender()


    Public Sub New()
        InitializeComponent()
        Me.Size = New Size(500, 500)
        bar1.Location = New Point(100, 100)
        bar1.Width = 300
        bar1.Height = 50
        bar1.Maximum = 30
        bar1.Step = 1
        Controls.Add(bar1)
    End Sub

    Public Sub OnRendered(ByVal valueRendered As Integer) Handles bar1.OnRendered
        If valueRendered = bar1.Maximum Then
            ' We know everything has been drawn
            Me.Close()
        End If
    End Sub


    <STAThread()> _
    Public Shared Sub Main()
        ' The call to EnableVisualStyles below does not affect
        ' whether ProgressBarRenderer.IsSupported is true; as 
        ' long as visual styles are enabled by the operating system, 
        ' IsSupported is true.
        Application.EnableVisualStyles()
        Application.Run(New Form1())

    End Sub 'Main

    Private Sub Form1_Click(sender As Object, e As System.EventArgs) Handles Me.Click
        For i = 1 To 30
            bar1.PerformStep()
            Threading.Thread.Sleep(10)
        Next
    End Sub

End Class 'Form1

Public Class ProgressBarWithRender
    Inherits Control

    Public Delegate Sub RenderedEventArgs(ByVal valueRendered As Integer)
    Public Event OnRendered As RenderedEventArgs

    Private ProgressBarRectangles() As Rectangle

    Public Property [Step] As Integer

    Public Property InnerPadding As Integer = 3

    Private _Maximum As Integer
    Public Property Maximum As Integer
        Get
            Return _Maximum
        End Get
        Set(value As Integer)
            _Maximum = value
            CalculateTickSizes()
        End Set
    End Property

    Private _Minimum As Integer
    Public Property Minimum As Integer
        Get
            Return _Minimum
        End Get
        Set(value As Integer)
            _Minimum = value
            CalculateTickSizes()
        End Set
    End Property

    Private _Value As Integer
    Public Property Value As Integer
        Get
            Return _Value
        End Get
        Set(newValue As Integer)
            If newValue < Me.Value AndAlso newValue > 0 Then
                Throw New NotImplementedException("ProgressBarWithRender does not support decrementing the value")
            End If
            Me._Value = newValue
        End Set
    End Property

    Public Sub PerformStep()
        ' Ensure step doesn't exceed boundaries
        If Value + [Step] > Maximum Then
            Value = Maximum
        ElseIf Value + [Step] < Minimum Then
            Value = Minimum
        Else
            Value += [Step]
        End If

        ' We are limited by the Renderers Chunk Width, so we possibly can't draw every step if there is a high maximum
        Dim g As Graphics = Me.CreateGraphics
        ProgressBarRenderer.DrawHorizontalChunks(g, ProgressBarRectangles(Value - Minimum))
        RaiseEvent OnRendered(Value)

    End Sub

    Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
        If Not ProgressBarRenderer.IsSupported Then
            Throw New NotImplementedException("Progress Bar Rendering is not supported")
        End If
        ProgressBarRenderer.DrawHorizontalBar(e.Graphics, ClientRectangle)
    End Sub

    Private Sub CalculateTickSizes()
        ' Changing the Maximum will change the tick rectangle size
        ProgressBarRectangles = New Rectangle(Maximum) {}
        Dim chunkThickness As Integer = ProgressBarRenderer.ChunkThickness + (ProgressBarRenderer.ChunkSpaceThickness * 2)
        Dim tickThickness As Double = ((ClientRectangle.Width - (InnerPadding * 2)) - (ProgressBarRenderer.ChunkSpaceThickness * 2)) / (Maximum - Minimum)
        If tickThickness < chunkThickness Then
            Debug.Print("This will go wrong because we can't draw small enough chunks...")
        End If
        For i As Integer = 0 To Maximum
            Dim filledRectangle As Integer = CInt(tickThickness * i)
            ProgressBarRectangles(i) = New Rectangle(ClientRectangle.X + InnerPadding,
                                                     ClientRectangle.Y + InnerPadding,
                                                     filledRectangle,
                                                     ClientRectangle.Height - (InnerPadding * 2))
        Next
    End Sub

End Class

答案 1 :(得分:0)

问题是你在一个单线程程序中,线程需要时间来更新显示。

添加行

Application.DoEvents()

关闭UpdateProgress子。

之前

你可以摆脱最后两次刷新。

答案 2 :(得分:0)

这是我的代码基于Matt Wilko的建议:

Imports System.Net
Imports System.IO
Imports System.Text.RegularExpressions
Public Class Form1
Dim client As New WebClient
Public Sub New()

    ' This call is required by the Windows Form Designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    AddHandler client.DownloadStringCompleted, AddressOf client_DownloadStringCompleted
    AddHandler client.DownloadProgressChanged, AddressOf client_DownloadProgressChanged

End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    ProgressBar1.Value = 0
    ProgressBar1.Visible = True
    client.DownloadStringAsync(New Uri("http://somewebsite.com"), Nothing)
End Sub
Private Sub client_DownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs)
    If ProgressBar1.Value < e.ProgressPercentage Then
        ProgressBar1.Value = e.ProgressPercentage
    End If
End Sub
Private Sub client_DownloadStringCompleted(ByVal sender As Object, ByVal e As System.Net.DownloadStringCompletedEventArgs)
    ProgressBar1.Value = 100
    Timer1.Enabled = True
End Sub

Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
    Static waitToCloseProgressBar As Integer
    If ProgressBar1.Value = 100 Then
        If waitToCloseProgressBar > 6 Then
            Timer1.Enabled = False
            waitToCloseProgressBar = 0
            ProgressBar1.Visible = False
        Else
            waitToCloseProgressBar = waitToCloseProgressBar + 1
        End If
    End If
End Sub
End Class

答案 3 :(得分:0)

我使用进度条滞后效果很好,使用此函数设置值:

Private Sub SetProgressNoAnimation(ByVal value As Integer)
    ' To get around the progressive animation, we need to move the 
    ' progress bar backwards.
    If (value = progressBarCarga.Maximum) Then
        ' Special case as value can't be set greater than Maximum.
        progressBarCarga.Maximum = (value + 1)
        ' Temporarily Increase Maximum
        progressBarCarga.Value = (value + 1)
        ' Move past
        progressBarCarga.Maximum = value
        ' Reset maximum
    Else
        progressBarCarga.Value = (value + 1)
        ' Move past
    End If
    progressBarCarga.Value = value
    ' Move to correct value
End Sub

更多信息:

https://derekwill.com/2014/06/24/combating-the-lag-of-the-winforms-progressbar/

答案 4 :(得分:-1)

我有类似的问题。 我更愿意起诉标准进度条,以便在应用程序中使用典型设计。

确实需要时间来更新,而且由于DoEvents不能独立工作,我建议使用后台工作程序进行加载。完成后它仍然不起作用,然后添加一个doevents或一个小延迟。但我想你的广告延迟100毫秒的解决方案将是最好的,因为它需要最少的更改并且仍在工作。怎么样只是增加10毫秒的延迟。 另一种方法是:尝试progressBar.Invalidate(强制重绘),添加绘制代码,检查是否执行绘制,然后关闭表单。我想DoEvents不会在油漆中起作用,因为油漆需要完成。所以你可以用100毫秒间隔启用计时器,它会关闭窗口。

关于所有少于1 / 24s对于人类来说是不可见的是bu..sh .. 眼睛里有液体,甚至可以看到短暂的东西。如果我们可以立即对某些东西作出反应,那是不同的,但由于信息在眼睛表面被“烧掉”,直到它被神经“读取”,它不会丢失。甚至有60Hz的问题,我相信大多数人都知道烦人的60Hz crt问题。 LCD没有那么强烈的闪烁,但是如果你需要高帧率,而60还不够,那么你仍然可以“看到”60的问题,看起来效果更好,100或更高。

答案 5 :(得分:-1)

我发现使用PerformStep()而不是设置值没有这个渲染延迟 - 但仍然必须调用Application.DoEvents()。

答案 6 :(得分:-2)

我通过插入获得了可接受的结果 frmMain.Refresh
在为标准ProgressBar分配新值之后。