Mirror

Changing the z-order of controls (Views: 22)


Problem/Question/Abstract:

How to move a control just one position within the z-order of the parent.

Answer:

The default methods

Usually you can bring any control on a form to front or send it to the back using the methods supplied with the TControl class.

AnyControl.BringToFront;

AnyControl.SendToBack;

However, often these methods will not suffice. If you want to move the control just one position, there are no public methods to acompolish just this. In the private section of the TControl-class you find the method SetZOrderPosition which you cannot use. Looking at the source code, you'll notice, you cannot even cut-n-copy that, as it is accessing some private variables/objects, which are not made public either.

A simple solution

The solution, to work around this limitation, is to move the control either to the top or the back and move the others, that should remain in front (or behind), too. The following procedure will do just this.

The first parameter Sender takes the control to be moved. The second paramter points the direction. True will bring it to front, False will move it to the back.

procedure ChangeControlZOrder(Sender: TObject; MoveUp: Boolean = True);
var
  I, Curr: Integer;
  Control: TControl;
  List: TList;
begin
  if Sender is TControl then
  begin
    // sender is an control
    Control := Sender as TControl;
    // check for parent control, managing the z-order
    if Control.Parent = nil then
      // not available
      Exit;
    // get position of the sender
    Curr := -1;
    for I := 0 to Pred(Control.Parent.ControlCount) do
      if Control.Parent.Controls[I] = Sender then
      begin
        Curr := I;
        Break;
      end;
    if Curr < 0 then
      // hm, position not found
      Exit;
    List := TList.Create;
    try
      if MoveUp then
      begin
        for I := Curr + 2 to Pred(Control.Parent.ControlCount) do
          // get the other controls, to be moved, too
          List.Add(Control.Parent.Controls[I]);
        // bring sender to front
        Control.BringToFront;
        for I := 0 to Pred(List.Count) do
          // move the remaining controls
          TControl(List[I]).BringToFront;
      end
      else
      begin
        for I := 0 to Curr - 2 do
          // get the other controls, to be moved, too
          List.Add(Control.Parent.Controls[I]);
        // send sender to back
        Control.SendToBack;
        for I := Pred(List.Count) downto 0 do
          // move the remaining controls
          TControl(List[I]).SendToBack;
      end;
    finally
      List.Free;
    end;
  end;
end;

<< Back to main page