Delphi – indexed properties
Posted by jpluimers on 2009/07/23
Sometimes, indexed properties can make your life much easier: they allow you to redirect single properties to central Get/Set methods.
You don’t see it used very often, but when you need it, it is very handy.
There is even some online help on it (look for “Index Specifiers”) it gives you the basics, but no compiling example.
For me it works best when I have a complete, but concise example.
So here is one: TMyIndexedPropertyComponent that has property Range1Value through Range4Value of type TRange, all centralized to one pair of read/write methods: GetRangeValue and SetRangeValue.
First the type of the property itself: TRange (and the array: TRanges).
unit RangesUnit; interface const MinRangeIndex = 0; MaxRangeIndex = 3; type TRange = record Value: Integer; function Equals(const Range: TRange): Boolean; end; TRanges = array[MinRangeIndex..MaxRangeIndex] of TRange; implementation { TRange } function TRange.Equals(const Range: TRange): Boolean; begin Result := Self.Value = Range.Value; end; end.
Then the TMyIndexedPropertyComponent:
unit MyIndexedPropertyComponentUnit; interface uses SysUtils, Classes, ExtCtrls, RangesUnit; type TMyIndexedPropertyComponent = class(TPaintBox) strict private FRanges: TRanges; strict protected function GetRangeValue(Index: Integer): Integer; virtual; procedure SetRangeValue(Index: Integer; const Value: Integer); virtual; procedure SetRangeAndRepaint(Index: Integer; Range: TRange); virtual; procedure SetRanges(const Value: TRanges); virtual; public constructor Create(aOwner: TComponent); override; property Range1Value: Integer index 0 read GetRangeValue write SetRangeValue; property Range2Value: Integer index 1 read GetRangeValue write SetRangeValue; property Range3Value: Integer index 2 read GetRangeValue write SetRangeValue; property Range4Value: Integer index 3 read GetRangeValue write SetRangeValue; property Ranges: TRanges read FRanges write SetRanges; end; implementation { TMyIndexedPropertyComponent } constructor TMyIndexedPropertyComponent.Create(aOwner: TComponent); var Range1: TRange; Range2: TRange; Range3: TRange; Range4: TRange; begin inherited; Width := 260; Height := 160; Range1.Value := 5; Range1.Value := 15; Range2.Value := 85; Range3.Value := 110; Range4.Value := 175; FRanges[0] := Range1; FRanges[1] := Range2; FRanges[2] := Range3; FRanges[3] := Range4; end; function TMyIndexedPropertyComponent.GetRangeValue(Index: Integer): Integer; var Range: TRange; begin Range := Self.Ranges[Index]; Result := Range.Value; end; procedure TMyIndexedPropertyComponent.SetRangeValue(Index: Integer; const Value: Integer); var Range: TRange; begin Range := Self.Ranges[Index]; if Range.Value <> Value then begin Range.Value := Value; SetRangeAndRepaint(Index, Range); end; end; procedure TMyIndexedPropertyComponent.SetRangeAndRepaint(Index: Integer; Range: TRange); begin Self.FRanges[Index] := Range; //jpl: not: Ranges[Index]!! (you will assign a copy if you do) Repaint(); end; procedure TMyIndexedPropertyComponent.SetRanges(const Value: TRanges); var Equals: Boolean; Index: Integer; begin Equals := True; for Index := MinRangeIndex to MaxRangeIndex do Equals := Equals and FRanges[Index].Equals(Value[Index]); if not Equals then begin FRanges := Value; Repaint; end; end; end.
And finally an example on how to use both the Ranges property and the individual Range1Value through Range4Value properties:
program IndexedPropertyDemoProject; {$APPTYPE CONSOLE} uses SysUtils, RangesUnit in 'RangesUnit.pas', MyIndexedPropertyComponentUnit in 'MyIndexedPropertyComponentUnit.pas'; var MyIndexedPropertyComponent: TMyIndexedPropertyComponent; Ranges: TRanges; begin try MyIndexedPropertyComponent := TMyIndexedPropertyComponent.Create(nil); try Ranges := MyIndexedPropertyComponent.Ranges; MyIndexedPropertyComponent.Ranges := Ranges; MyIndexedPropertyComponent.Range1Value := 1; MyIndexedPropertyComponent.Range1Value := 2; MyIndexedPropertyComponent.Range1Value := 3; MyIndexedPropertyComponent.Range1Value := 4; finally MyIndexedPropertyComponent.Free; end; except on E:Exception do Writeln(E.Classname, ': ', E.Message); end; end.
uligerhardt said
“There is _even_ some online help” for a language feature – that’s funny.
jpluimers said
(: