////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unit clRTTI;
{
Author: Chris Lichti
Email: chlichti@
February 2, 2001
clRTTI is a utility unit that wraps Delphi's Run-Time Type Information in
a set of easy-to-use classes:
TrtWrapper
This class takes a class or object in its constructor and provides access to
properties in a manner similar to TTable. For example, here is the code to
make every control on a form that can support a Popup menu use PopupMenu1:
var
RTObj: TrtWrapper;
i: integer;
begin
for i := 0 to ComponentCount - 1 do
begin
RTObj := TrtWrapper.Create(Components[i]);
if RTObj.HasProperty('PopupMenu') then
RTObj['PopupMenu'].AsObject := PopupMenu1;
RTObj.Free;
end;
end;
And here is the code to get strings for the enumeration values of the Align
property of any TControl. Notice that no created instance is required for this:
var
Prop: TrtProperty;
i: integer;
begin
Prop := TrtProperty.Create( TControl, 'Align' );
for i := 0 to Prop.EnumNames.Count - 1 do
begin
ShowMessage( Prop.EnumNames[i] );
end;
end;
TrtProperty
This class represents an individual published property of a class or object.
It provides a simple interface for checking attributes of the property, and it
provides convenient and familiar 'AsSomething' properties to get and set the
Property's value. Here's an extension of the previous example, this time with
TrtProperty:
var
RTObj: TrtWrapper;
Prop: TrtProperty;
i, j: integer;
begin
for i := 0 to ComponentCount - 1 do
begin
RTObj := TrtWrapper.Create(Components[i]);
for j := 0 to RTObj.Count - 1 do
begin
Prop := RTObj.Items[j];
//set all fonts to match the form font, regardless of property name
if Prop.Kind = tkClass then
begin
if Prop.PropClassType.InheritsFrom( TFont ) then
Prop.AsObject := Font;
end;
end;
RTObj.Free;
end;
end;
}
interface
uses typinfo, Classes, SysUtils, contnrs;
type
{
TrtProperty is a class the represents a single property's RTTI.
}
TrtProperty = class
protected
FEnumNames: TStringList;
FSetNames: TStringList;
function GetAsString: string;
function GetAsFloat: extended;
function GetAsInt64: int64;
function GetAsInteger: integer;
function GetAsMethod: TMethod;
function GetAsObject: TObject;
function GetAsVariant: Variant;
function GetName: string;
function GetValue: Variant;
procedure SetAsFloat( const Value: extended );
procedure SetAsInt64( const Value: int64 );
procedure SetAsInteger( const Value: integer );
procedure SetAsMethod( const Value: TMethod );
procedure SetAsObject( const Value: TObject );
procedure SetAsString( const Value: string );
procedure SetAsVariant( const Value: Variant );
procedure SetValue( const Value: Variant );
function GetTypeKind: TTypeKind;
function GetEnumNames: TStrings;
function GetSetNames: TStrings;
function GetPropClassType: TClass;
function GetIsDelegate: boolean;
function GetIsStored: boolean;
public
Instance: TObject;
ObjClassType: TClass;
TypeData: PTypeData;
TypeInfo: PTypeInfo;
PropInfo: PPropInfo;
constructor Create( APropInfo: PPropInfo ); overload; virtual;
constructor Create( AObject: TObject; propName: string ); overload; virtual;
constructor Create( AClass: TClass; propName: string ); overload; virtual;
destructor Destroy; override;
//Getting and Setting the Property's Value
property AsString: string read GetAsString write SetAsString;
property AsInteger: integer read GetAsInteger write SetAsInteger;
property AsInt64: int64 read GetAsInt64 write SetAsInt64;
property AsFloat: extended read GetAsFloat write SetAsFloat;
property AsMethod: TMethod read GetAsMethod write SetAsMethod;
property AsObject: TObject read GetAsObject write SetAsObject;
property AsVariant: Variant read GetAsVariant write SetAsVariant;
property Value: Variant read GetValue write SetValue;
//Direct access to commonly used type information
property Name: string read GetName;
property Kind: TTypeKind read GetTypeKind;
property EnumNames: TStrings read GetEnumNames;
property SetNames: TStrings read GetSetNames;
function SetHasMember( MemberStr: string ): boolean;
property PropClassType: TClass read GetPropClassType;
property IsDelegate: boolean read GetIsDelegate;
property IsStored: boolean read GetIsStored;
//A helper function to help determine how many characters would be
// needed to store all possible values of the property. This may
// only be useful for Enumeration and Set types.
function MaxStringLen: integer;
end;
{
TrtPropertyList is a container class for TrtProperty instances. It helps
TrtWrapper (below) keep track of and clean up after instances of
TrtPropertyList.
}
TrtPropertyList = class( TObjectList )
private
function GetItem( Index: Integer ): TrtProperty;
procedure SetItem( Index: Integer; const Value: TrtProperty );
public
constructor Create; virtual;
function Add( ArtPropObj: TrtProperty ): Integer;
function Remove( ArtPropObj: TrtProperty ): Integer;
property Items[Index: Integer]: TrtProperty read GetItem write SetItem; default;
end;
{
TrtWrapper
TrtWrapper is an object that is meant to parallel any object instance or class
to providing random access to any of the published property type information
of that instance or class.
This class uses RTTI to navigate the published
properties of an instance or class. I encapsulates the logic of
navigating specific property kinds (VisiblePropKinds), while avoiding
properties of specific names (StopNames)
}
TrtWrapper = class
private
procedure SetStopNames( const Value: TStrings );
procedure SetVisiblePropKinds( const Value: TTypeKinds );
function GetStopNames: TStrings;
protected
FVisiblePropKinds: TTypeKinds;
FStopNames: TStringList;
FPropObjects: TrtPropertyList;
function NextAllIndexFromAllIndex( AllIndex: integer ): integer; virtual;
function CreatePropertyObject( Index: integer ): TrtProperty;
function GetPropertyObject( Index: integer ): TrtProperty;
function GetItemByName( Name: string ): TrtProperty;
function GetItemByIndex( index: integer ): TrtProperty;
property PropertyObjects[index: integer]: TrtProperty read GetPropertyObject;
public
AllCount, Count: integer;
ObjClass: TClass;
ObjInstance: TObject;
ObjTypeData: PTypeData;
ObjTypeInfo: PTypeInfo;
ObjPropList: PPropList;
VisiblePropInfos: array of PPropInfo;
constructor Create; overload; virtual;
constructor Create( AClass: TClass; PropKinds: TTypeKinds = [] ); overload; virtual;
constructor Create( AObject: TObject; PropKinds: TTypeKinds = [] ); overload; virtual;
destructor Destroy; override;
procedure Initialize; virtual;
property VisiblePropKinds: TTypeKinds read FVisiblePropKinds write SetVisiblePropKinds;
property StopNames: TStrings read GetStopNames write SetStopNames;
function HasProperty( PropertyName: string ): boolean;
property ItemsByName[Name: string]: TrtProperty read GetItemByName; default;
property Items[index: integer]: TrtProperty read GetItemByIndex;
end;
//Global Utility Functions
// Find a component in the scope of all forms and data modules.
// (Adapted from Borland's hidden version)
function rtFindGlobalComponent( const Name: string ): TComponent;
// Access a singleton TList of all the components in the scope of all forms and data modules.
function rtGlobalComponentList: TList;
// Convert a type kind into its string representation.
function TypeKindToStr( Kind: TTypeKind ): string;
resourcestring
ERR_NOINSTANCE = 'This method cannot be called without an object instance. ' +
'Construct the RTTI object by passing an object instance to the constructor.';
implementation
uses Forms;
var
FrtGlobalComponentList: TList; //Singleton accessed by rtGlobalComponentList
//---------- Internal Utility Functions ------------
{
GetSetString
Since GetSetProp does not initialize Result to an empty string, I must do so
for it. silly bug...
}
function GetSetString( Obj: TObject; pInfo: PPropInfo; Brackets: boolean ): string;
begin
SetLength( Result, 0 );
Result := GetSetProp( Obj, pInfo, Brackets );
end;
{
GetEnumNamesFromTypeInfo
Fill the TStrings with the enumeration names described in TypeInfo's TypeData
}
procedure GetEnumNamesFromTypeInfo( TypeInfo: PTypeInfo; sl: TStrings );
var
TypeData: PTypeData;
nValue: integer;
begin
sl.Clear;
TypeData := GetTypeData( TypeInfo );
for nValue := TypeData.MinValue to TypeData.MaxValue do
begin
sl.AddObject( GetEnumName( TypeInfo, nValue ), TObject( nValue ) );
end;
end;
//---------- Global Utility Functions ------------
{
rtFindGlobalComponent
Find a component in the scope of all forms and data modules.
(Adapted from Borland's hidden version)
}
function rtFindGlobalComponent( const Name: string ): TComponent;
var
I: Integer;
begin
for I := 0 to Screen.FormCount - 1 do
begin
Result := Screen.Forms[I];
if not ( csInline in Result.ComponentState ) and
( CompareText( Name, Result.Name ) = 0 ) then Exit;
Result := Result.FindComponent( Name );
if Assigned( Result ) then Exit;
end;
for I := 0 to Screen.DataModuleCount - 1 do
begin
Result := Screen.DataModules[I];
if CompareText( Name, Result.Name ) = 0 then Exit;
Result := Result.FindComponent( Name );
if Assigned( Result ) then Exit;
end;
Result := nil;
end;
{
rtGlobalComponentList
Access a singleton TList of all the components in the scope of all forms and
data modules.
}
function rtGlobalComponentList: TList;
var
nContainer, nComp: Integer;
Comp: TComponent;
List: TList;
procedure AddComp( AComp: TComponent );
begin
if List.IndexOf( AComp ) < 0 then
List.Add( AComp );
end;
begin
if not Assigned( FrtGlobalComponentList ) then
begin
FrtGlobalComponentList := TList.Create;
end;
List := FrtGlobalComponentList;
List.Clear;
for nContainer := 0 to Screen.FormCount - 1 do
begin
Comp := Screen.Forms[nContainer];
AddComp( Comp );
for nComp := 0 to Comp.ComponentCount - 1 do
begin
AddComp( Comp.Components[nComp] );
end;
end;
for nContainer := 0 to Screen.DataModuleCount - 1 do
begin
Comp := Screen.DataModules[nContainer];
AddComp( Comp );
for nComp := 0 to Comp.ComponentCount - 1 do
begin
AddComp( Comp.Components[nComp] );
end;
end;
Result := List;
end;
{
TypeKindToStr
Convert a type kind into its string representation.
}
function TypeKindToStr( Kind: TTypeKind ): string;
begin
case Kind of
tkUnknown: Result := 'tkUnknown';
tkInteger: Result := 'tkInteger';
tkChar: Result := 'tkChar';
tkEnumeration: Result := 'tkEnumeration';
tkFloat: Result := 'tkFloat';
tkString: Result := 'tkString';
tkSet: Result := 'tkSet';
tkClass: Result := 'tkClass';
tkMethod: Result := 'tkMethod';
tkWChar: Result := 'tkWChar';
tkLString: Result := 'tkLString';
tkWString: Result := 'tkWString';
tkVariant: Result := 'tkVariant';
tkArray: Result := 'tkArray';
tkRecord: Result := 'tkRecord';
tkInterface: Result := 'tkInterface';
tkInt64: Result := 'tkInt64';
tkDynArray: Result := 'tkDynArray';
end;
end;
{ TrtWrapper }
{ TrtProperty }
constructor TrtProperty.Create( AObject: TObject; propName: string );
var
APropInfo: PPropInfo;
begin
Instance := AObject;
ObjClassType := AObject.ClassType;
APropInfo := GetPropInfo( AObject, propName );
Create( APropInfo );
end;
constructor TrtProperty.Create( APropInfo: PPropInfo );
begin
PropInfo := APropInfo;
TypeInfo := APropInfo^.PropType^;
TypeData := GetTypeData( TypeInfo );
FEnumNames := nil;
FSetNames := nil;
end;
function TrtProperty.MaxStringLen: integer;
function TotalLenWithCommas( sl: TStrings ): integer;
var
nStr: integer;
begin
Result := 0;
for nStr := 0 to sl.Count - 1 do
Result := Result + length( sl[nStr] ) + 1;
end;
function MaxStrLen( sl: TStrings ): integer;
var
nStr: integer;
begin
Result := 0;
for nStr := 0 to sl.Count - 1 do
if length( sl[nStr] ) > Result then
Result := length( sl[nStr] );
end;
begin
Result := -1;
case Kind of
tkSet: Result := TotalLenWithCommas( SetNames );
tkEnumeration: Result := MaxStrLen( EnumNames ) + 1;
end;
end;
constructor TrtProperty.Create( AClass: TClass; propName: string );
var
APropInfo: PPropInfo;
begin
APropInfo := GetPropInfo( AClass, propName );
ObjClassType := AClass;
Create( APropInfo );
end;
destructor TrtProperty.Destroy;
begin
if Assigned( FEnumNames ) then FEnumNames.Free;
if Assigned( FSetNames ) then FSetNames.Free;
inherited;
end;
function TrtProperty.GetAsFloat: extended;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetFloatProp( Instance, PropInfo );
end;
function TrtProperty.GetAsInt64: int64;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetInt64Prop( Instance, PropInfo );
end;
function TrtProperty.GetAsInteger: integer;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
case Kind of
tkEnumeration: Result := GetEnumValue( TypeInfo, GetEnumProp( Instance, PropInfo ) );
else
Result := GetOrdProp( Instance, PropInfo );
end;
end;
function TrtProperty.GetAsMethod: TMethod;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetMethodProp( Instance, PropInfo );
end;
function TrtProperty.GetAsObject: TObject;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetObjectProp( Instance, PropInfo );
end;
function TrtProperty.GetAsString: string;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
case Kind of
tkEnumeration: Result := GetEnumProp( Instance, PropInfo );
tkSet: Result := GetSetString( Instance, PropInfo, true );
else
Result := GetStrProp( Instance, PropInfo );
end;
end;
function TrtProperty.GetAsVariant: Variant;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetVariantProp( Instance, PropInfo );
end;
function TrtProperty.GetEnumNames: TStrings;
var
nValue: integer;
begin
if not Assigned( FEnumNames ) then
begin
FEnumNames := TStringList.Create;
for nValue := TypeData.MinValue to TypeData.MaxValue do
begin
FEnumNames.Add( GetEnumName( TypeInfo, nValue ) );
end;
end;
Result := FEnumNames;
end;
function TrtProperty.GetIsDelegate: boolean;
begin
Result := TypeData.ClassType.InheritsFrom( TPersistent ) and
( not TypeData.ClassType.InheritsFrom( TComponent ) );
end;
function TrtProperty.GetIsStored: boolean;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := IsStoredProp( Instance, PropInfo );
end;
function TrtProperty.GetName: string;
begin
Result := PropInfo^.Name;
end;
function TrtProperty.GetPropClassType: TClass;
resourcestring
ERR_MUSTBETKCLASS = 'PropClassType property is only valid for tkClass properties';
begin
Assert( Kind = tkClass, ERR_MUSTBETKCLASS );
Result := TypeData.ClassType;
end;
function TrtProperty.SetHasMember( MemberStr: string ): boolean;
resourcestring
ERR_MUSTBETKSET = 'SetHasMember property is only valid for tkSet properties';
var
nMemberIdx: integer;
slSetValue: TStringList;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Assert( Kind = tkSet, ERR_MUSTBETKSET );
Result := false;
slSetValue := nil;
try
slSetValue := TStringList.Create;
nMemberIdx := SetNames.IndexOf( MemberStr );
if nMemberIdx < 0 then
raise Exception.CreateFmt( 'SetHasMember: Unknown Member String (%s)', [MemberStr] );
slSetValue.CommaText := Copy(AsString, 2, length(AsString) - 2);
Result := (slSetValue.IndexOf(MemberStr) > -1);
finally
slSetValue.Free;
end;
end;
function TrtProperty.GetSetNames: TStrings;
var
enumTypeInfo: PTypeInfo;
begin
if not Assigned( FSetNames ) then
begin
FSetNames := TStringList.Create;
enumTypeInfo := TypeData^.CompType^;
if enumTypeInfo^.Kind = tkEnumeration then
GetEnumNamesFromTypeInfo( enumTypeInfo, FSetNames );
end;
Result := FSetNames;
end;
function TrtProperty.GetTypeKind: TTypeKind;
begin
Result := TypeInfo^.Kind;
end;
function TrtProperty.GetValue: Variant;
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
Result := GetPropValue( Instance, Name );
end;
procedure TrtProperty.SetAsFloat( const Value: extended );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetFloatProp( Instance, PropInfo, Value );
end;
procedure TrtProperty.SetAsInt64( const Value: int64 );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetInt64Prop( Instance, PropInfo, Value );
end;
procedure TrtProperty.SetAsInteger( const Value: integer );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
case Kind of
tkEnumeration: SetEnumProp( Instance, PropInfo, GetEnumName( TypeInfo, Value ) );
else
SetOrdProp( Instance, PropInfo, Value );
end;
end;
procedure TrtProperty.SetAsMethod( const Value: TMethod );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetMethodProp( Instance, PropInfo, Value );
end;
procedure TrtProperty.SetAsObject( const Value: TObject );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetObjectProp( Instance, PropInfo, Value );
end;
procedure TrtProperty.SetAsString( const Value: string );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
case Kind of
tkEnumeration: SetEnumProp( Instance, PropInfo, Value );
tkSet: SetSetProp( Instance, propInfo, Value );
else
SetStrProp( Instance, PropInfo, Value );
end;
end;
procedure TrtProperty.SetAsVariant( const Value: Variant );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetVariantProp( Instance, PropInfo, Value );
end;
procedure TrtProperty.SetValue( const Value: Variant );
begin
Assert( Instance <> nil, ERR_NOINSTANCE );
SetPropValue( Instance, Name, Value );
end;
{ TrtWrapper }
constructor TrtWrapper.Create( AClass: TClass; PropKinds: TTypeKinds );
begin
ObjClass := AClass;
ObjInstance := nil;
ObjTypeInfo := AClass.ClassInfo;
FVisiblePropKinds := PropKinds;
Create;
end;
constructor TrtWrapper.Create( AObject: TObject; PropKinds: TTypeKinds );
begin
ObjInstance := AObject;
ObjClass := ObjInstance.ClassType;
ObjTypeInfo := AObject.ClassInfo;
FVisiblePropKinds := PropKinds;
Create;
end;
constructor TrtWrapper.Create;
begin
FStopNames := TStringList.Create;
FPropObjects := TrtPropertyList.Create;
ObjPropList := nil;
Initialize;
end;
{
CreatePropertyObject
Creates a TrtProperty for the specified AllIndex. If an instance is available,
it uses the instance to create the TrtProperty so that properties of that
instance can be manipulated. If no instance is available, then it tries to
create the TrtProperty with a class. If no class is available, then it just
uses the raw propInfo from the ObjPropList.
}
function TrtWrapper.CreatePropertyObject( Index: integer ): TrtProperty;
var
propInfo: PPropInfo;
propName: string;
begin
propInfo := VisiblePropInfos[Index];
propName := propInfo^.Name;
if Assigned( ObjInstance ) then
FPropObjects[Index] := TrtProperty.Create( ObjInstance, propName )
else if Assigned( ObjClass ) then
FPropObjects[Index] := TrtProperty.Create( ObjClass, propName )
else
FPropObjects[Index] := TrtProperty.Create( propInfo );
Result := FPropObjects[Index];
end;
destructor TrtWrapper.Destroy;
begin
FStopNames.Free;
FPropObjects.Free;
if Assigned( ObjPropList ) then
FreeMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );
inherited;
end;
function TrtWrapper.GetItemByName( Name: string ): TrtProperty;
var
nProp: integer;
begin
Result := nil;
for nProp := 0 to Count - 1 do
begin
if CompareText( PropertyObjects[nProp].Name, Name ) = 0 then
begin
Result := PropertyObjects[nProp];
break;
end;
end;
end;
function TrtWrapper.GetItemByIndex( index: integer ): TrtProperty;
begin
Result := PropertyObjects[index];
end;
function TrtWrapper.GetStopNames: TStrings;
begin
Result := FStopNames;
end;
function TrtWrapper.HasProperty( PropertyName: string ): boolean;
begin
Result := IsPublishedProp( ObjClass, PropertyName );
end;
procedure TrtWrapper.Initialize;
var
nHandledCount: integer;
nAProp: integer;
begin
//Clean up buffers from previous calls to Initialize...
if Assigned( ObjPropList ) then
begin
FreeMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );
ObjPropList := nil;
end;
FPropObjects.Clear;
//Prepare buffer for a new property list
ObjTypeData := GetTypeData( ObjTypeInfo );
GetMem( ObjPropList, ObjTypeData^.PropCount * SizeOf( PPropInfo ) );
AllCount := GetPropList( ObjTypeInfo, tkAny, ObjPropList );
SetLength( VisiblePropInfos, AllCount );
//Calculate the count of the properties that are VisiblePropKinds and not in
// StopNames. The logic for this is in NextAllIndexFromAllIndex
nHandledCount := 0;
nAProp := NextAllIndexFromAllIndex( -1 );
while nAProp > -1 do
begin
VisiblePropInfos[nHandledCount] := ObjPropList[nAProp];
FPropObjects.Add( nil ); //Place holder for the Property Object
inc( nHandledCount );
nAProp := NextAllIndexFromAllIndex( nAProp );
end;
SetLength( VisiblePropInfos, nHandledCount );
Count := nHandledCount;
end;
{
NextAllIndexFromAllIndex
Horrible name, huh? The idea here is the indexes passed in and returned from
this method are relative to the entire list of properties, not just the ones
that are "visible". Visible properties are properties that satisfy these
conditions:
- The property's Kind is in VisiblePropKinds
- The property's Name is not in StopNames
}
function TrtWrapper.NextAllIndexFromAllIndex( AllIndex: integer ): integer;
var
propName: string;
nAllProp: integer;
propInfo: PPropInfo;
begin
nAllProp := AllIndex + 1;
Result := -1;
while nAllProp < AllCount do
begin
propInfo := ObjPropList^[nAllProp];
propName := ObjPropList^[nAllProp]^.Name;
if FStopNames.IndexOf( LowerCase( propName ) ) = -1 then
if ( VisiblePropKinds = [] ) or
( propInfo^.PropType^.Kind in VisiblePropKinds ) then
begin
Result := nAllProp;
break;
end;
inc( nAllProp );
end;
end;
{
GetPropertyObject
Gets the TrtProperty at the specified AllIndex. Creates it if necessary.
}
function TrtWrapper.GetPropertyObject(
Index: integer ): TrtProperty;
begin
Result := FPropObjects[Index];
if not Assigned( Result ) then
begin
Result := CreatePropertyObject( Index );
end;
end;
procedure TrtWrapper.SetStopNames( const Value: TStrings );
var
nName: integer;
begin
FStopNames.Assign( Value );
for nName := 0 to FStopNames.Count - 1 do
FStopNames[nName] := LowerCase( FStopNames[nName] );
Initialize;
end;
procedure TrtWrapper.SetVisiblePropKinds( const Value: TTypeKinds );
begin
FVisiblePropKinds := Value;
Initialize;
end;
{ TrtPropertyList }
function TrtPropertyList.Add( ArtPropObj: TrtProperty ): Integer;
begin
Result := inherited Add( ArtPropObj );
end;
constructor TrtPropertyList.Create;
begin
inherited;
OwnsObjects := true;
end;
function TrtPropertyList.GetItem( Index: Integer ): TrtProperty;
begin
Result := TrtProperty( inherited Items[index] );
end;
function TrtPropertyList.Remove( ArtPropObj: TrtProperty ): Integer;
begin
Result := inherited Remove( ArtPropObj );
end;
procedure TrtPropertyList.SetItem( Index: Integer;
const Value: TrtProperty );
begin
inherited Items[index] := Value;
end;
end.
For more information, see
http://chlichti.home./