Search code examples
delphidelphi-xe6dfm

How to tell Delphi to not include non-published properties in DFM?


I have a custom control:

type
   TContosoFrobber = class(TCustomControl)
   private
   end;

Internally, my component creates a control:

type
   TContosoFrobber = class(TCustomControl)
   private
      FMyDateTimePicker: TDateTimePicker;
   public
      constructor Create(AOwner : TComponent); override;
      property DateTimePicker: TDateTimePicker read FMyDateTimePicker;
   end;

constructor TContosoFrobber.Create(AOwner: TComponent);
begin
   inherted Create(AOwner);

   FMyControl := TMyDateTimePicker.Create(AOwner);
end;

where TMyDateTimePicker is a simple descendant of TDateTimePicker

TMyDateTimePicker = class(TDateTimePicker)
protected
end;

So sum up what i have done:

  • declare a private variable of TDateTimePicker
  • expose it as a public (i.e. non-published) property of type TDateTimePicker
  • through polymorphism, the control is actually a descendant of TDateTimePicker

And that all worked - until i recently when i re-installed Delphi XE6 (on Windows 10).

The DFM

Which is why i couldn't understand why i got the error at design-time:

Class TMyDateTimePicker not found

Why is it trying to find that class? That class in an implementation detail; it's not published for streaming. How is the streaming system even trying to create it!? So i check the DFM:

  object cfBeachBall: TContosoFrobber
     Left = 445
     Top = 25
     Width = 101
     Height = 22
     ...snip...
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
  end

Why is the TInternalDateTimePicker ending up in the dfm:

  • the property is public, not published

How do i stop the form streaming system from placing a non-published property in the dfm?

Even worse is the buggy IDE

Not only is it sometimes including a property it shouldn't. Sometimes it is including the property it shouldn't twice:

  object cfPlasticBag: TContosoFrobber
     Left = 445
     Top = 25
     Width = 101
     Height = 22
     ...snip...
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
     object TMyDateTimePicker
        Left = 0
        Top = 0
        Width = 101
        Height = 22
        Date = 37306.581535243100000000
        Time = 37306.581535243100000000
        TabOrder = 0
        TabStop = False
     end
  end
  • how do i stop the dfm from containing non-published properties?
  • how do i stop the dfm from including it twice?

Hack workaround

I know the horrible hack: tell the DFM about the control it should not have any business knowing about:

initialization
    RegisterClass(TMyDateTimePicker);

finalization
    UnRegisterClass(TMyDateTimePicker);

end.

Now the dfm contains a control it has no business knowing about. Any time i save a form it will contain references to things it shouldn't. And worst of all: i validated it's mistaken belief in a TMyDateTimePicker.

Why did Delphi XE6 not do this before the reinstall? Perhaps i need to install the last updated of the long-since unsupported version of Delphi?

Known bug?

enter image description here


Solution

  • The problem is that you are assigning the wrong Owner to the TDateTimePicker. You are assigning your TContosoFrobber's Owner instead of the TContosoFrobber itself:

    constructor TContosoFrobber.Create(AOwner: TComponent);
    begin
      inherted Create(AOwner);
      FMyControl := TMyDateTimePicker.Create(Self); // <-- not AOwner!
    end;
    

    Or: as Jerry Doge mentioned, you can use a nil Owner instead. You just have to Free() the TDataTimePicker manually:

    type
      TContosoFrobber = class(TCustomControl)
      private
        FMyDateTimePicker: TDateTimePicker;
      public
        constructor Create(AOwner : TComponent); override;
        destructor Destroy; override;
        ...
      end;
    
    constructor TContosoFrobber.Create(AOwner: TComponent);
    begin
      inherted Create(AOwner);
      FMyControl := TMyDateTimePicker.Create(nil);
    end;
    
    destructor TContosoFrobber.Destroy;
    begin
      FMyControl.Free;
      inherted Destroy;
    end;