Search code examples
delphidelphi-xe8

Method for placing a TProgressBar on a TStatusBar no longer works


In the past I used the method described here to place a TProgressBar on a TStatusBar in Delphi:

procedure TForm1.FormCreate(Sender: TObject);
var
  ProgressBarStyle: integer;
begin
  //enable status bar 2nd Panel custom drawing
  StatusBar1.Panels[1].Style := psOwnerDraw;
  //place the progress bar into the status bar
  ProgressBar1.Parent := StatusBar1;
  //remove progress bar border
  ProgressBarStyle := GetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE);
  ProgressBarStyle := ProgressBarStyle - WS_EX_STATICEDGE;
  SetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE, ProgressBarStyle);
end;

procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel;  
  const Rect: TRect);
begin
  if Panel = StatusBar.Panels[1] then
  with ProgressBar1 do
  begin
    Top := Rect.Top;
    Left := Rect.Left;
    Width := Rect.Right - Rect.Left;
    Height := Rect.Bottom - Rect.Top;
  end;
end;

But (after a recent Windows update?) this no longer works, i.e. the old programs still work as expected, but newly compiled ones don't. I'm using the same Delphi version, XE8, on Windows 10.

Does this mean that this method was inappropriate? What is the right way to do this?


Solution

  • As others have explained, your mismanagement of the TProgressBar's window styles is the cause of your problem.

    I want to add that you do not need to use (and should not be using) the TStatusBar.OnDrawPanel event to position the TProgressBar at all. It is a drawing event, not an object management event. If you are not going to manually draw a progress bar onto the TStatusBar.Canvas then you should get rid of the OnDrawPanel handler completely.

    You can instead position the TProgressBar one time at startup, by using the SB_GETRECT message to get the panel's coordinates and dimensions and then position the TProgressBar accordingly, eg:

    uses
      CommCtrl;
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      ...
      R: TRect;
    begin
      // no need to set the panel's Style to psOwnerDraw!
      ...
      //place the progress bar into the status bar
      SendMessage(StatusBar1.Handle, SB_GETRECT, 1, LPARAM(@R));
      ProgressBar1.Parent := StatusBar1;
      ProgressBar1.SetBounds(R.Left, R.Top, R.Width, R.Height);
      ...
    end;
    

    If your Form is resizable, you can use the TStatusBar.OnResize event to reposition the TProgressBar if the panel resizes:

    uses
      CommCtrl;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      // no need to set the panel's Style to psOwnerDraw!
      ...
      //place the progress bar into the status bar
      ProgressBar1.Parent := StatusBar1;
      StatusBar1Resize(nil);
      ...
    end;
    
    procedure TForm1.StatusBar1Resize(Sender: TObject);
    var
      R: TRect;
    begin
      //place the progress bar over the 2nd panel
      SendMessage(StatusBar1.Handle, SB_GETRECT, 1, LPARAM(@R));
      ProgressBar1.SetBounds(R.Left, R.Top, R.Width, R.Height);
    end;