Wednesday, February 06, 2008

TWinControl.Controls Enumerator, Revisited

Fredrik Loftheim made an interesting observation to my previous article on TWinControl.Controls enumerator - namely that the enumerator factory doesn't have to be interface based. It can be replaced by a record. Source is almost the same as before, but we can skip the interface declaration. Generated code is simpler as the enumerator factory is simply allocated from the stack and automatically destroyed when it goes out of scope. No interface support and no reference counting is required. Simpler and faster, that's the way to go.


TControlEnumerator = record
strict private
ceClass : TClass;
ceIndex : integer;
ceParent: TWinControl;
constructor Create(parent: TWinControl; matchClass: TClass);
function GetCurrent: TControl;
function MoveNext: boolean;
property Current: TControl read GetCurrent;
end; { TControlEnumerator }

TControlEnumeratorFactory = record
strict private
cefClass : TClass;
cefParent: TWinControl;
constructor Create(parent: TWinControl; matchClass: TClass);
function GetEnumerator: TControlEnumerator;
end; { TControlEnumeratorFactory }

function EnumControls(parent: TWinControl; matchClass: TClass = nil): TControlEnumeratorFactory;


function EnumControls(parent: TWinControl; matchClass: TClass = nil): TControlEnumeratorFactory;
Result := TControlEnumeratorFactory.Create(parent, matchClass);
end; { EnumControls }

{ TControlEnumerator }

constructor TControlEnumerator.Create(parent: TWinControl; matchClass: TClass);
ceParent := parent;
ceClass := matchClass;
ceIndex := -1;
end; { TControlEnumerator.Create }

function TControlEnumerator.GetCurrent: TControl;
Result := ceParent.Controls[ceIndex];
end; { TControlEnumerator.GetCurrent }

function TControlEnumerator.MoveNext: boolean;
Result := false;
while ceIndex < (ceParent.ControlCount - 1) do begin
if (ceClass = nil) or (ceParent.Controls[ceIndex].InheritsFrom(ceClass)) then begin
Result := true;
break; //while
end; //while
end; { TControlEnumerator.MoveNext }

{ TControlEnumeratorFactory }

constructor TControlEnumeratorFactory.Create(parent: TWinControl; matchClass: TClass);
cefParent := parent;
cefClass := matchClass;
end; { TControlEnumeratorFactory.Create }

function TControlEnumeratorFactory.GetEnumerator: TControlEnumerator;
Result := TControlEnumerator.Create(cefParent, cefClass);
end; { TControlEnumeratorFactory.GetEnumerator }

The new version of the TWinControl.Controls enumerator plus some other stuff is available at

Labels: , , , ,


Blogger David Glassborow said...

Also why not add the function to the TWinControl class using a class helper, would make the code using it look nicer.

Blogger gabr said...

I'm trying not to use class helpers too much, mainly because of the weird implementation which only allows one active helper per class.

And in this case, there's not much difference between typing EnumControls(control) and control.EnumControls.

Blogger HeartWare said...

You can use the syntax

THelperClass2 = CLASS HELPER(THelperClass1) FOR TClassToBeHelped

to have more than one active CLASS HELPER for a given class...

Blogger gabr said...

I'm aware of that.

The problem occurs when you have two class helpers for TWinControl in two different units, written by different authors. Then you have to manually change one of them to descend from the other class helper - and you have to do it every time the unit changes.

In my opinion a simple function is better and more stable.


Post a Comment

Links to this post:

Create a Link

<< Home