Did your mother ever tell you that appearances aren’t everything? Well, she was wrong. When it comes to much of the user experience of software, appearances are everything. Imagine if you have a User Interface (UI) with a progress bar and you have an event that is triggered when the process monitored by the progress bar is completed. Now imagine that when this process is completed and the internal value of the progress bar is set to 100%, that this is not what the progress bar displays. Well, that is exactly what the ProgressBar control of the Windows Forms API does on Windows machines using the Aero theme. As a side note, most editions of Windows Vista/ 7 ship with the Aero theme.
On clients using the Aero theme, there exists a small lag between the actual value and what is displayed in the WinForms ProgressBar control. The reason for this is that Aero animation will cause a progressive transition between values of the ProgressBar to create a visually stunning effect. Usually this is not an issue, but if you have an event that is supposed to be triggered on completion of a process, then this discrepancy can cause bugs/ confusion for users.
As an example, I wrote a simple application that displays the problem at hand:
Notice how the “Start” button is re-enabled slightly before the ProgressBar hits completion. In this simple toy example, the problem does not seem like such a big deal. However, the latency can manifest itself in a much more dramatic and uglier way. I encountered this problem when I was creating a modal window which displayed the progress of a file download. The modal window was supposed to close itself upon completion of the download, but instead it was closing itself with the ProgressBar only displaying about 85%. When I stepped through the code, the Value property of the ProgressBar was at 100 (the Maximum property of the ProgressBar control was set to 100 as well) – so it should have displayed 100%. But why wasn’t it?
I eventually discovered that this discrepancy was not from anything that I was doing, but a side effect of the Aero animation that the Windows OS was running. The workaround was to use the fact that the animation only happens when the ProgressBar is incremented and not when it is decremented. So by moving the value past where you want to set it, then moving it back to where you actually need it, you cut out the progressive animation! Besides handling a border case at the end, it is really that simple to fix.
But how best to implement this? I believe this is a perfect example of when to use an Extension Method in .NET. Just wrap this functionality in an extension method and make your progress updates via this new method instead of assigning the new value directly.
Here is the extension method in all of it’s glory:
public static class ExtensionMethods
/// Sets the progress bar value, without using 'Windows Aero' animation.
/// This is to work around a known WinForms issue where the progress bar
/// is slow to update.
public static void SetProgressNoAnimation(this ProgressBar pb, int value)
// To get around the progressive animation, we need to move the
// progress bar backwards.
if (value == pb.Maximum)
// Special case as value can't be set greater than Maximum.
pb.Maximum = value + 1; // Temporarily Increase Maximum
pb.Value = value + 1; // Move past
pb.Maximum = value; // Reset maximum
pb.Value = value + 1; // Move past
pb.Value = value; // Move to correct value
And then use this new extension method like this (I am using a BackgroundWorker):
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
// This approach will cause some lag due to the progressive animation style
// this.progressBar.Value = e.ProgressPercentage;
// This approach will side step the lag by removing the progressive animation
Now when you update the value of the ProgressBar using the new extension method, you will no longer witness the discrepancy between what is on the “inside” (the value) and what is on the “outside” (the UI display). Despite what your mother may have told you, users of your software will not care about your ProgressBar enough to get to know it like you do.
Here is a snapshot of what the new code produces when it runs: