Delphi – class helper to add for … in support for TComponent.Components / ComponentCount
Posted by jpluimers on 2009/05/07
You might have discovered that I’m a fan of Delphi class helpers.
This is another case where I’d like to use the for … in statement: accessing the Components of a TComponent instance.
So I wanted to end up with this:
procedure MyForm.FormCreate(Sender: TObject); var Component: TComponent; begin for Component in Self do begin // business logic goes here end; end;
in stead of this:
procedure MyForm.FormCreate(Sender: TObject); var Component: TComponent; ComponentIndex: Integer; begin for ComponentIndex := 0 to ComponentCount - 1 do begin Component := Components[ComponentIndex]; // business logic goes here end; end; end;
As the core explanation is in my post on Delphi – adding for … in support to TPageControl I’ll just explain the main difference compared to that post:
function TComponentEnumerator.MoveNext: Boolean; begin Assert(FMaxValue = FComponent.ComponentCount - 1, 'ComponentCount changed during enumeration'); // ... end;
This extra assertion (which I forgot in the TPageControlEnumerator) is to protect against changes in the underlying components.
So this is the full code:
unit ComponentHelperUnit;
interface
uses
Classes;
type
TComponentEnumerator = class(TObject)
strict private
FMinValue: Integer;
FMaxValue: Integer;
FValue: Integer;
FComponent: TComponent;
public
constructor Create(const Component: TComponent);
function GetCurrent: TComponent;
function MoveNext: Boolean;
property Current: TComponent read GetCurrent;
end;
TComponentHelper = class helper for TComponent
public
function GetEnumerator: TComponentEnumerator;
end;
implementation
{ TComponentHelper }
function TComponentHelper.GetEnumerator: TComponentEnumerator;
begin
Result := TComponentEnumerator.Create(Self);
end;
{ TComponentEnumerator }
constructor TComponentEnumerator.Create(const Component: TComponent);
begin
Assert(Assigned(Component), ‘cannot enumerate nil FComponent’);
inherited Create();
Self.FComponent := Component;
FMinValue := 0;
FMaxValue := Component.ComponentCount – 1;
FValue := FMinValue – 1;
end;
function TComponentEnumerator.GetCurrent: TComponent;
begin
Result := FComponent.Components[FValue];
end;
function TComponentEnumerator.MoveNext: Boolean;
begin
Assert(FMaxValue = FComponent.ComponentCount – 1, ‘ComponentCount changed during enumeration’);
Inc(FValue);
Result := FValue <= FMaxValue;
end;
end.
[/sourcecode]
What you probably have guessed from my posts so far, is that I usually have small units.
This is to keep myself focussed, and to make reuse easier.
--jeroen
.NET Enums enumerated: System.ArgumentException was unhandled by user code Message=An item with the same key has already been added. « The Wiert Corner – irregular stream of Wiert stuff said
[…] Below is first the simplified code based on Enum.GetValues, which isn’t really nice because extension methods can only be run on instances (they can not be true static methods like class helpers in Delphi can): […]
Basil said
Hi,
I know we can ovrride a method using Class Helper as below :
TFormHelper = class helper for TCustomForm
public
end;
TFooHelper = class helper (TFormHelper) for TCustomForm
protected
procedure DoCreate; overload;
end;
implementation
procedure TFooHelper.DoCreate;
begin
inherited DoCreate;
end;
My doubt is : i have written above code to get fired for each form creation in my application. but it is not working correctly. can you please tell me what i miss here.. i wish the doCreate procedur of TCustom form get fired auto matically when a form creating in my application.
many thanks
jpluimers said
Since a class helper only works if it is visible, you have to make sure that for each form in your application, the TFormHelper is visible.
You usually do that by including the unit of TFormHelper in the uses list for every form in your application.
–jeroen
Delphi – for … in on enumerated data types « The Wiert Corner – Jeroen Pluimers’ irregular stream of Wiert stuff said
[…] always wondered why – since the for … in statement was added to the structured statements part of the Delphi language – it is not […]
Delphi – record and class helpers: an overview of useful linksoo « The Wiert Corner – Jeroen Pluimers’ irregular stream of Wiert stuff said
[…] The Wiert Corner: adding for … in support for TComponent.Components […]
Enumerators and overly-defensive programming « Delphi Haven said
[…] far seems fairly straightforward, but partly because of that, I’m inclined to take issue with this sort of […]
Delphi – more for … in support for TComponent.Components using a class helper « The Wiert Corner said
[…] Comments Edelklaus on Delphi – class helper to…jpluimers on Delphi – class helper to…Alister Christie on Delphi – class helper […]
Alister Christie said
I hope to make my next video on for..in loops on TDataSets (using Class Helpers) based on a unit in Code Central (and one of the presentations in CodeRage III).
jpluimers said
That would be really cool!
Drop me a note when you put that online so I can refer people to it.
Edelklaus said
+1
gabr said
TComponent already contains component-enumerating enumerator, at least in Delphi 2007.
There is no control-enumerating enumerator in TWinControl, though. Some time ago I used a similar technique to implement it: http://17slon.com/blogs/gabr/2008/02/twincontrolcontrols-enumerator.html
jpluimers said
I know, and it is good you are the first one to notice.
This blog was a first step to an enumerator that is a bit more complex.
What I actually wanted todo (a blog is scheduled for next week) is this:
for Component in Self.ComponentsOfType(TSQLQuery) do
(Component as TSQLQuery).SQL[1] := FTableName;
The solution is similar to what you did; so watch my blog next week :-)