Ich würde hier das Visitor-Pattern verwenden. Dazu habe ich mal einen Artikel geschrieben, der die Hintergründe ausführlich beleuchtet und ein paar Lösungsansätze aufzeigt. Im
4.Teil des Artikels wird eine Implementation beschrieben, die ich bei dieser Lösung verwendet habe. Als Beispiel habe ich ein Form mit einem DbEdit und einem DbText verwendet. Beide führen das DataSource-Property parallel ein und man kann somit nicht auf einen gemeinsamen Vorfahren zugreifen.
Hier zunächst der Basis-Visitor:
Delphi-Quellcode:
unit uVisitor;
interface
type
TVisitor =
class
public
procedure Visit(Instance: TObject);
virtual;
end;
implementation
uses
Rtti;
procedure TVisitor.Visit(Instance: TObject);
var
context: TRttiContext;
currentClass: TClass;
params: TArray<TRttiParameter>;
paramType: TRttiType;
selfMethod: TRttiMethod;
S:
string;
begin
context := TRttiContext.Create;
currentClass := Instance.ClassType;
repeat
S := currentClass.ClassName;
Delete(S, 1, 1);
// remove "T"
for selfMethod
in context.GetType(self.ClassType).GetMethods('
Visit' + S)
do begin
params := selfMethod.GetParameters;
if (Length(params) = 1)
then begin
paramType := params[0].ParamType;
if ParamType.IsInstance
and (ParamType.AsInstance.MetaclassType = currentClass)
then begin
selfMethod.Invoke(Self, [Instance]);
Exit;
end;
end;
end;
currentClass := currentClass.ClassParent;
until currentClass =
nil;
end;
end.
Und hier die abgeleitete Klasse mit den Implementationen für die verschiedenen Controls. Weitere Controls (z.B. TDBAdvEdit) kann man durch einfaches Hinzufügen einer
procedure VisitDBAdvEdit(Instance: TDBAdvEdit);
abdecken:
Delphi-Quellcode:
unit uVisitorDbState;
interface
uses
uVisitor,
Vcl.DBCtrls, Data.DB;
type
TVisitorDbState =
class(TVisitor)
private
FDataSource: TDataSource;
public
procedure VisitDbEdit(Instance: TDbEdit);
procedure VisitDbText(Instance: TDbText);
property DataSource: TDataSource
read FDataSource
write FDataSource;
end;
implementation
uses
Vcl.Graphics;
procedure TVisitorDbState.VisitDbEdit(Instance: TDbEdit);
begin
if Instance.DataSource = DataSource
then begin
case DataSource.State
of
dsEdit: Instance.Color := clYellow;
dsInsert: Instance.Color := clGreen;
else
Instance.Color := clWindow;
end;
end;
end;
procedure TVisitorDbState.VisitDbText(Instance: TDbText);
begin
if Instance.DataSource = DataSource
then begin
case DataSource.State
of
dsEdit: Instance.Font.Style := [fsBold];
dsInsert: Instance.Font.Style := [fsItalic];
else
Instance.Font.Style := [];
end;
end;
end;
end.
Hier die Verwendung des Visitors. Natürlich kann man auch die Iteration über die Controls durchführen, statt über die Components:
Delphi-Quellcode:
procedure TForm188.AdjustStateColor;
var
I: Integer;
visitor: TVisitorDbState;
begin
visitor := TVisitorDbState.Create;
try
visitor.DataSource := DataSource1;
for I := 0 to ComponentCount - 1 do
visitor.Visit(Components[I]);
finally
visitor.Free;
end;
end;
procedure TForm188.DataSource1StateChange(Sender: TObject);
begin
AdjustStateColor;
end;