bravesofts 6 Posted September 26, 2021 (edited) i have this Abstract Base Unit : unit UBase; interface uses System.Classes; type TBaseAbstract = class(TComponent) procedure Method(aObjectEvent: TNotifyEvent); virtual; abstract; end; TConcrete = class(TBaseAbstract) procedure Method(aObjectEvent: TNotifyEvent); override; strict private fReadOnlyProperty: string; public constructor New(aValue: string; aOwner: TComponent); destructor Destroy; override; property ReadOnlyProperty: string Read fReadOnlyProperty; // Read Only Property .. end; implementation { TConcrete } destructor TConcrete.Destroy; begin inherited; end; procedure TConcrete.Method(aObjectEvent: TNotifyEvent); begin aObjectEvent(Self) end; constructor TConcrete.New(aValue: string; aOwner: TComponent); begin inherited Create(aOwner); fReadOnlyProperty := aValue + Self.ClassName +' ] '+ sLineBreak + 'Object Instance Name is: [' + Self.Name +' ]'; end; end. Quote in this Unit i'm using TCOMPONENT ANCESTOR for Abstracted Base Class in-order to get TComponentName for the Object that will Instantiate from TBaseAbstract Class in my App Demo Main Form i have this: var FrmMain: TFrmMain; implementation uses UBase; var fConcrete: TConcrete; fBASE: TBaseAbstract; {$R *.dfm} procedure TFrmMain.FormCreate(Sender: TObject); begin fConcrete := TConcrete.New('i''m a Read Only PROPERTY of [ ', Self); end; procedure TFrmMain.FormDestroy(Sender: TObject); begin fConcrete.Free; end; procedure TFrmMain.Btn_Normal_EventClick(Sender: TObject); begin ShowMessage('i''m an Event of [ ' + Sender.ClassName +' ] '+ sLineBreak + ' And My Object Instance Name IS: [ ' + TComponent(Sender).Name +' ]'); end; procedure TFrmMain.Btn_Call_MethodClick(Sender: TObject); begin fBASE := TConcrete.Create(nil); try fBASE.Method(Btn_Normal_Event.OnClick); finally fBASE.Free; end; end; procedure TFrmMain.Btn_Call_Abstract_MethodClick(Sender: TObject); begin fConcrete.Method(Btn_Normal_Event.OnClick); end; procedure TFrmMain.Btn_Get_Concret_PropertyClick(Sender: TObject); begin ShowMessage(fConcrete.ReadOnlyProperty); end; end. Btn_Normal_EventClick work correctlly (i got the name of this button when i click on it [Btn_Normal_Event]) but from fConcrete or fBase whether using Abstract Method or GetProperty the NAME IS EMPTY !!! ------------ do i missing something ? Edited September 26, 2021 by bravesofts Share this post Link to post
bravesofts 6 Posted September 26, 2021 i test also the code for a Normal Class with ancestor TComponent the same Result !!! ------ why ? Share this post Link to post
Uwe Raabe 2057 Posted September 26, 2021 I am unsure what you expect, but in this code procedure TConcrete.Method(aObjectEvent: TNotifyEvent); begin aObjectEvent(Self) end; the parameter Self represents the instance of TConcrete. As long as you don't give a name to that instance like in this code sequence fBASE := TConcrete.Create(nil); try fBASE.Name := 'My Name'; // give fBASE a name fBASE.Method(Btn_Normal_Event.OnClick); finally fBASE.Free; end; the Name property remains empty. But as I already mentioned in the beginning: It is quite unclear what you expect. 1 Share this post Link to post
Guest Posted September 26, 2021 25 minutes ago, bravesofts said: why ? The answer is easy, and i assume you are low on caffeine ! 🙂 Anyway, try this change to get what you are missing constructor TConcrete.New(aValue: string; aOwner: TComponent); begin inherited Create(aOwner); Name := 'My_default_Name111111'; fReadOnlyProperty := aValue + Self.ClassName + ' ] ' + sLineBreak + 'Object Instance Name is: [' + Self.Name + ' ]'; Name := 'My_default_Name222222'; end; now you you can see the big picture, right ? as for Btn_Call_MethodClick, it will not change its name and will stay empty coming from its ancestors. Hope that helped. Share this post Link to post
bravesofts 6 Posted September 26, 2021 (edited) Quote fBASE.Name := 'My Name'; // give fBASE a name is it fBase the name of that Object fBase ? and fConcrete the name of fConcrete Object -------- for example the NORMAL Button when user click on it it will give him the TButton as ClassName and the Btn_Normal_Event as Object Name where the Default was Button1 in Delphi Edited September 26, 2021 by bravesofts Share this post Link to post
Uwe Raabe 2057 Posted September 26, 2021 No, it is the name of the variable that contains the object. The name of a TComponent instance can be different from the names of the variables containing that instance. 1 Share this post Link to post
Guest Posted September 26, 2021 5 minutes ago, bravesofts said: as Object Name where the Default was Button1 in Delphi That is not default name, that is a name added by the designer of your DFM at design time. Share this post Link to post
bravesofts 6 Posted September 26, 2021 (edited) thank you All for All this Infos ... ----- Really i'm not just low on caffeine :) i Missing one of the Pillar of delphi language hhh Edited September 26, 2021 by bravesofts Share this post Link to post
bravesofts 6 Posted September 26, 2021 (edited) i have another Kinda strange question: (Maybe a Stupid hhh :) ) why the components that we create at designtime can call them using their NAME's Rather Call them from their Variables Names that Contains them ? in runtime we can do this for ex: procedure CreateButton(aParent: TWinControl; aBtnEvent: TNotifyEvent); var vButton: TButton; begin vButton := TButton.Create(Self); try vButton.Parent := aParent; vButton.Name := 'Btn_Test'; vButton.SetBounds(10, 10, 100, 25); vButton.Caption := 'Test'; vButton.OnClick := aBtnEvent; finally vButton.Visible := True; end; end; whereas any Object created at designtime we call them using their Name !!! -------- what is the secret thing or the missing part that should i know (seriously) to understand the difference between an Object Name at DesignTime & Obj Name RunTime ? -------- i know Logically that we can't Call any Obj not yet created in designtime from his NAME because it is not reconized for our Project yet (Therefore, we used variables instead), whereas we can call Objects that was created at designTime just from their Names because our Project know them & Recognize them !!(so how did recognize them and use TComponentName which is a string by default as pointer or i don't know as whatever is !!!) ------- what i mean exactlly: what happen to any OBJ CREATED AT DESIGNTIME makes delphi & our project call them from thier Names instead of variables Names like the RunTime Scenario does ?? -------- i know that my question is trivial & stupid !!! but (seriously) i'm lost here (i know that i missing An essential thing that childrens know ....) plz clarify to me the Secret behind delphi backstage designtime objects !!! Notice: All Objects will at the end created Atruntime (so why designtime is a superbox than others !!! (is a designtime = a temporary or pre runtime section or what is the word that can describe this superbox better than designtime ???) ) -------- in another meaning: why TComponentName of any OBJECT from ObjectInspector IS totally different from TComponentName of Runtime of any Object !!! they are both a string (TComponentName = type string;) from System.Classes -------- thanks in advance Edited September 26, 2021 by bravesofts Share this post Link to post
Uwe Raabe 2057 Posted September 26, 2021 (edited) When a TComponent is inserted into another TComponent (like a TButton into a TForm) it tries to connect that component to a published field matching the name of the component. The responsible code part is the SetReference call in TComponent.InsertComponent. That is one reason why the Form Designer adds the component fields as published. Side note: You can even take advantage of this mechanism for your own needs. It is not restricted to the streaming system. Edited September 26, 2021 by Uwe Raabe 1 Share this post Link to post
Guest Posted September 26, 2021 28 minutes ago, bravesofts said: why the components that we create at designtime can call them using their NAME's Rather Call them from their Variables Names that Contains them ? We are not calling them by name, we never do call any anything by its name, but the form(frame/module) designer is managing them, so when you drop a component on Form/Frame/Module the designer will automatically declare this component as a var in the TForm/TFrame/TModule, and will try to keep its TComponent.Name in sync with that var, this is happening for convenience also it is easier for the designer to manage the component by their names without confusing you (the developer) between these fields vars (declared in TForm) and their names in the designer (dropped component ) , imagine if you drop a button on a form and it called CloseButton in the designer while in the code it is "Button3", also the designer will store each component published property (changed from the default ) in the DFM file. TComponent.Name is there for one thing only which to make a named component unique from its parent point of view, the VCL library designed this Name to make the visual designer more accurate, and to make it possible to for DFM to store properties properly. Share this post Link to post
bravesofts 6 Posted September 26, 2021 wow it seems a complicated backstage that the developer like me didn't know or even think about it previously !!! ------- i need to a respect time to understand exactly what happen inside that designtime backstage !!! thank you All for this Very valuable information ... Share this post Link to post
Guest Posted September 26, 2021 Try this type TForm9 = class(TForm) ... private // could be public or protected, it doesn't matter UnNamedBtn: TButton; procedure SomeNotifyClick(Sender: TObject); public { Public declarations } end; ... procedure TForm9.SomeNotifyClick(Sender: TObject); begin if Sender is TButton then begin ShowMessage(TButton(Sender).Name + sLineBreak + UnNamedBtn.Name); UnNamedBtn.Name := 'Name' + IntToStr(GetTickCount); end; end; procedure TForm9.FormCreate(Sender: TObject); begin // if you use nil instead of Self, here then we need to free that button or it will leak memory, // by assinging the owenr, the owner will destroy it when it destroy itself, (it will be a child for Self which is this form) UnNamedBtn := TButton.Create(Self); UnNamedBtn.Left := 150; UnNamedBtn.Top := 150; UnNamedBtn.Parent := Self; UnNamedBtn.Caption := 'UnNamed one'; UnNamedBtn.OnClick := SomeNotifyClick; end; click that button any number of times and see the name is changing. Share this post Link to post
David Schwartz 426 Posted September 27, 2021 10 hours ago, bravesofts said: wow it seems a complicated backstage that the developer like me didn't know or even think about it previously !!! ------- i need to a respect time to understand exactly what happen inside that designtime backstage !!! thank you All for this Very valuable information ... When Delphi first came out, seeing how this worked was one of those O-M-G! moments! If you've never programmed the old Windows event loops using Microsoft's crappy old "object framework" based on C++ but not really using objects, it's really hard to appreciate what the inventors did that actually hid all of that garbage. I don't think any of us really learned any of that "complicated backstage" because ... honestly, who cares? What it did was SIMPLIFY THE HELL out of creating forms in MS Windows apps. Not even VB was that elegant at first, because you had to write VB components in C++ due to the Windows part of that "complicated backstage". Delphi let you write components in ... Delphi! In large part because a TComponent had certain magical properties that let you drop it on a form and refer to everything in it as if it was part of the form. It was really ingenious, and so intuitive that nobody really gave it much thought. So coming at it the way you are, from the "run-time first", it's not surprising that you're confused. If you're going to do it that way, then study how to write components. That may confuse you even more at first, but at least it will get you going down the right road. That said, your naiveté might lead you to figure out something that the rest of us are blind to because we've been working with this for so long. The biggest annoyance I have is when I want to derive something from a common component, like a TLabel and make a TmyLabel with additional fields and methods. But you can't then inject it into the form easily because the IDE needs all components to be registered and installed, not just declared. I think I've seen some ways of doing it, but they've never stuck in my memory. So while this mechanism was almost miraculous when it was first introduced, it does have its limitations. 1 Share this post Link to post
M.Joos 30 Posted September 27, 2021 17 hours ago, Uwe Raabe said: Side note: You can even take advantage of this mechanism for your own needs. It is not restricted to the streaming system Out of curiosity (and lack of imagination this morning), what for might this mechanism be good for other than in the Designer ? 1 Share this post Link to post
Uwe Raabe 2057 Posted September 27, 2021 1 hour ago, M.Joos said: Out of curiosity (and lack of imagination this morning), what for might this mechanism be good for other than in the Designer ? Well, I don't have a practical example at hand in the moment and some academic ones would be of no value. Nonetheless, I will try to think of some, but as always there will be other ways to achieve the same goal - whatever that will be. 1 Share this post Link to post
Lars Fosdal 1792 Posted September 27, 2021 One thing about component names is that you f.x. cannot dynamically add multiple identical frames to a form at runtime unless you ensure a unique name by doing something like this: type TOurFrameBase = class(TFrame) constructor Create(const AOwner: TWinControl); reintroduce; end; constructor TOurFrameBase.Create(const AOwner: TWinControl); begin inherited Create(TComponent(AOwner)); FPanel := AOwner; Parent := AOwner; Align := alClient; Name := ClassName + '_' + Seed.Next.ToString; // Ensure that every frame created gets a unique name I.e. we ensure a unique name for the frame instance. (Seed is a singleton for the class that delivers a unique (incrementing) cardinal (per app life time)). If you don't do this - you will run into duplicate name errors. 1 Share this post Link to post
Uwe Raabe 2057 Posted September 27, 2021 35 minutes ago, Lars Fosdal said: cannot dynamically add multiple identical frames to a form at runtime unless you ensure a unique name In case you don't need the name you can also assign it to an empty string. Unnamed components can coexist inside their owner. 2 Share this post Link to post
Remy Lebeau 1398 Posted September 27, 2021 On 9/26/2021 at 12:29 AM, bravesofts said: constructor TConcrete.New(aValue: string; aOwner: TComponent); begin inherited Create(aOwner); fReadOnlyProperty := aValue + Self.ClassName +' ] '+ sLineBreak + 'Object Instance Name is: [' + Self.Name +' ]'; end; Self.Name will never work in this situation, since it has not been assigned a value yet while the constructor is still running. The Name property is assigned a value after the constructor exits, whether that be at design-time or run-time. The only way around that is to pass in the desired value as a parameter to the constructor (but that will obviously not work for objects created at desing-time), eg: constructor TConcrete.New(aValue: string; aName: string; aOwner: TComponent); begin inherited Create(aOwner); Self.Name := aName; // <-- fReadOnlyProperty := aValue + Self.ClassName +' ] '+ sLineBreak + 'Object Instance Name is: [' + aName +' ]'; end; 1 Share this post Link to post
Lars Fosdal 1792 Posted September 28, 2021 16 hours ago, Uwe Raabe said: In case you don't need the name you can also assign it to an empty string. Unnamed components can coexist inside their owner. That I did not know. Thank you, Uwe! 1 Share this post Link to post