![]() |
Delphi-Version: XE4
Klasseninstanz zur Laufzeit bestimmen
Hi zusammen
Ich habe eine Klasse, der ich (Schrift-)Attribute zuweisen kann, die ich per eigenem Frame einstelle. Den Frame habe ich ![]()
Delphi-Quellcode:
Instanzen dieser Klasse erstelle ich so:
TAttributsClass = Class(TPersistent)
public BackGround : TColor; ForeGround : TColor; StyleBold: Boolean; StyleItalic : Boolean; StyleUnderLine : Boolean; StyleStrikeOff : Boolean; AttributName: String; end;
Delphi-Quellcode:
Der erste Parameter bezeichnet dabei die Instanz, die erzeugt werden soll, der zweite den String, der in der Combobox angezeigt wird.
constructor TCssAttriTLBXFrame.Create(AOwner: TComponent);
begin inherited; FCssAttributsList := TDataObjectList<TAttributsClass>.Create(); FCssAttributsList.OwnsObjects := True; CreateAttribute(FCommentAttri, 'Kommentare'); CreateAttribute(FPropertyAttri, 'Eigenschaften'); CreateAttribute(FKeyAttri, 'Schlüsselworte'); CreateAttribute(FSpaceAttri, 'Leerzeichen'); CreateAttribute(FStringAttri, 'Strings'); CreateAttribute(FColorAttri, 'Farben'); CreateAttribute(FNumberAttri, 'Zahlen'); CreateAttribute(FSymbolAttri, 'Symbole'); CreateAttribute(FTextAttri, 'Text'); CreateAttribute(FValueAttri, 'Werte'); CreateAttribute(FUndefPropertyAttri, 'Undefinierte Eigenschaften'); CreateAttribute(FImportantPropertyAttri, 'Wichtige Eigenschaften'); end; Nun geht es darum, die Werte, die ich zB. aus einer Colorbox auslese, im Closeup per Event weiterzureichen. Gefeuert werden die Events, wenn ein Wert gewählt wurde, zB. wenn eine Checkbox geklickt wird oder beim CloseUp einer Colorbox:
Delphi-Quellcode:
Die Eventpropertys sind wie folgt deklariert:
procedure TCssAttriTLBXFrame.CmbxValueForegroundColorCloseUp(Sender: TObject);
begin FActiveAttribut := TAttributsClass(CmbxAttributes.Items.Objects[CmbxAttributes.ItemIndex]); Label1.Caption := FActiveAttribut.Name; // if Assigned(FActiveAttribut) then // FActiveAttribut(FActiveAttribut); end;
Delphi-Quellcode:
Nun brauche ich ja beim Feuern des Events den Eventtip, der gefeuert werden soll. Bloss: Wie bestimme ich den? Dazu brauche ich ja die Instanz meiner Attributklasse.
property CSSCommentEvent: TCSSCommentEvent read FCSSCommentEvent write FCSSCommentEvent;
property CssPropertyEvent: TCssPropertyEvent read FCssPropertyEvent write FCssPropertyEvent; property CssKeyEvent: TCssKeyEvent read FCssKeyEvent write FCssKeyEvent; property CssSpaceEvent: TCssSpaceEvent read FCssSpaceEvent write FCssSpaceEvent; property CssStringEvent: TCssStringEvent read FCssStringEvent write FCssStringEvent; property CssColorEvent: TCssColorEvent read FCssColorEvent write FCssColorEvent; property CssNumberEvent: TCssNumberEvent read FCssNumberEvent write FCssNumberEvent; property CssSymbolEvent: TCssSymbolEvent read FCssSymbolEvent write FCssSymbolEvent; property CssTextEvent: TCssTextEvent read FCssTextEvent write FCssTextEvent; property CssValueEvent: TCssValueEvent read FCssValueEvent write FCssValueEvent; property CssUndefPropertyEvent: TCssUndefPropertyEvent read FCssUndefPropertyEvent write FCssUndefPropertyEvent; property CssImportantPropertyEvent: TCssImportantPropertyEvent read FCssImportantPropertyEvent write FCssImportantPropertyEvent; Gruss Delbor |
AW: Klasseninstanz zur Laufzeit bestimmen
Wieso sind deine Eventhandler eigentlich alle unterschiedlich deklariert? Reicht nicht ein Typ?, z.B.
Delphi-Quellcode:
Na ja, wie Du meinst. Wenn das so wäre, dann reicht folgender Code.
TAttributEvent = Procedure (Sender : TObject; Attribut : TAttributsClass) of Object;
Delphi-Quellcode:
Ja, das ist eine ziemlich lange if-else-Folge. Sieht blöd aus, ist aber normal. Da Deine Eventhandler alle individuell deklariert sind, kannst Du das nette 'CallEventHandler' nicht verwenden, sondern musst die 'If Assigned(Event)' Abfrage für jeden Event neu implementieren.
Function TMyForm.CreateEvent(Attribut : TAttributsClass) : TAttributEvent;
begin if Attribut=FCommentAttri then result := FCSSCommentEvent else if Attribut=FPropertyAttri then Result := FCSSPropertyEvent else if ... ... else Raise Exception.Create('Unknown Attribut: '+Attribut.Name); end; Procedure TMyForm.FireEvent(Attribut : TAttributsClass); Var Event : TAttributEvent; begin Event := CreateEvent(Attribut); CallEventHandler(Event, Attribut); end; Procedure TMyForm.CallEventHandler (Event : TAttributEvent; Attribut : TAttributsClass); Begin if Assigned (Event) Then Event(Self, Attribut); end; Noch einfacher geht es übrigens mit einem einfachen
Delphi-Quellcode:
, denn wenn das 'CommentEvent' gefeuert wird, ist ja klar, das mit den CommitAttributen etwas los ist, ergo muss man das Attribut nicht übergeben.
TNotifyEvent
Delphi-Quellcode:
Function TMyForm.CreateEvent(Attribut : TAttributsClass) : TNotifyEvent;
begin if Attribut=FCommentAttri then result := FCSSCommentEvent else if Attribut=FPropertyAttri then Result := FCSSPropertyEvent else if ... ... else Raise Exception.Create('Unknown Attribut: '+Attribut.Name); end; Procedure TMyForm.FireEvent(Attribut : TAttributsClass); Var Event : TNotifyEvent; begin Event := CreateEvent(Attribut); CallEventHandler(Event); end; Procedure TMyForm.CallEventHandler (Event : TNotifyEvent); Begin if Assigned (Event) Then Event(Self); end; |
AW: Klasseninstanz zur Laufzeit bestimmen
Nun ja, bei XE4 gibt es natürlich auch schon nettere Arten, damit umzugehen
Delphi-Quellcode:
unit Unit1;
interface uses {System.}Generics.Collections, {System.}SysUtils; type TObjectHandler = class abstract private type TAction = TProc<TObject>; private FRoutes: TDictionary<TClass, TAction>; protected type TRoute<T> = procedure( Argument: T ) of object; protected procedure RegisterRoute<T: class>( ARoute: TRoute<T> ); procedure DoRaise( Argument: TObject ); end; TFoo = class end; TBar = class end; TFooBar = class( TObjectHandler ) private procedure Apply( Argument: TFoo ); overload; procedure Apply( Argument: TBar ); overload; public constructor Create( ); procedure Handle( Argument: TObject ); end; implementation { TObjectHandler } procedure TObjectHandler.DoRaise( Argument: TObject ); var LAction: TAction; begin if FRoutes.TryGetValue( Argument.ClassType, LAction ) then LAction( Argument ) else raise ENotImplemented.CreateFmt( 'Handler für %s fehlt', [ Argument.ClassName ] ); end; procedure TObjectHandler.RegisterRoute<T>( ARoute: TRoute<T> ); begin FRoutes.Add( T, procedure( Argument: TObject ) begin ARoute( Argument as T ); end ); end; { TFooBar } procedure TFooBar.Apply( Argument: TFoo ); begin end; procedure TFooBar.Apply( Argument: TBar ); begin end; constructor TFooBar.Create; begin inherited Create; RegisterRoute<TFoo>( Apply ); RegisterRoute<TBar>( Apply ); end; procedure TFooBar.Handle( Argument: TObject ); begin DoRaise( Argument ); end; end. |
AW: Klasseninstanz zur Laufzeit bestimmen
Zu sehr verwirren wollte ich den TE nun auch nicht.
'Case' (oder hier: if/else-Schlangen) sind zwar 'böse', aber in Fabrikmethoden durchaus erlaubt. Denn es ist ja nun kein Mehrwert ggü dem if/else bzw. 'case' (geht hier leider nicht), eine 1:1 Abbildung in eine Liste (oder Dictionary) zu stopfen. Obwohl.. bei endlose if/else-Wicklungen würde ich nicht drauf beharren. |
AW: Klasseninstanz zur Laufzeit bestimmen
Wenn ich CleanCode haben möchte, dann nehmen ich so ein Routing-Dictionary.
(Gerade fällt mir auf, dass die Basis-Klasse keinen Destruktor hat ... ts ts ts) |
AW: Klasseninstanz zur Laufzeit bestimmen
Hi zusammen
Vielen Dank für eure Antworten! @Dejan Vu:
Delphi-Quellcode:
Hier entspricht FActiveAttribut deiner Variablen 'Attribut' in der Prozedur CreateEvent. Als ClassName wird mir hier allerdings TAttributClass zurückgegeben. Gebe ich im Label FActiveAttribut.AttributName aus, erhalte ich allerdings den von mir an die Instanz übergebenen String. Das bedeutet aber auch: die Info über die Klasseninstanz ist in meiner Feldvariablen enthalten - das zeigt ja auch dein Code mit der Abfrage:
procedure TCssAttriTLBXFrame.CmbxAttributesCloseUp(Sender: TObject);
begin FActiveAttribut := TAttributsClass(CmbxAttributes.Items.Objects[CmbxAttributes.ItemIndex]); Label1.Caption := FActiveAttribut.ClassName; //FActiveAttribut.AttributName; CmbxAttributes.Items[CmbxAttributes.ItemIndex]; end;
Delphi-Quellcode:
Deshalb denke ich, die Info ist im Property 'Tipinfo' von Tpersistent-Nachfolgern enthalten. Die Frage ist (oder war) nur: wie komme ich da ran?
if Attribut=FCommentAttri then result := FCSSCommentEvent
Zitat:
Delphi-Quellcode:
Ein Auszug aus der dfm:
fCommentAttri: TSynHighlighterAttributes;
fPropertyAttri: TSynHighlighterAttributes; fKeyAttri: TSynHighlighterAttributes; fSpaceAttri: TSynHighlighterAttributes; fStringAttri: TSynHighlighterAttributes; fColorAttri: TSynHighlighterAttributes; fNumberAttri: TSynHighlighterAttributes; fSymbolAttri: TSynHighlighterAttributes; fTextAttri: TSynHighlighterAttributes; fValueAttri: TSynHighlighterAttributes; fUndefPropertyAttri: TSynHighlighterAttributes; fImportantPropertyAttri: TSynHighlighterAttributes;
Delphi-Quellcode:
Das sind die Werte, die meine Klasse übergeben muss.
AndAttri.Background = clRed
CommentAttri.Foreground = clOlive CommentAttri.Style = [fsBold, fsItalic] IdentifierAttri.Foreground = clBlue IdentifierAttri.Style = [fsBold, fsUnderline] KeyAttri.Foreground = clBlue SpaceAttri.Foreground = clMoneyGreen SpaceAttri.Style = [fsUnderline] SymbolAttri.Foreground = clGray TextAttri.Foreground = clRed TextAttri.Style = [fsItalic] UndefKeyAttri.Background = clYellow ValueAttri.Background = clSilver
Delphi-Quellcode:
Das heisst: im Eventhandler, der diesen Event entgegennimmt, muss ich erstmal die übergebene Instanz identifizieren. Also eigentlich das Gegenstück zu deiner Prozedur CreateEvent schreiben und dann an passender Stelle die Zuweisung machen - pro identifizierter Instanz je einmal. Oder ich kann die 'passende Stelle' in jeweils eine eigene Prozedur auslagern. Das wären dann 9 - 12 Zuweisungsproceduren, gleichviel, wie einzelne Eventhandler nötig wären.
Procedure TMyForm.CallEventHandler (Event : TAttributEvent; Attribut : TAttributsClass);
Begin if Assigned (Event) Then Event(Self, Attribut); end; Einzig bei den 'Kanonieren' (die das Event abfeuern) wäre was einzusparen. Inzwischen habe ich deinen neuen Beitrag mitbekommen, Dejan Vu. Zitat:
Zitat:
Gruss Delbor |
AW: Klasseninstanz zur Laufzeit bestimmen
Nein, mein
Delphi-Quellcode:
hat keinen, obwohl dort das Dictionary aufgeräumt werden muss.
TObjectHandler
|
AW: Klasseninstanz zur Laufzeit bestimmen
Zitat:
Zitat:
|
AW: Klasseninstanz zur Laufzeit bestimmen
Zitat:
Man kanns übertreiben... |
AW: Klasseninstanz zur Laufzeit bestimmen
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:22 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz