6

I am trying to use TFlowPanel component in the following manner:

  1. Place on the main form Form1 component FlowPanel1: TFlowPanel.
  2. Set Form1.Width = 400, FlowPanel1.Align = alTop, FlowPanel1.AutoSize = True, FlowPanel1.AutoWrap = True.
  3. Place on the FlowPanel1 5 SpeedButtons and set their Width to 64.
  4. Compile and run.
  5. Reduce width of the form (something about Form1.Width = 200).

For some reason, the speedbuttons do not automatically line up in two rows when user resizes the form. Although, they do line up in two rows when AutoSize = False, AutoWrap = True.
What is the reason for this behavior and how to solve it?

Edit: I've found "quick and dirty" solution. The following code is the event handler to the TFlowPanel.OnResize event:

procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
  with FlowPanel1 do
  begin
    AutoSize := False;
    Realign;            // line up controls
    AutoSize := True;   // adjust TFlowPanel.Height
  end;
end;

However, I still wonder if there is a standard way to solve the problem.

2 Answers2

6

I wasn't able to find the exact reason of such behavior in code, but basically you've challenged two sizing properties to fight, the AutoSize and Align. The problem is, I think, that when you resize a form, the control with AutoSize configured to True and Align set to alTop will first try to autosize the control and then align to top of its parent. What I can tell for sure, these two properties shouldn't be combined at least from their logical meaning.

What I would suggest to your workaround is turn off the autosize by default and in OnResize event turn it temporary on and back to off to automatically adjust the height. So in code it would change simply to:

procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
  // there's no Realign here, since the AlignControls request is called
  // at control resize, so here you have children already aligned, what
  // you then need is to request the control to autosize the height and
  // turn off the autosizing to the default, disabled state
  FlowPanel1.AutoSize := True;
  FlowPanel1.AutoSize := False;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • 1
    +1 for shorter and nicer code; Align and AutoSize are not combined by default. –  Sep 13 '12 at 10:17
  • `AutoSize` and `Align` go together well by default, see [my answer](http://stackoverflow.com/a/12412249/757830). Nice shortener though. +1 – NGLN Sep 13 '12 at 18:10
3

tl,dr: It's a bug in TFlowPanel.

Normally, the AutoSize and Align properties go together very well by default since this is taken care of already at TControl level, so I wondered why this happened. I noticed an overriden AlignControls method in TFlowPanel and thought to bypass it for testing purposes:

type
  TWinControlAccess = class(TWinControl);
  TAlignControls = procedure(Instance: TObject; AControl: TControl;
    var Rect: TRect);

  TFlowPanel = class(Vcl.ExtCtrls.TFlowPanel)
  protected
    procedure AlignControls(AControl: TControl; var Rect: TRect); override;
  end;

  TForm1 = class(TForm)
    ...

procedure TFlowPanel.AlignControls(AControl: TControl; var Rect: TRect);
begin
  // Skip TCustomFlowPanel.AlignControls
  TAlignControls(@TWinControlAccess.AlignControls)(Self, AControl, Rect);
end;

procedure TForm1.FlowPanel1Resize(Sender: TObject);
begin
  // Do my own aligning of the last button
  if FlowPanel1.ClientWidth < Button5.BoundsRect.Right then
  begin
    Button5.Left := 1;
    Button5.Top := Button1.Height + 1;
  end
  else if FlowPanel1.ClientWidth > Button4.BoundsRect.Right + Button5.Width then
  begin
    Button5.Left := Button4.BoundsRect.Right;
    Button5.Top := 1;
  end;
end;

Now, this works as expected. So what's wrong with TFlowPanel's implementation of AlignControls? It looks like the following snippet is the reason:

if AutoSize then
  Rect := TRect.Create(
    Rect.Left,
    Rect.Top,
    Rect.Left + (ExplicitWidth - (Width - (Rect.Right - Rect.Left))),
    Rect.Top + (ExplicitHeight - (Height - (Rect.Bottom - Rect.Top))));

When this part is commented out, the behaviour is as expected with Align set as well as not. Now, I would like to submit this to QC, but maybe I am overlooking some of its aspects. Please edit or comment when (and then why) this code indeed is needed.

NGLN
  • 43,011
  • 8
  • 105
  • 200
  • +1, I've been suspecting the same, but wasn't sure. Still, I'll rather avoid to combine `AutoSize` with `Align` in my forms (maybe because of a logical reason and some bad experience from the past :-) – TLama Sep 13 '12 at 18:24
  • I've just tried TLabel component with `AutoSize=True`, `Align=alTop`, `WordWrap=True` and some long text in `Caption`. It seems that there is the same problem: resizing the form does not lead to adjusting height of the TLabel. Also, the same two-line shortener proposed by @TLama works fine. –  Sep 13 '12 at 19:00
  • `TLabel` has its own reintroduced `AutoSize` property which - I now dare to say - also has a buggy implementation. The same goes for `TStaticText`, etc... Thinking of it, I really don't get why these controls have reintroduced `AutoSize` properties or why they don't behave like implemented in `T(Win)Control`. This in comparison to `TToolBar` which doesn't rumble with an own `AutoSize` implementation but instead is satisfied with the one from `TControl` and auto sizes well because of it. – NGLN Sep 13 '12 at 19:20
  • Thanks, I see now that TToolBar autosizes correctly. However, I cannot comment your answer about the bug in TFlowPanel as I've never delved into VCL source (maybe I should?). Probably I'll stay with simple workaround. –  Sep 13 '12 at 19:41