Search code examples
delphivcl

Delphi custom component, can't position in Designer when dragging (custom setter for Top/Left properties)


I have written a component that paints a picture centered at X,Y. At runtime, the component is moved around with a SetXY(X,Y: integer) procedure.

To acheive this, I calculate what the Left and Top values should be in the paint routine and set them accordingly. And all that works just fine.

But when I try to do the initial positioning at design-time, I cannot position the component by dragging it to the desired location.

When I set either the Left or Top property via the Object Inspector, it works.

procedure MyCustomComponent.SetTop(const Value: integer);
begin
  if Top = (Value) then
    exit;
    
  inherited Top := Value;
  if csDesigning in ComponentState then
    FY := Value + FBmp.Width div 2;
  Invalidate;
end;
    
procedure MyCustomComponent.SetLeft(const Value: integer);
begin
  if Left = (Value) then
    exit;
    
  inherited Left := Value;
  if csDesigning in ComponentState then
    FX := Value + FBmp.Width div 2;
  Invalidate;
end;

I suspect that when the component is dragged and dropped at design-time on the Form, it does not actually set the Left and Top public properties, but instead calls some other function that sets the private field members of the underlying control that I inherit the component from (TGraphicControl).


Solution

  • As Both fpiette and Remy pointed out, overriding the SetBounds did the trick. When positioning a component in design time via dragging and dropping the component it does not individually set the public Left and Top properties. But instead makes a call to the SetBounds procedure.

    procedure MyComponent.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
    begin
      inherited;
      if csDesigning in ComponentState then begin
        FX := ALeft + FBmp.Width div 2;
        FY := ATop + FBmp.Height div 2;
      end;
    end;
    

    Edit:

    After testing i found that for the component to be placed correctly on the form in runtime you have to also check the csLoading in component state.

    So a more complete solution would be like this:

    procedure MyComponent.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
    begin
      inherited;
      if (csDesigning in ComponentState) or (csLoading in ComponentState ) then begin
        FX := ALeft + FBmp.Width div 2;
        FY := ATop + FBmp.Height div 2;
      end;
    end;