Getting component name in constructor?

I am creating a custom control derived from TCustomControl, for example:

type TMyCustomControl = class(TCustomControl) private FText: string; procedure SetText(const Value: string); protected procedure Paint; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property Text: string read FText write SetText; end; 

Please note that the above is incomplete for this example to be short and simple.

Anyway, in my control, I have a Paint event that displays text (from the FText field) using Canvas.TextOut.

When my component is added to the Delphi form constructor (before the user can make changes to the component), I want TextOut to display the name Component-TButton, TCheckBox, TPanel, etc., are examples of this with their property header.

If I try to assign the name of my component to FText in the constructor, it will be empty, for example '' ;

 constructor TMyCustomControl.Create(AOwner: TComponent); begin inherited Create(AOwner); FText := Name; //< empty string ShowMessage(Name); //< empty message box too end; 

If I change FText := Name to FText := 'Name'; , it outputs the text to my Component, so I know that this is not a problem in the actual code, but obviously it outputs the "Name" and not the actual name of the component, for example MyCustomControl1, MyCustomControl2, etc.

So my question is: how can you get the name of your component from your constructor event?

+4
source share
4 answers

The Name property is not yet assigned when the constructor starts. During development, the IDE assigns a value to the Name property after the component has been dropped into the constructor, after the control constructor exits. At run time, the Name property is set in place of the DFM streaming system, which is also called after the constructor exits.

In any case, the TControl.SetName() property setter checks the new value and then sets the new value to the Text control property to match if the current Text value matches the old Name value and the ControlStyle control contains the csSetCaption flag (which it does by default). When the Text property changes for any reason, the control automatically sends a CM_TEXTCHANGED notification. You can make your control catch this message and call Invalidate() to start a new redraw. Inside your Paint() handler, simply draw the current Name as it is, regardless of its value. If it is empty, so be it. Do not try to force Name , let VCL process it for you usually.

+5
source

I believe the correct way to handle this is to use the inherited Text or Caption TCustomControl and make sure csSetCaption ControlStyle .

+5
source

To apply the name, you can override the TComponent.Loaded method.

But I do not think you need to copy the name into the text. These are semantically separate properties, and adding an unexpected binding to them will one day hurt you.

Rather, the WMPaint method should check if the text is empty and then render the Name, but the Text property itself should not be changed.

 procedure TMyComponent.WMPaint; message WM_Paint; var InternalCaption: string; begin .... InternalCaption := Self.Text; If InternalCaption = '' then InternalCaption := Self.Name; If InternalCaption = '' then InternalCaption := Self.ClassName; .... Self.Canvas.OutText(InternalCaption); 

If anything, you should leave the properties separated for the simple reason that Name := 'AAA'; Name := 'BBB'; Name := 'AAA'; Name := 'BBB'; should not contain text and the name should not be synchronized. And with your approach, the 1st operator will resolve the text, and the second will make the old name still visible after changing the actual name.

0
source

A tricky way to override the SetName method:

 TMyCaptionString = type of WideString; TMyLabel = class(TCustomControl) private FCaption: TMyCaptionString; FCaptionAsName: Boolean; procedure SetCaption(Value: TMyCaptionString); protected procedure SetName(const NewName: TComponentName); override; public constructor Create(AOwner: TComponent); override; property Caption: TMyCaptionString read FCaption write SetCaption; end; implementation constructor TMyLabel.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := ControlStyle + [csOpaque, csReplicatable,csSetCaption]; FCaptionAsName := (csDesigning in ComponentState) and not (csReadingState in ControlState); ... end; procedure TMyLabel.SetName(const NewName: TComponentName); begin if FCaptionAsName then begin FCaptionAsName := FCaption = Name; FCaption := NewName; invalidate; end; inherited SetName(NewName); end; procedure TMyLabel.SetCaption(Value: TMyCaptionString); begin if FCaption <> Value then begin FCaption := Value; Invalidate; FCaptionAsName := False; end; end; 

I needed my variable for pateprty Caption, because I want to use widestring instead of unicode and write my own property editor. Sorry that I am writing in an old thread, but hope this will be helpful.

0
source

All Articles