Jump to content
stacker_liew

Does ProgressBar Inside StatusBar Still Working?

Recommended Posts

2 hours ago, stacker_liew said:

Like this Link

 

I tried in Windows 10, using D10.4, it has no effect.

If you used the button OnClick handler from the link you gave it will not work unless uncomment the Application.ProcessMesssages call.

Share this post


Link to post
41 minutes ago, PeterBelow said:

If you used the button OnClick handler from the link you gave it will not work unless uncomment the Application.ProcessMesssages call.

I tried with Application.ProcessMessage still no effect for ProgressBar.

Share this post


Link to post
42 minutes ago, PeterBelow said:

it will not work unless uncomment the Application.ProcessMesssages call

Or, at the very least, calling Update() on the TProgressBar or TForm.  Or, the example shown, by simply using a timer or thread instead of a blocking loop, let the main message queue run unblocked and handle UI updates normally.

 

That being said, TStatusBar is not really intended to host child controls.  And using a *drawing* event to update UI layout is a big no-no in general.

 

Any time I've ever needed to show a progress bar inside of a status bar (or, any other non-container parent, such as TListView or TTreeView, etc), I always prefer to owner-draw the parent and actually draw a progress bar myself.  I never try to hack in a TProgressBar as a child control.  Especially in a parent that can be resized/scrolled at runtime.  Keeping a TProgressBar positioned correctly at all times can get ugly quick.

Edited by Remy Lebeau

Share this post


Link to post
1 hour ago, Remy Lebeau said:

Or, at the very least, calling Update() on the TProgressBar or TForm.  Or, the example shown, by simply using a timer or thread instead of a blocking loop, let the main message queue run unblocked and handle UI updates normally.

 

That being said, TStatusBar is not really intended to host child controls.  And using a *drawing* event to update UI layout is a big no-no in general.

 

Any time I've ever needed to show a progress bar inside of a status bar (or, any other non-container parent, such as TListView or TTreeView, etc), I always prefer to owner-draw the parent and actually draw a progress bar myself.  I never try to hack in a TProgressBar as a child control.  Especially in a parent that can be resized/scrolled at runtime.  Keeping a TProgressBar positioned correctly at all times can get ugly quick.

Any example of these?

Share this post


Link to post

I create my progress bars in the first panel of the status bar, with a label on it to show some meaningful information on the progress... like "30 of 999 items processed". Yes, it won't work if you resize the panel, needs some adjustments if you want to create it in the 3rd, but this is the code I use:

Unit uProgressBarInStatusBar;

Interface

Uses Vcl.ComCtrls, Vcl.StdCtrls;

Type
 TProgressBarInStatusBar = Class
 public
  Class Procedure CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
 End;

Implementation

Uses Vcl.Controls, System.Classes;

Class Procedure TProgressBarInStatusBar.CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
Var
 statusbar: TStatusBar;
Begin
 statusbar := inStatusBarPanel.Collection.Owner As TStatusBar;

 outProgressBar := TProgressBar.Create(statusbar);
 outProgressBar.Parent := statusbar;
 outProgressBar.Top := 2;
 outProgressBar.Left := 1;
 outProgressBar.Width := inStatusBarPanel.Width - 3;
 outProgressBar.Height := statusbar.ClientHeight - 3;

 outLabel := TLabel.Create(outProgressBar);
 outLabel.Parent := outProgressBar;
 outLabel.Align := alClient;
 outLabel.AutoSize := False;
 outLabel.Alignment := taCenter;
End;

End.

The small sacrifice of having it in the first panel makes up to it with no custom drawings / hacks needed at all. And it looks good enough:

 

image.png.a052b18560be4c685eedc70179a3f2ae.png

Share this post


Link to post
3 hours ago, aehimself said:

I create my progress bars in the first panel of the status bar, with a label on it to show some meaningful information on the progress... like "30 of 999 items processed". Yes, it won't work if you resize the panel, needs some adjustments if you want to create it in the 3rd, but this is the code I use:


Unit uProgressBarInStatusBar;

Interface

Uses Vcl.ComCtrls, Vcl.StdCtrls;

Type
 TProgressBarInStatusBar = Class
 public
  Class Procedure CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
 End;

Implementation

Uses Vcl.Controls, System.Classes;

Class Procedure TProgressBarInStatusBar.CreateIn(Const inStatusBarPanel: TStatusPanel; Var outProgressBar: TProgressBar; Var outLabel: TLabel);
Var
 statusbar: TStatusBar;
Begin
 statusbar := inStatusBarPanel.Collection.Owner As TStatusBar;

 outProgressBar := TProgressBar.Create(statusbar);
 outProgressBar.Parent := statusbar;
 outProgressBar.Top := 2;
 outProgressBar.Left := 1;
 outProgressBar.Width := inStatusBarPanel.Width - 3;
 outProgressBar.Height := statusbar.ClientHeight - 3;

 outLabel := TLabel.Create(outProgressBar);
 outLabel.Parent := outProgressBar;
 outLabel.Align := alClient;
 outLabel.AutoSize := False;
 outLabel.Alignment := taCenter;
End;

End.

The small sacrifice of having it in the first panel makes up to it with no custom drawings / hacks needed at all. And it looks good enough:

 

image.png.a052b18560be4c685eedc70179a3f2ae.png

Thanks, how to use it?

Share this post


Link to post

Something like this:

Var
  pbar: TProgressBar;
  lbl: TLabel;
  a: Integer;
Begin
  TProgressBarInStatusBar.CreateIn(StatusBar1, pbar, lbl);
  
  For a := 0 To 1000 Do
  Begin
    pbar.Position := a Div 10;
    lbl.Caption := 'Working ' + a.ToString + '...';
    Application.ProcessMessages; // Don't do this. It's just pseudocode.
    Sleep(500);
  End;
End;

 

Share this post


Link to post
8 hours ago, aehimself said:

Something like this:


Var
  pbar: TProgressBar;
  lbl: TLabel;
  a: Integer;
Begin
  TProgressBarInStatusBar.CreateIn(StatusBar1, pbar, lbl);
  
  For a := 0 To 1000 Do
  Begin
    pbar.Position := a Div 10;
    lbl.Caption := 'Working ' + a.ToString + '...';
    Application.ProcessMessages; // Don't do this. It's just pseudocode.
    Sleep(500);
  End;
End;

 

Thanks

Share this post


Link to post
On 5/13/2022 at 4:04 PM, aehimself said:

Something like this:


Var
  pbar: TProgressBar;
  lbl: TLabel;
  a: Integer;
Begin
  TProgressBarInStatusBar.CreateIn(StatusBar1, pbar, lbl);
  
  For a := 0 To 1000 Do
  Begin
    pbar.Position := a Div 10;
    lbl.Caption := 'Working ' + a.ToString + '...';
    Application.ProcessMessages; // Don't do this. It's just pseudocode.
    Sleep(500);
  End;
End;

 

Does this method support multiple TStatusPanel? I tried with a TStatusPanel in Panel[1], but the ProgressBar, still appear in Panel[0].

Share this post


Link to post
32 minutes ago, stacker_liew said:

Does this method support multiple TStatusPanel? I tried with a TStatusPanel in Panel[1], but the ProgressBar, still appear in Panel[0].

That is because the TProgressBarInStatusBar.CreateIn() code provided earlier is not taking the Left position of the specified TStatusPanel into account, only its Width and Height.  So it is positioning the TProgressBar in the wrong place for any TStatusPanel other than the 1st one.

 

Unfortunately, neither TStatusBar nor TStatusPanel provide a property or method to get the Left position of a TStatusPanel, so you will have to retrieve it manually, either by:

 

- looping through the TStatusBar.Panels collection:

function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer;
var
  I: Integer;
  Coll: TStatusPanels;
begin
  Result := 0;
  Coll := APanel.Collection as TStatusPanels;
  for I := 0 to APanel.Index-1 do
    Inc(Result, Coll[I].Width);
end;

- or by asking the OS directly:

uses
  ..., Winapi.CommCtrl;

function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer;
var
  R: TRect;
begin
  SendMessage((APanel.Collection.Owner as TStatusBar).Handle, SB_GETRECT, APanel.Index, LPARAM(@R));
  Result := R.Left;
end;

In which case, since CreateIn() wants the TProgressBar to fill the full rectangle of the TStatusPanel anyway, I would just ask the OS for the rectangle and use it as-is, eg:

uses
  ..., Winapi.CommCtrl;

class procedure TProgressBarInStatusBar.CreateIn(const inStatusBarPanel: TStatusPanel;
  var outProgressBar: TProgressBar; var outLabel: TLabel);
var
  statusbar: TStatusBar;
  R: TRect;
Begin
  statusbar := inStatusBarPanel.Collection.Owner As TStatusBar;

  SendMessage(statusbar.Handle, SB_GETRECT, inStatusBarPanel.Index, LPARAM(@R));

  outProgressBar := TProgressBar.Create(statusbar);
  outProgressBar.Parent := statusbar;
  outProgressBar.Top := R.top;
  outProgressBar.Left := R.left;
  outProgressBar.Width := R.Width;
  outProgressBar.Height := R.Height;

  outLabel := TLabel.Create(outProgressBar);
  outLabel.Parent := outProgressBar;
  outLabel.Align := alClient;
  outLabel.AutoSize := False;
  outLabel.Alignment := taCenter;
end;

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
45 minutes ago, Remy Lebeau said:

That is because the TProgressBarInStatusBar.CreateIn() code provided earlier is not taking the Left position of the specified TStatusPanel into account, only its Width and Height.  So it is positioning the TProgressBar in the wrong place for any TStatusPanel other than the 1st one.

 

Unfortunately, neither TStatusBar nor TStatusPanel provide a property or method to get the Left position of a TStatusPanel, so you will have to retrieve it manually, either by:

 

- looping through the TStatusBar.Panels collection:


function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer;
var
  I: Integer;
  Coll: TStatusPanels;
begin
  Result := 0;
  Coll := APanel.Collection as TStatusPanels;
  for I := 0 to APanel.Index-1 do
    Inc(Result, Coll[I].Width);
end;

- or by asking the OS directly:


uses
  ..., Winapi.CommCtrl;

function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer;
var
  R: TRect;
begin
  SendMessage((APanel.Collection.Owner as TStatusBar).Handle, SB_GETRECT, APanel.Index, LPARAM(@R));
  Result := R.Left;
end;

In which case, since CreateIn() wants the TProgressBar to fill the full rectangle of the TStatusPanel anyway, I would just ask the OS for the rectangle and use it as-is, eg:


uses
  ..., Winapi.CommCtrl;

class procedure TProgressBarInStatusBar.CreateIn(const inStatusBarPanel: TStatusPanel;
  var outProgressBar: TProgressBar; var outLabel: TLabel);
var
  statusbar: TStatusBar;
  R: TRect;
Begin
  statusbar := inStatusBarPanel.Collection.Owner As TStatusBar;

  SendMessage(statusbar.Handle, SB_GETRECT, inStatusBarPanel.Index, LPARAM(@R));

  outProgressBar := TProgressBar.Create(statusbar);
  outProgressBar.Parent := statusbar;
  outProgressBar.Top := R.top;
  outProgressBar.Left := R.left;
  outProgressBar.Width := R.Width;
  outProgressBar.Height := R.Height;

  outLabel := TLabel.Create(outProgressBar);
  outLabel.Parent := outProgressBar;
  outLabel.Align := alClient;
  outLabel.AutoSize := False;
  outLabel.Alignment := taCenter;
end;

 

Thanks, it works now.

Share this post


Link to post
18 minutes ago, stacker_liew said:

Thanks, it works now.

BTW, how to handle when the form is resized while the processing is doing?

Share this post


Link to post
9 hours ago, stacker_liew said:

BTW, how to handle when the form is resized while the processing is doing?

You either manually set the position on the form's OnResize event or set the proper anchors after creating the components.

Share this post


Link to post
On 5/15/2022 at 7:45 PM, aehimself said:

You either manually set the position on the form's OnResize event or set the proper anchors after creating the components.

Setting the acnchors after creating the components doesn't work.

Share this post


Link to post
20 minutes ago, stacker_liew said:

Setting the acnchors after creating the components doesn't work.

Done with setting the correct position and size on the form's OnResize event, thanks.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×