Search code examples
delphiflowpanel

How to resize a control inside a TFlowPanel using the Align property?


I have a few controls (namely, TDBChart) inside a TFlowPanel. When the user clicks on one of them, I'd like it to fill the entire flow panel's client area. But, it seems that changing the visible and align property of child controls inside a flow panel at run time doesn't have any effect. Is there a special trick to this? I found the Realign() method, but it doesn't seem to have any effect on the control's layout. Here's the code to my OnClick event:

var
  AChart: TDBChart;
  V: Boolean;
  i: Integer;
begin
  AChart := TDBChart(Sender);
  if AChart.Align = alNone then
  begin
    V := False;
    AChart.Align := alClient;
  end else begin
    V := True;
    AChart.Align := alNone;
  end;
  for i := 0 to FlowPanel1.ControlCount - 1 do
    if FlowPanel1.Controls[i] is TDBChart then
      if FlowPanel1.Controls[i] <> AChart then
        FlowPanel1.Controls[i].Visible := V;
end;

The charts are hidden or shown as expected, but ADBChart doesn't fill the entire flow panel's client area.


Solution

  • A FlowPanel does not care its controls' alignment settings, much like it doesn't care for their position - it is designed only to flow them.

    One solution can be to derive a new class and override AlignControls, and in it, resize the control that would fill the surface accordingly. As an example:

    type
      TFlowPanel = class(extctrls.TFlowPanel)
      protected
        procedure AlignControls(AControl: TControl; var Rect: TRect); override;
      end;
    
    ..
    
    procedure TFlowPanel.AlignControls(AControl: TControl; var Rect: TRect);
    var
      i, VisibleCount, VisibleControl: Integer;
    begin
      VisibleCount := 0;
      VisibleControl := 0;
      for i := 0 to ControlCount - 1 do
        if Controls[i].Visible then begin
          Inc(VisibleCount);
          VisibleControl := i;
        end;
    
      if (VisibleCount = 1) and (Controls[VisibleControl] = AControl) and
          (AControl.Align = alClient) then begin
        // preserve 'Explicit..' settings
        AControl.ControlState := AControl.ControlState + [csAligning];
        AControl.SetBounds(1, 1, ClientWidth - 1, ClientHeight -1);
        AControl.ControlState := AControl.ControlState - [csAligning];
      end;
    
      inherited;
    end;
    

    Then you can set all of your charts' click event to this handler:

    var
      AChart: TTDBChart;
    
      procedure SetVisibility(Visible: Boolean);
      var
        i: Integer;
      begin
        for i := 0 to FlowPanel1.ControlCount - 1 do
          if FlowPanel1.Controls[i] is TDBChart then
            if FlowPanel1.Controls[i] <> AChart then
              FlowPanel1.Controls[i].Visible := Visible;
      end;
    
    begin
      AChart := TDBChart(Sender);
      if AChart.Align = alNone then
      begin
        SetVisibility(False);
        AChart.Align := alClient;
      end else begin
        AChart.Align := alNone; // set before changing visible
        SetVisibility(True);
        AChart.SetBounds(0, 0, AChart.ExplicitWidth, AChart.ExplicitHeight);
      end;
    end;
    

    I should note that this is only good for a fixed sized flowpanel.