2

When designing a form in the IDE, non-visual components (eg TMainMenus, TDatamodules) can be freely placed and positioned. The position is persisted, so that on reloading the form these components appear in the correct place.

But, TComponent does not have Top or Left properties!

So, how can my code access the 'designed position' of non visual components?

Roddy
  • 66,617
  • 42
  • 165
  • 277
  • @David, well the IDE is just "Runtime code", surely? I'm not expecting it to be easy, but it's got to be possible. – Roddy Jun 11 '12 at 21:19
  • 2
    For the left postion try `LongRec(YourComponent.DesignInfo).Lo` and for the top use `LongRec(YourComponent.DesignInfo).Hi` – RRUZ Jun 11 '12 at 21:28
  • 5
    What's the benefit of your runtime code having access to the left and top properties of non-visual components? – Sam M Jun 11 '12 at 21:48

3 Answers3

6

This can be accessed at runtime, but it's sort of a hack. (Mostly because it's implemented as sort of a hack.)

The Left and Top properties are set up as Word-size values, and the two of them are packed together into a Longint called TComponent.FDesignInfo. You can obtain its value with the DesignInfo property. Have a look at TComponent.DefineProperties to get a look into how it's used.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
0

And also:

  • How to set DesignInfo to a point like (-100,-100)?

Objective: Put the icon out of visual area, hide it on design-time.

Note: It is very usefull when for example creating simple visual components derived directly from TComponent, i have in mind a very simple label (taht is allways aligned to top, has allways left=0, top is auto-calculated, bla bla bla) that only stores it's caption property into the .dfm file; and also any localizer will only see that caption property.

SOLUTION is to Override ReadState with code like this:

procedure TMyComponent.ReadState(Reader:TReader);
var
   NewDesignInfo:LongRec;
begin
     inherited ReadState(Reader);
     NewDesignInfo.Hi:=Word(-100); // Hide design-time icon (top position = -100)
     NewDesignInfo.Lo:=Word(-100); // Hide design-time icon (left position = -100)
     DesignInfo:=Longint(NewDesignInfo); // Set the design-icon position out of visual area
end;

Hope help others!

anonymous
  • 1
  • 1
0

This worked for me. Source: CnPack CnAlignSizeWizard.pas.

procedure SetNonVisualPos(Form: TCustomForm; Component: TComponent; X, Y: Integer);
const
  NonvisualClassNamePattern = 'TContainer';
  csNonVisualSize = 28;
  csNonVisualCaptionSize = 14;
  csNonVisualCaptionV = 30;
var
  P: TSmallPoint;
  H1, H2: HWND;
  Offset: TPoint;

  function HWndIsNonvisualComponent(hWnd: hWnd): Boolean;
  var
    AClassName: array[0..256] of Char;
  begin
    AClassName[GetClassName(hWnd, @AClassName, SizeOf(AClassName) - 1)] := #0;
    Result := string(AClassName) = NonvisualClassNamePattern;
  end;

  procedure GetComponentContainerHandle(AForm: TCustomForm; L, T: Integer; var H1, H2: hWnd; var Offset: TPoint);
  var
    R1, R2: TRect;
    P: TPoint;
    ParentHandle: hWnd;
    AControl: TWinControl;
    I: Integer;
  begin
    ParentHandle := AForm.Handle;
    AControl := AForm;
    if AForm.ClassNameIs('TDataModuleForm') then // ÊÇ DataModule
    begin
      for I := 0 to AForm.ControlCount - 1 do
        if AForm.Controls[I].ClassNameIs('TComponentContainer')
        and (AForm.Controls[I] is TWinControl) then
        begin
          AControl := AForm.Controls[I] as TWinControl;
          ParentHandle := AControl.Handle;
          Break;
        end;
    end;
    H2 := 0;
    H1 := GetWindow(ParentHandle, GW_CHILD);
    H1 := GetWindow(H1, GW_HWNDLAST);
    while H1 <> 0 do
    begin
      if HWndIsNonvisualComponent(H1) and GetWindowRect(H1, R1) then
      begin

        P.x := R1.Left;
        P.y := R1.Top;
        P := AControl.ScreenToClient(P);

        if (P.x = L) and (P.y = T) and (R1.Right - R1.Left = csNonVisualSize)
        and (R1.Bottom - R1.Top = csNonVisualSize) then
        begin
          H2 := GetWindow(ParentHandle, GW_CHILD);
          H2 := GetWindow(H2, GW_HWNDLAST);
          while H2 <> 0 do
          begin
            if HWndIsNonvisualComponent(H2) and GetWindowRect(H2, R2) then
            begin
              if (R2.Top - R1.Top = csNonVisualCaptionV) and (Abs(R2.Left + R2.Right - R1.Left - R1.Right) <= 1)
              and (R2.Bottom - R2.Top = csNonVisualCaptionSize) then
              begin
                Offset.x := R2.Left - R1.Left;
                Offset.y := R2.Top - R1.Top;
                Break;
              end;
            end;
            H2 := GetWindow(H2, GW_HWNDPREV);
          end;
          Exit;
        end;
      end;
      H1 := GetWindow(H1, GW_HWNDPREV);
    end;
  end;

begin
  P := TSmallPoint(Component.DesignInfo);
  GetComponentContainerHandle(Form, P.x, P.y, H1, H2, Offset);
  Component.DesignInfo := Integer(PointToSmallPoint(Point(X, Y)));
  if H1 <> 0 then
    SetWindowPos(H1, 0, X, Y, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
  if H2 <> 0 then
    SetWindowPos(H2, 0, X + Offset.x, Y + Offset.y, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
end;

Use sample:

SetNonVisualPos(TCustomForm(Designer.Root),MyComponent,10,10);