FireMonkey’s BoundsRect bug, and working around it

Just a quick one, but the BoundsRect property getter in the FireMonkey TControl is incorrectly implemented, returning (0, 0, Width, Height) rather than (Position.X, Position.Y, Position.X + Width, Position.Y + Height). At first you might be tempted to say the property just has different semantics to the VCL version, however the implementation of the property setter gives the lie to that. Steps:

  1. Create a new FireMonkey HD application.
  2. Add a button to the form.
  3. Handle the button’s OnClick event like this:
procedure TForm1.Button1Click(Sender: TObject);
begin
  Button1.BoundsRect := Button1.BoundsRect;
end;

Expected: nothing to happen. Actual: the button moves to the top-left corner of the form.

To work around this bug, either don’t read BoundsRect in the first place (it is after all just a small utility property), or put the following class helper in scope:

type
  TControlHelper = class helper for TControl
  strict private
    function GetBoundsRect: TRectF;
    procedure SetBoundsRect(const Value: TRectF);
  public
    property BoundsRect: TRectF read GetBoundsRect
      write SetBoundsRect;
  end;

//...

function TControlHelper.GetBoundsRect: TRectF;
begin
  Result.Left := Position.X;
  Result.Top := Position.Y;
  Result.Right := Result.Left + Width;
  Result.Bottom := Result.Top + Height;
end;

procedure TControlHelper.SetBoundsRect(const Value: TRectF);
begin
  SetBounds(Value.Left, Value.Top, Value.Width, Value.Height);
end;

Update 20/12/13: as pointed out in the comments, while GetBoundsRect remains as miscoded as ever in XE5, there is also a ParentedRect read-only property that does what the BoundsRect property getter should do. What a mess!

Advertisements

9 thoughts on “FireMonkey’s BoundsRect bug, and working around it

  1. Hmmm. I don’t develop in Delphi since 2007, but i think they don’t have learned testdriven development… Anyway just following the things that are going on on the delphi side of life. Great to post such errors. They are endless annoying afaik when you stumble upon them.

  2. Maybe I’m wrong, but isn’t that by design?
    Delphi help on BoundsRect says:

    The coordinates of the upper-left corner of the rectangle are (0,0), and the coordinates of the lower-right corner are (Width, Height).

    When BoundsRect is set, the Position property is set to be the upper-left corner of the given bound rectangle, and the Width and Height of the control receive the values of the width and height of the given bound rectangle.

    • If it were by ‘design’, then the property setter wouldn’t behave in the way it does (moreover, searching the FMX source shows it is the property setter that is used a lot more than the getter). Or can you sincerely claim the behaviour I outline in my ‘steps’ actually makes sense…?

  3. Well I agree. I did expect BoundsRect to return actual bound rect too, not bound rect relative to left-top of control, i.e.

    instead current

    function TControl.GetBoundsRect: TRectF;
    begin
    Result := RectF(0, 0, Width, Height);
    end;

    I guess it should be

    function TControl.GetBoundsRect: TRectF;
    begin
    Result := RectF(0, 0, Width, Height);
    Result.Offset(Position);
    end;

    But then I don’t know why docs confirm left-top is 0, 0 🙂

    As for steps: yes, it seems no sense of course

  4. Another hint that it is indeed a bug is the fact that you can run the same code with VCL and then the button stays in place. There the getter is implemented like this:

    function TControl.GetBoundsRect: TRect;
    begin
    Result.Left := Left;
    Result.Top := Top;
    Result.Right := Left + Width;
    Result.Bottom := Top + Height;
    end;

  5. Had the same problem, so I browsed through the FMX.Controls (XE5) code and found that ParentedRect does what I expected Boundsrect do do. (In XE2) ParentedRect is located in FMX.Types

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s