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