Overview
Txyz = class(Twhatever) private FmyProperty : Integer ; // This defines the "Field" variable // (That's why it starts with 'F') public constructor Create(AOwner: TComponent); override; published property myProperty : Integer read FmyProperty write FmyProperty; end;There are many variations on this
All properties should be assigned initial values in the component's overridden Create method.
Properties with a pick list
Txyz_WriteMode = (mcWM_Update, mcWM_Generate, mcWM_AddSection) ; Txyz = class(Twhatever) private FmyProperty : Txyz_WriteMode ; // This defines the "Field" variable published property myProperty : Txyz_WriteMode read FmyProperty write FmyProperty; end;
TStrings
Several components (such as TMemo and TRichEdit) have a property of this type. However ... because TStrings is an abstract class you can not CREATE a property of this type.
The secret (based on examining code and the help example for TStrings.Assign) is to define the property to be of type TStrings, but to create the property using TStringList (because it implements the TStrings abstract methods). Since the component creates the TStringList, be sure that it also Frees it. The other part of the secret is that you must Assign values to the property.
Txyz = class(Twhatever) private FmyProperty : TStrings; procedure SetmyProperty(Value: TStrings); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property myProperty : TStrings read FmyProperty write SetmyProperty; end; constructor Txyz.Create(AOwner: TComponent); begin inherited; FmyProperty := TStringList.Create; // Notice - not TStrings.Create !! end; destructor Txyz.Destroy; begin FmyProperty.Free; // Program leaks memory without this inherited; end; // Txyz.Destroy procedure Txyz.SetmyProperty(Value: TStrings); begin FmyProperty.Assign(Value); end;Warning: If you define the property to simply read and write a TStrings field, you will get wierd bpl failures (Access violation ... in ... VCL50.bpl). Once data is assigned to the property, you will not be able to save, compile, or close applications that use the component. The following line causes this type of problem.
property myProperty : TStrings read FmyProperty write FmyProperty;Note: To type a tab character in a TStrings property, use ctrl-Tab.
By default, TMemo sets the Text property default value and TRichEdit sets the Lines property default value to the name of the component, but I have not been able to figure out how that works.
Using Functions as Properties
TMouseEvent = procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer) of object; TControl = class(TComponent) private FOnMouseDown: TMouseEvent; protected procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); dynamic; property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown; end; procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y); end;Connecting the event to a Windows message requires additional code similar to this.
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;Sometimes, Delphi uses a protected DoXyz to execute events (from forms.pas).
procedure TCustomForm.DoHide; begin if Assigned(FOnHide) then FOnHide(Self); end;
Some Pre-defined Events
From classes.pas
TNotifyEvent = procedure(Sender: TObject) of object;Controls.pas - contains 18 common windows events
Arrays
This is how it is defined.
function GetHeaderAsInteger(Name: string): Integer; procedure SetHeaderAsInteger(Name: string; const Value: Integer); property HeaderAsInteger[Name:string] : Integer read GetHeaderAsInteger write SetHeaderAsInteger; function TJCAMP.GetHeaderAsInteger(Name: string): Integer; var s : string; i, code : integer; begin s := FHeader.Values[Name]; val(s, i, code); if code <> 0 then begin i := 0; if s='' then ErrorText := 'Not in header - ' + Name else ErrorText := 'Not an integer - ' + Name + ' = ' + s; end else begin ErrorText := ''; end; result := i; end; procedure TJCAMP.SetHeaderAsInteger(Name: string; const Value: Integer); var s : string; begin s := IntToStr(Value); FHeader.Values[Name] := s; end;This is how it is called
var i : integer; begin JCAMP1.HeaderAsInteger['FindThis'] := 6; i := JCAMP1.HeaderAsInteger['FindThis']; end; // This is called when ErrorText is set procedure TJCAMP.SetErrorText(const Value: String); begin FErrorText := Value; if Value <> '' then begin if Assigned(OnConversionError) then OnConversionError(Self); end; end;
Default Property Values
published property Line: TLineType read FLine write SetLine default ltLeftToRight; constructor TLine.Create(AOwner : TComponent); begin inherited; line := ltLeftToRight; end;
If the default directive is omitted, all values are stored in the *.dfm file. When present, if the property's value matches the default, it is not stored.
You can not use the default directive with strings. In this case, any string, except null (''), is automatically stored in the *.dfm file.
Testing for Design Time
// Without this test, the IDE exe (Delphi32.exe) is placed // in the "Filename" property at design time // when you would prefer it to remain blank if not (csDesigning in ComponentState) then if (Filename='') or (Filename='.') then begin s := Application.ExeName; Filename := ChangeFileExt(s, '.ini'); end;
Updating the component