Winforms ProgressBar Takes time to render

I noticed that when using PorgressBar. If I set the value of x, the displayed value will not be updated immediately, it will take a small amount of time to draw it, since the line is animated from the current value to the new value.

This is easy to see in the following code:

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 

You will notice that this code works when Value Now at 100% appears long before the panel reaches 100%.

Is there any way to detect when a panel has finished rendering?

+2
source share
7 answers

I just tried this and can see exactly what you mean. Unfortunately, after spending some time seeing that the DrawToBitmap functions in the progress bar can help, I'm a little out of date.

The next step is to create a custom execution bar that provides events to complete the rendering.

For a reasonable example of creating a custom progress bar, try here: http://msdn.microsoft.com/en-us/library/system.windows.forms.progressbarrenderer(v=VS.100).aspx

A quick code scan looks like you should be able to hook up an OnRendered event or the like on calls or call "DrawHorizontalChunks" (or "DrawVerticalChunks") or around them.

This is probably not the answer you need, but at least gives you the control you need if you pursue it?

Note. I have not tried this myself, so please do not send me hateful mail if you spend all day on this to find the same results ...

Good luck

EDIT:

Not happy with my answer, seemed a little lazy ... The following uses a custom progress bar, as I described. It has a couple of basic properties for setting Max / Min values, performing steps, and setting the value directly. I checked this by changing the waiting interval to various amounts, in all cases the form displayed a progress bar as complete until closed. Notice the new OnRendered event.

 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 
+3
source

The problem is that you are working in the same program with the stream, and the stream needs time to update the display.

Add line

 Application.DoEvents() 

Before closing UpdateProgress sub.

And you can get rid of the last two updates.

0
source

This is my code based on Matt Wilko's assumption:

 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 
0
source

I have good results with a progressbar delay setting the value using this function:

 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 

Additional Information:

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

0
source

I have a similar problem. I would rather sue the standard progress bar to have a typical design in the app.

Itโ€™s true that updating takes time, and since DoEvents doesnโ€™t work on its own, I would recommend downloading using a background worker. When done and it still won't work, add extra events or a little delay. But I think that your solution for advertising, a delay of 100 ms will be better, since it requires minimal changes and still works. how about adding 10 ms to it. Another way: try progressBar.Invalidate (force redrawing), add code to draw, which checks if the paint is running, and then close the form. I think DoEvents will not work in paint, since the paint should run out. therefore, you can turn on the timer at intervals of 100 ms and close the window.

about everything that is less than 1 / 24s, invisible to people, it bu..sh .. there are fluids in the eye that see even shorter things. The difference is whether you can immediately respond to something, but since the information is โ€œburningโ€ on the surface of the eyes, until it is โ€œreadโ€ by the nerves, it will not be lost. there are even problems with a frequency of 60 Hz, and I am sure that most people know that this annoys the 60 Hz crt problem. The LCD does not flicker so intensely, but if you need a high frame rate, and 60 is not enough, then you can still โ€œseeโ€ problems with 60, and it looks better with 100 or higher.

-1
source

I found that using the PerformStep () function instead of setting the value did not have this rendering delay - I still had to call Application.DoEvents (), though.

-1
source

I got an acceptable result by inserting frmMain.Refresh
after assigning a new value to the ProgressBar standard.

-2
source

All Articles