Friday, April 8, 2011

Delphi 5: How to suspend anchor layouts?

Is there a way to suspend all anchored controls on a form from moving or resizing themselves temporarily? i.e.:

procedure ScaleFormBy(AForm: TForm; n, d: Integer);
begin
    AForm.SuspendAnchors();
    try
       AForm.ScaleBy(n, d);
    finally
       AForm.ResumeAnchors();
    end;
end;

I need to do this because I'm calling

AForm.ScaleBy(m, d);

Which does not handle anchored controls properly. (it pushes left+right or top+bottom anchored controls off the edge of the form.

Note: I want to disable Anchors, not Alignment.

From stackoverflow
  • SuspendAnchors sound like a base method but I don't think it's part of the base Delphi language :) Here is some code that does the trick:


    var aAnchorStorage: Array of TAnchors;
    procedure AnchorsDisable(AForm: TForm);
    var
      iCounter: integer;
    begin
      SetLength(aAnchorStorage, AForm.ControlCount);
      for iCounter := 0 to AForm.ControlCount - 1 do begin
        aAnchorStorage[iCounter] := AForm.Controls[iCounter].Anchors;
        AForm.Controls[iCounter].Anchors := [];
      end;
    end;
    
    procedure AnchorsEnable(AForm: TForm);
    var
      iCounter: integer;
    begin
      SetLength(aAnchorStorage, AForm.ControlCount);
      for iCounter := 0 to AForm.ControlCount - 1 do
        AForm.Controls[iCounter].Anchors := aAnchorStorage[iCounter];
    end;
    
    procedure TForm1.btnAnchorsDisableClick(Sender: TObject);
    begin
      AnchorsDisable(Self);
    end;
    
    procedure TForm1.btnAnchorsEnableClick(Sender: TObject);
    begin
      AnchorsEnable(Self);
    end;
    


    Enjoy

    mghie : I would replace the global variable by extra parameters to the procedures, or make some kind of map between the TForm reference and the saved anchors, but apart from that it's a +1.
    Kris De Decker : Yes - this code needs to be adjusted for production use. I tested the code with a simple form and it works fine with regards to scaling, adjusting size and width.
    Ian Boyd : It doesn't handle nested control - i.e. TPanel, TTabSheet
  • Guy had a good idea, but it didn't handle child control (i.e. TPanel, TPageControl, etc)

    Here's a variant that uses recursion. Also, notice that i don't actually disable anchors - turn out that ScaleBy doesn't work with no anchors either.

    So now you can scale a form using:

    procedure ScaleFormBy(AForm: TForm; M, D: Integer);
    var
       StoredAnchors: TAnchorsArray;
    begin
       StoredAnchors := DisableAnchors(AForm);
       try
           AForm.ScaleBy(M, D);
       finally
           EnableAnchors(AForm, StoredAnchors);
       end;
    end;
    

    With the support library functions:

    TAnchorsArray = array of TAnchors;
    
    function DisableAnchors(ParentControl: TWinControl): TAnchorsArray;
    var
       StartingIndex: Integer;
    begin
       StartingIndex := 0;
       DisableAnchors_Core(ParentControl, Result, StartingIndex);
    end;
    
    procedure EnableAnchors(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray);
    var
       StartingIndex: Integer;
    begin
       StartingIndex := 0;
       EnableAnchors_Core(ParentControl, aAnchorStorage, StartingIndex);
    end;
    
    procedure DisableAnchors_Core(ParentControl: TWinControl; var aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
    var
       iCounter: integer;
       ChildControl: TControl;
    begin
       if (StartingIndex+ParentControl.ControlCount+1) > (Length(aAnchorStorage)) then
          SetLength(aAnchorStorage, StartingIndex+ParentControl.ControlCount+1);
    
       for iCounter := 0 to ParentControl.ControlCount - 1 do
       begin
          ChildControl := ParentControl.Controls[iCounter];
          aAnchorStorage[StartingIndex] := ChildControl.Anchors;
    
          if ([akLeft, akRight ] * ChildControl.Anchors) = [akLeft, akRight] then
             ChildControl.Anchors := ChildControl.Anchors - [akRight];
    
          if ([akTop, akBottom] * ChildControl.Anchors) = [akTop, akBottom] then
             ChildControl.Anchors := ChildControl.Anchors - [akBottom];
    
          Inc(StartingIndex);
       end;
    
       //Add children
       for iCounter := 0 to ParentControl.ControlCount - 1 do
       begin
          ChildControl := ParentControl.Controls[iCounter];
          if ChildControl is TWinControl then
             DisableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
       end;
    end;
    
    procedure EnableAnchors_Core(ParentControl: TWinControl; aAnchorStorage: TAnchorsArray; var StartingIndex: Integer);
    var
       iCounter: integer;
       ChildControl: TControl;
    begin
       for iCounter := 0 to ParentControl.ControlCount - 1 do
       begin
          ChildControl := ParentControl.Controls[iCounter];
          ChildControl.Anchors := aAnchorStorage[StartingIndex];
    
          Inc(StartingIndex);
       end;
    
       //Restore children
       for iCounter := 0 to ParentControl.ControlCount - 1 do
       begin
          ChildControl := ParentControl.Controls[iCounter];
          if ChildControl is TWinControl then
             EnableAnchors_Core(TWinControl(ChildControl), aAnchorStorage, StartingIndex);
       end;
    end;
    
    
    end;
    
    Ian Boyd : All this hackery to fix problems Borland could.

0 comments:

Post a Comment