The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

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.

One Response to “Delphi – adding for … in support to TPageControl”

  1. […] is another case where I’d like to use the for … in statement: accessing the Components of a TComponent […]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.