Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi 2009 creates my components in wrong order

Three components, working together:
* CompA, a TComponent descendant, a mastermind component knowing many things and tying things together
* CompB, a TComponent descendant, mines some data from it's CompA and crunches it. Can amongst other things feed CompC with data to present
  - Has a published property of type CompA
* CompC, a TComponent descendant, a TFrame descendant drawing surface that can be set at designtime to use a CompB as data provider
  - Has a published property of type CompA
  - Has a published property of type CompB

I think I remember having read, even though I cannot state where, that Delphi's streaming engine reads all components from the .dfm and builds a dependency graph. This graph is then used to create all components in correct order. For the listed components it should be CompA first (since it uses none of the other ones), then the CompB (it uses CompA and must be created after) and lastly the CompC since it has properties of both the other component types.

This does not happen. CompC is created before CompB. If i rearrange the order in the .dfm file using a text editor it works. The property values are not used in any constructors, only in the Loaded procedures. But truly there must be a way to make it work no matter the order of components in the dfm?

I've been banging my head against the wall for two days straight now, I need somebody to tell me which keyword I forgot or what error in design I have.

like image 316
DelphiDabber Avatar asked Dec 17 '22 16:12

DelphiDabber


2 Answers

I suspect your fault is you're trying to access other objects properties on setters for sibling pointers, forgetting that at dfm loading stage --runtime-- you can't be sure pointers to other components your component depends on are yet valid because it is possible that other component is not yet created. This works this way since Delphi 1.

Because of this, you usually deffer the reading of other component's state (for example) to your overridden Loaded method.

When the streaming system loads a form or data module from its form file, it first constructs the form component by calling its constructor, then reads its property values from the form file. After reading all the property values for all the components, the streaming system calls the Loaded methods of each component in the order the components were created. This gives the components a chance to initialize any data that depends on the values of other components or other parts of itself. Note: All references to sibling components are resolved by the time Loaded is called. Loaded is the first place that sibling pointers can be used after being streamed in.

Because of this, usually on a setter method for a sibling pointer property you usually perform a check of this type:

procedure TMyComponent.SetDataSource(Value: TDataSource);
begin
  FDataSource := Value;
  //streaming in stage
  if not (csLoading in ComponentState) then
    ReadDataSourceProperties;
end;

procedure TMyComponent.Loaded;
begin
  ReadDataSourceProperties;
end;

Take a look at the VCL source, you'll find hundreds of examples of this.

like image 77
jachguate Avatar answered Dec 24 '22 11:12

jachguate


If your components are that much dependent on creation order, you are always going to be in trouble relying on the streaming mechanism. Just one addition or removal of a(n other) component on the form/datamodule can throw your order out of whack.

To ensure proper creation order, you'd be better off creating them at run time. Just note that when you create components at run-time the Loaded method will not be called. You will either have to do it yourself or move the code to some init method that you call after you create your components.

like image 31
Marjan Venema Avatar answered Dec 24 '22 13:12

Marjan Venema