Reputation: 3139
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?
Upvotes: 0
Views: 2283
Reputation: 596256
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;
Upvotes: 4
Reputation: 612964
The only obvious explanation I have for the change of behaviour is that this code is wrong:
ProgressBarStyle := ProgressBarStyle - WS_EX_STATICEDGE;
This code assumes that WS_EX_STATICEDGE
is already in the style. But if it is not then you are destroying the window style. That code needs to use bitwise operations:
ProgressBarStyle := ProgressBarStyle and not WS_EX_STATICEDGE;
Note also that this window style will be lost if the window is recreated, something that does happen under the VCL. A better option would be to subclass the progress bar class and set the style directly in an overridden CreateParams
.
Upvotes: 4
Reputation: 3139
It works if you remove the lines that take care of the border:
// remove these lines
ProgressBarStyle := GetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE);
ProgressBarStyle := ProgressBarStyle - WS_EX_STATICEDGE;
SetWindowLong(ProgressBar1.Handle, GWL_EXSTYLE, ProgressBarStyle);
The resulting double border doesn't look as nice, so David's solution of calling FillRect in the OnDrawPanel may be the better solution. This has the extra advantage that you finally can get rid of that ugly green :-).
procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel;
const Rect: TRect);
var
R: TRect;
begin
if Panel = StatusBar.Panels[1] then
begin
StatusBar.Canvas.Brush.Color := clBtnFace;
StatusBar.Canvas.FillRect(Rect);
R := Rect;
R.Right := Round(R.Left + (R.Right - R.Left) * FProgress {0..1});
StatusBar.Canvas.Brush.Color := clGrayText;
StatusBar.Canvas.FillRect(R);
end;
end;
Note: you'll have to call the StatusBar's Invalidate
method so that the ONDrawPanel event handler gets executed.
Upvotes: 2