Quantcast
Channel: Planet Object Pascal
Viewing all articles
Browse latest Browse all 1725

Žarko Gajić: TProgressBar Not Updating Fast Enough?

$
0
0

progressbar-slow-update
Do users of your Delphi application actually see the progress bar moving?

The TProgressBar control provides visual feedback about the progress of some actions within your application. The Position, Max and Min properties determine the current position of the progress bar within its minimum and maximum values.

In most processing situations of some kind, where the progress bar is used, I have code that looks like:

ProgressBar1.Max := NUMBER_OF_ELEMENTS;
try
  for i  := 1 to NUMBER_OF_ELEMENTS do
  begin
    begin
     // do some operation
    end;

    //update progress bar
    ProgressBar1.StepIt; //if Step = 1
    //OR
    ProgressBar1.Position := i;
  end;
finally
  ProgressBar1.Position := 0;
end;

I guess the above way of using a progress bar is also what you have in your application. Set the Max value, run some loop, update the progress bar position to notify the user of the progress of the loop.

Note that after the operation is finished the progress bar position is set to zero, to make it ready for the next operation requiring visual feedback where the same progress bar control is used. In fact, I have a single progress bar positioned in the status bar of the main form of my application – this single progress bar is used for all progress-type actions.

Now, would you expect, using the above code, to actually see the progress bar progressing?

The answer to that questions depends on two other questions: how fast will the loop finish and on what version of Windows (themed or not) the application is being run!

The TProgressBar as implemented in Delphi (as the case is with most other controls) is a direct implementation of the Windows common control – and therefore Windows (the operating system) is responsible for painting it.

Progress Animation on Vista, Windows 7, 8 ….

If your application is run under Windows Vista or Windows 7 (and later) with Aero enabled, the operating system draws the progress bar in a way that it smoothly scrolls from the previous position to the new position. Since this painting takes time, it can take more time then needed in your code to step to the next position.

As a result, and depending on the time needed to process your loop, you might notice that the progress bar is sometimes not painted at all, sometimes it will go to 30%, sometimes to 80% (whatever number or position).

Here’s a real code example you can use to see this (should be running on Windows 7, 8 or Vista with Aero):

procedure TProgressForm.Button1Click(Sender: TObject);
var
  dirFiles : TStringDynArray;
  aFile : string;
begin
  dirFiles := TDirectory.GetFiles('c:\SomeFolderWithPasFiles');

  ProgressBar1.Max := Length(dirFiles);
  ProgressBar1.Position := 0;

  for aFile in dirFiles do
  begin
    if TPath.MatchesPattern(ExtractFileName(aFile),'*.pas',false) then
    begin
      //do something with .PAS files that could take time
    end;

    ListBox1.Items.Add(aFile);

    ProgressBar1.Position := 1 + ProgressBar1.Position;
  end;

  ProgressBar1.Position := 0;
end;

The code loops through all the files in a specified folder and places their names in a list box, a progress bar is used for the visual feedback. If a file is a .PAS file some additional action will be done (whatever). Depending on the number of files and the number of .PAS files (and what you would do to them) it might appear that the progress bar is not being painted at all or it will reach some random position before being reset to position zero (initial position).

Similarly, if the progress bar is at 0% and you set it to 75% then 100% (very fast) – the progress bar does not “jump” to that position – it “slowly” and smoothly fills in its area – possibly too slow for the purpose!.

ProgressBar.ProperlyPaint!

Now, enough with the intro to the problem. The thing is that the way to solve the painting problem is rather simple and involves a trick.

When you set the progress position backwards the (slow) animation does not take place, rather the progress bar jumps quickly to that position.

Having this in mind, I have a simple procedure “ProgressBarStepItOne” implemented as:

procedure TProgressForm.ProgressBarStepItOne;
begin
  ProgressBar1.StepBy(1);
  ProgressBar1.StepBy(-1);
  ProgressBar1.StepBy(1);

  //same as
  (*
    ProgressBar1.Position := 1 + ProgressBar1.Position;
    ProgressBar1.Position := -1 + ProgressBar1.Position;
    ProgressBar1.Position := 1 + ProgressBar1.Position;
  *)
end;

And my code looking as below, ensures the visibility of the progress bar actually moving even when the time to process something is less than it would be needed for Windows to paint (animate) the progress bar moving from 0 to 100!

procedure TProgressForm.Button1Click(Sender: TObject);
var
  dirFiles : TStringDynArray;
  aFile : string;
begin
  dirFiles := TDirectory.GetFiles('C:\SomeFolderWithPasFiles');

  ProgressBar1.Max := Length(dirFiles);
  ProgressBar1.Position := 0;

  for aFile in dirFiles do
  begin
    if TPath.MatchesPattern(ExtractFileName(aFile),'*.pas',false) then
    begin
      //do something with .PAS files that could take time
    end;

    ListBox1.Items.Add(aFile);

    ProgressBarStepItOne;
  end;

  ProgressBar1.Position := 0;
end;

If you use the TProgressBar’s StepIt procedure to advance the Position property with the current value of M by some value > 1, let’s say N , make sure you then set it to M+N-1 and back to M+N.

Now, is that a trick you would never think of or?


Viewing all articles
Browse latest Browse all 1725

Trending Articles