Delphi – adding for … in support to TPageControl
Posted by jpluimers on 2009/04/22
A long time ago, the for ... in statement was added to the structured statements part of the Delphi language. It is a really powerful statement, that allows for code like this:
var
Line: string;
begin
for Line in MyMemo.Lines do
// some business logic
end;
in stead of using the traditional for statement which needs an extra LineIndex variable and an assignment statement:
var
LineIndex: Integer;
Line: string;
begin
for LineIndex := 0 to MyMemo.Lines.Count do
begin
Line := MyMemo.Lines[LineIndex];
// some business logic
end;
end;
So, “for … in” is a cool feature, but now I wanted to do the same for a TPageControl:
procedure TContactLensRadiiExpressionsForm.FormCreate(Sender: TObject);
var
TabSheet: TTabSheet;
begin
for TabSheet in RadiiPageControl do
TabSheet.TabVisible := False;
end;
In order to do that, TPageControl needs a function called GetEnumerator which returns an instance of an enumerator type that assists the for ... in (lets call it TPageControlEnumerator).
But: TPageControl is part of the VCL, and it is a no-no to change the VCL yourself.
(A long time ago, I was at a client where a programmer thought to be smart and extended the VCL by changing the Delphi VCL files; that effectively made it a tour-de-force to upgrade to a more recent Delphi version).
But, wait: in the same time frame (the DCCIL .NET commandline compiler preview), another feature was added to the Delphi language as well: class helpers (which now have been extended to record helpers and both have been back-ported to the Win32 compiler in Delphi 2005).
And this helps!
I have been a long-time fan of class helpers (especially because it allowed me to develop .NET Compact Framework applications using Delphi for .NET), and they come in really handy this time as well.
Complete code is at the end of this blog post, but first lets explain the fundamentals:
The TPageControlHelper implements a GetEnumerator function which returns an instance of the actual class that does the enumeration: TPageControlEnumerator.
Then TPageControlEnumerator implements a Current property and MoveNext method. And of course a Create constructor:
type
TPageControlEnumerator = class(TObject)
public
constructor Create(const FPageControl: TPageControl);
function GetCurrent: TTabSheet;
function MoveNext: Boolean;
property Current: TTabSheet read GetCurrent;
end;
TPageControlHelper = class helper for TPageControl
public
function GetEnumerator: TPageControlEnumerator;
end;
The important thing to remember here is that the TPageControlEnumerator instance must be initialized to the state before the first call of MoveNext.
In this case, this means we keep an FValue variable that is initialized one less than FMinValue. Technically, FMinValue is not needed, but it is easier for debugging purposes.
MoveNext then increases FValue and returns a boolean indicating if it was successful.
The rest then is just filling in the blanks.
Usage is easy: just add “PageControlHelperUnit” to your uses list, and you can use “for … in” on TPageControls.
This idea is of course straight forward to implement in other places as well (command-line parameters anyone?).
unit PageControlHelperUnit;
interface
uses
ComCtrls;
type
TPageControlEnumerator = class(TObject)
strict private
FMinValue: Integer;
FMaxValue: Integer;
FValue: Integer;
FPageControl: TPageControl;
public
constructor Create(const FPageControl: TPageControl);
destructor Destroy; override;
function GetCurrent: TTabSheet;
function MoveNext: Boolean;
property Current: TTabSheet read GetCurrent;
end;
TPageControlHelper = class helper for TPageControl
public
function GetEnumerator: TPageControlEnumerator;
end;
implementation
{ TPageControlHelper }
function TPageControlHelper.GetEnumerator: TPageControlEnumerator;
begin
Result := TPageControlEnumerator.Create(Self);
end;
{ TPageControlEnumerator }
constructor TPageControlEnumerator.Create(const FPageControl: TPageControl);
begin
Assert(Assigned(FPageControl), 'cannot enumerate nil FPageControl');
inherited Create();
Self.FPageControl := FPageControl;
FMinValue := 0;
FMaxValue := FPageControl.PageCount - 1;
FValue := FMinValue - 1;
end;
destructor TPageControlEnumerator.Destroy;
begin
inherited Destroy();
end;
function TPageControlEnumerator.GetCurrent: TTabSheet;
begin
Result := FPageControl.Pages[FValue];
end;
function TPageControlEnumerator.MoveNext: Boolean;
begin
Inc(FValue);
Result := FValue <= FMaxValue;
end;
end.






Delphi - class helper to add for … in support for TComponent.Components/ComponentCount « The Wiert Corner said
[…] is another case where I’d like to use the for … in statement: accessing the Components of a TComponent […]