Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Spezieller TFrame (https://www.delphipraxis.net/200091-spezieller-tframe.html)

hzzm 19. Mär 2019 10:15

Delphi-Version: 10 Seattle

Spezieller TFrame
 
Guten Morgen,

ich steh grad irgendwie auf dem Schlauch:

Gibt es eine Moeglichkeit, einen TFrame zu definieren, bei dessen Kindern klar ist, dass sie immer ein
Code:
Icon: TImage;
haben?

Im constructor einer Klasse, wo ich normalerweise einen TFrame erstelle, moechte ich einfach TMeinSpeziellerFrame neben der Angabe eines AIcon: TImage erstellen, so dass dem Frame das Icon direkt mitgegeben wird:

Code:
TMyFrameClass = class(TFrame)
  Icon: TImage;
end;

TMyClass = class
public
  FMyFrameList: TObjectDictionary<String, TMyFrameClass>;
  constructor Create(AOwner: TComponent; AIcon: TImage);
end;

implementation

constructor TMyClass.Create(AOwner: TComponent; AIcon: TImage);
begin
  FMyFrameList := TObjectDictionary<String, TMyFrameClass>.Create;
  FMyFrameList.Add('ExampleFrame', TMyFrameClass.Create);
  FMyFrameList['ExampleFrame'].Icon := AIcon;
end;
Das Urproblem ist also, dass ich den TFrame im Formdesigner in der .dfm nicht von TMyFrameClass ableiten kann.
Der muss erzwungenermassen von TFrame abgeleitet werden (nicht von dessen Nachfahren), sonst spackt die IDE und ich kann den Frame im designer nicht mehr oeffnen.

Wie wuerdet Ihr diese Sachlage loesen?

Der schöne Günther 19. Mär 2019 11:03

AW: Spezieller TFrame
 
Du tust dir keinen Gefallen damit das mit aller Gewalt in den Konstruktor drücken zu wollen. Die IDE weiß von deinem Spezial-Konstrukt nicht, sie arbeitet mit dem Constructor
Delphi-Quellcode:
Create(AOwner: TComponent)
der schon von
Delphi-Quellcode:
TComponent
(oder
Delphi-Quellcode:
TControl
?) vererbt wird.

Klar kannst du das machen, und deine Frames so über eigenen Code erstellen, einbauen und alles. Aber der Formular-Designer der IDE wird immer den Konstruktor
Delphi-Quellcode:
Create(AOwner: TComponent)
aufrufen.


Mach dir eine Basisklasse
Delphi-Quellcode:
TMyBaseFrame
der schonmal das
Delphi-Quellcode:
TImage
enthält. Du entdeckst später sicher noch mehr Gemeinsamkeiten die sich alle in eine Oberklasse auslagern lassen.

Wenn du jetzt einen speziellen Frame bauen willst leitest du den noch von
Delphi-Quellcode:
TFrame
ab, sondern von
Delphi-Quellcode:
TMyBaseFrame
(Datei -> Neu -> Weitere -> Vererbbare Elemente -> TMyBaseFrame).

hzzm 19. Mär 2019 11:07

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428066)
(Datei -> Neu -> Weitere -> Vererbbare Elemente -> TMyBaseFrame).

Volltreffer Guenther, das hab ich gebraucht.
Many thanks!

Der schöne Günther 19. Mär 2019 11:49

AW: Spezieller TFrame
 
Das war übrigens der umständliche Weg, schneller findest du deine Basisklassen-Frames (und Forms und Datenmodule) wenn du in die Toolpalette (Strg+Alt+P) schaust wenn der Formular-Designer nicht geöffnet ist. Dann einfach einen Eintrag daraus auswählen und schon hast du eine neue Ableitung von
Delphi-Quellcode:
TMyBaseFrame
erstellt.

hzzm 19. Mär 2019 12:22

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428080)
Das war übrigens der umständliche Weg, schneller findest du deine Basisklassen-Frames (und Forms und Datenmodule) wenn du in die Toolpalette (Strg+Alt+P) schaust wenn der Formular-Designer nicht geöffnet ist. Dann einfach einen Eintrag daraus auswählen und schon hast du eine neue Ableitung von
Delphi-Quellcode:
TMyBaseFrame
erstellt.

Zu frueh gefreut, ich will die abgeleiteten Frames ja nicht als einzelne unit haendisch erstellen.
Es geht ja darum, diese frames programmatisch zu erstellen und einfach ein Icon mitzugeben :(

Was mich wieder zu meiner Urspruenglichen Frage bringt:
Wie kann ich einen Type(TFrame) definieren, bei dem klar ist, dass er unbedingt ein Icon: TImage enthaelt, so dass ich dieses irgendwie programmatisch zuweisen kann?

hzzm 19. Mär 2019 12:27

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428066)
Du tust dir keinen Gefallen damit das mit aller Gewalt in den Konstruktor drücken zu wollen. Die IDE weiß von deinem Spezial-Konstrukt nicht, sie arbeitet mit dem Constructor
Delphi-Quellcode:
Create(AOwner: TComponent)

Okay, ja, aber der constructor Create oben soll auch nicht von dem TFrame Nachfahren sein. Der ist aus einem voellig anderen Objekt.
Der Frame selber kann ruhig den std-ctor haben

peterbelow 19. Mär 2019 14:45

AW: Spezieller TFrame
 
Zitat:

Zitat von hzzm (Beitrag 1428082)
Zitat:

Zitat von Der schöne Günther (Beitrag 1428080)
Das war übrigens der umständliche Weg, schneller findest du deine Basisklassen-Frames (und Forms und Datenmodule) wenn du in die Toolpalette (Strg+Alt+P) schaust wenn der Formular-Designer nicht geöffnet ist. Dann einfach einen Eintrag daraus auswählen und schon hast du eine neue Ableitung von
Delphi-Quellcode:
TMyBaseFrame
erstellt.

Zu frueh gefreut, ich will die abgeleiteten Frames ja nicht als einzelne unit haendisch erstellen.
Es geht ja darum, diese frames programmatisch zu erstellen und einfach ein Icon mitzugeben :(

Was mich wieder zu meiner Urspruenglichen Frage bringt:
Wie kann ich einen Type(TFrame) definieren, bei dem klar ist, dass er unbedingt ein Icon: TImage enthaelt, so dass ich dieses irgendwie programmatisch zuweisen kann?

Du kannst deiner Frame-Basisclasses durchaus einen Konstruktor verpassen, der neben dem Owner: TComponent noch einen weiteren Parameter für das zu verwendende Icon hat. Du mußt Dir nur klar darüber sein, dass dieser Konstruktor nur verwendet wird, wenn Du eine Frame-Instanz per Kode erzeugst und deinen Konstruktor explizit aufrufst. Wenn Du eine Instanz im Designer auf ein Form knallst wird unweigerlich der Standard TComponent-Konstruktor (oder dessen override) verwendet, da kannst Du nur ebenfalls im Designer das Icon laden oder halt in einer geeigneten Form-Methode zuweisen. So ein Icon fällt nicht vom Himmel, wenn Du die gleiche Frame-Klasse je nach Anwendung mit verschiedenen Icons verwenden willst geht nur auf eine der vorstehend genannten Wege.

Wenn Du allerdings einen festen Satz von Icons hast gibt es noch eine andere Möglichkeit. Da könntest Du der Basisklasse eine TImagelist mit dem vollen Satz der Icons verpassen, einen enumerated Type mit entsprechend vielen Elementen definieren, dem Frame eine published property dieses Typs mit einer Settermethode verpassen, und diese dann das gewünschte Icon aus der Imagelist in das Icon-TImage kopieren lassen.

hzzm 19. Mär 2019 15:42

AW: Spezieller TFrame
 
Zitat:

Zitat von peterbelow (Beitrag 1428088)
Du kannst deiner Frame-Basisclasses durchaus einen Konstruktor verpassen, der neben dem Owner: TComponent noch einen weiteren Parameter für das zu verwendende Icon hat. Du mußt Dir nur klar darüber sein, dass dieser Konstruktor nur verwendet wird, wenn Du eine Frame-Instanz per Kode erzeugst und deinen Konstruktor explizit aufrufst. Wenn Du eine Instanz im Designer auf ein Form knallst wird unweigerlich der Standard TComponent-Konstruktor (oder dessen override) verwendet, da kannst Du nur ebenfalls im Designer das Icon laden oder halt in einer geeigneten Form-Methode zuweisen. So ein Icon fällt nicht vom Himmel, wenn Du die gleiche Frame-Klasse je nach Anwendung mit verschiedenen Icons verwenden willst geht nur auf eine der vorstehend genannten Wege.

Wenn Du allerdings einen festen Satz von Icons hast gibt es noch eine andere Möglichkeit. Da könntest Du der Basisklasse eine TImagelist mit dem vollen Satz der Icons verpassen, einen enumerated Type mit entsprechend vielen Elementen definieren, dem Frame eine published property dieses Typs mit einer Settermethode verpassen, und diese dann das gewünschte Icon aus der Imagelist in das Icon-TImage kopieren lassen.

Der Konstruktor meiner "neuen" Frame-Klasse ist mir egal, der braucht kein Icon und gar nichts.
Die Idee ist, einen TMyBaseFrame in der IDE zu erstellen, der eine Member/Feldvariable Icon: TImage hat. Von dem will ich Frames ableiten (programmatisch erstellen), die leicht anders aufgebaut sind, aber auch immer ein Icon: TImage haben. Ueber die Klasse TMyBaseFrame sollte somit im code klar sein, dass sie immer dieses TMyBaseFrame.Icon: TImage; beinhaelt. Denn dann kann ich einfach generell bei so einem Frame auf
Code:
TMyCoolFrame = class(TMyBaseFrame)
...
AMyCoolFrame := TMyCoolFrame.Create(AOwner);
AMyCoolFrame.Icon := TImage(Whatever);
zugreifen.

Wenn ich also wie man es normal vermuten wuerde, einfach eine neue Klasse von TFrame ableite und ein Member Icon hinzufuege, war nicht moeglich, in der IDE im Code TMyCoolFrame = class(TFrame) abzuaendern in TMyCoolFrame = class(TMyBaseFrame), so dass bekannt ist, dass ein Icon enthalten ist und gleichzeitig trotzdem das Editieren des TFrame im FormEditor moeglich ist (F12).

Kurz gesagt wie in Post 1 geschrieben.
"war nicht moeglich", weil ich es mittlerweile geschafft habe. Was fuer ein Teufelsritt, ich weiss gar nicht, warum die IDE sich da so scheut :wall:

Rollo62 20. Mär 2019 07:44

AW: Spezieller TFrame
 
Als Gedankenanstoß:

Du könntest auch ein Interface dazunehmen, und so dein TFrame zwingen entsprechende Felder und Routinen anzulegen.

Delphi-Quellcode:
TMyCoolFrame = class(TFrame, IMyCoolFrame)
Das funktioniert dann auch im Designer wie gewohnt,
aber ich denke du möchtest von der Basisklasse ableiten eben um dir das Anlegen in jedem Frame zu sparen.

Der schöne Günther 20. Mär 2019 08:15

AW: Spezieller TFrame
 
Nur wenn man auf Zugriffsverletzungen und ähnliches steht.

Nie Interface- und TObject-basierte Referenzierung mischen :warn:

Schokohase 20. Mär 2019 08:16

AW: Spezieller TFrame
 
Was geht ist folgendes:

Wir erstellen uns manuell eine Ableitung von
Delphi-Quellcode:
TFrame
:
Delphi-Quellcode:
unit CustomFrameApp.Frames.MyBaseFrame;

interface

uses
  Vcl.ExtCtrls,
  Vcl.Forms;

type
  TMyBaseFrame = class(TFrame)
  private
    FIcon: TImage;
  published
    property Icon: TImage read FIcon write FIcon;
  end;

implementation

end.
Dann erstellen wir uns ein neues
Delphi-Quellcode:
TFrame
wie gehabt und verändern den Source leicht:
Delphi-Quellcode:
unit CustomFrameApp.Frames.MyFrame;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  // die Unit mit dem Vorgänger-Frame
  CustomFrameApp.Frames.MyBaseFrame;

// Umbiegen der Klasse TFrame auf die gewünschte Klasse
type
  TFrame = CustomFrameApp.Frames.MyBaseFrame.TMyBaseFrame;

type
  TMyFrame = class(TFrame)
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

implementation

{$R *.dfm}

end.

Schokohase 20. Mär 2019 08:19

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428135)
Nur wenn man auf Zugriffsverletzungen und ähnliches steht.

Nie Interface- und TObject-basierte Referenzierung mischen :warn:

So ein Unsinn ... in diesem Fall ist das genauso gefährlich als wenn ich die TFrame-Instanz direkt übergebe.

Das Problem, auf welches du dich beziehst ist dort, wo die Referenzzählung die Instanz aus dem Speicher kicken kann, da sollte man nur noch per Interface auf die Instanz zugreifen.

hzzm 20. Mär 2019 08:25

AW: Spezieller TFrame
 
Zitat:

Zitat von Schokohase (Beitrag 1428136)
Delphi-Quellcode:
// Umbiegen der Klasse TFrame auf die gewünschte Klasse
type
  TFrame = CustomFrameApp.Frames.MyBaseFrame.TMyBaseFrame;

Interessant, und der FormDesigner bei F12 frisst das Teil dann trotzdem wie gewohnt?
Muss ich mal testen. Mein Loesungsweg war dann doch ne Ecke umstaendlicher...

hzzm 20. Mär 2019 08:27

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428135)
Nur wenn man auf Zugriffsverletzungen und ähnliches steht.

Nie Interface- und TObject-basierte Referenzierung mischen :warn:

Soweit ich das verstanden habe, kriegst Du das Problem nur, wenn Du von TInterfacedObject erbst. Sonst findet keine Referenzgezaehlte Destruktion statt.

Schokohase 20. Mär 2019 08:29

AW: Spezieller TFrame
 
Das Problem ist, dass die IDE nach dem Namen der Vorgänger-Klasse schaut, wenn der Vorgänger keine DFM Datei hat.

Wenn dann der Name der Vorgänger-Klasse <>
Delphi-Quellcode:
TFrame
dann wird das Dingen wie ein
Delphi-Quellcode:
TForm
behandelt. Das ist das ganze Geheimnis.

Auf das gleiche Problem trifft man übrigens auch bei
Delphi-Quellcode:
TDataModule
(aber wahrscheinlich seltener, weil man da nicht so viel ableitet).

peterbelow 20. Mär 2019 12:01

AW: Spezieller TFrame
 
Zitat:

Zitat von hzzm (Beitrag 1428097)

Wenn ich also wie man es normal vermuten wuerde, einfach eine neue Klasse von TFrame ableite und ein Member Icon hinzufuege, war nicht moeglich, in der IDE im Code TMyCoolFrame = class(TFrame) abzuaendern in TMyCoolFrame = class(TMyBaseFrame), so dass bekannt ist, dass ein Icon enthalten ist und gleichzeitig trotzdem das Editieren des TFrame im FormEditor moeglich ist (F12).

Kurz gesagt wie in Post 1 geschrieben.
"war nicht moeglich", weil ich es mittlerweile geschafft habe. Was fuer ein Teufelsritt, ich weiss gar nicht, warum die IDE sich da so scheut :wall:

Ich hab dein Problem offenbar nicht richtig verstanden. Dir fehlte wirklich nur eine Kleinigkeit zum Erfolg: Bei Designerklassen fängt das DFM-File mit dem Keyword "object" an, wenn direkt von TForm, TFrame, oder TDatamodule abgeleitet wird. Wenn von einer anderen Klasse abgeleitet wird, die schon ein DFM-File hat (dein Fall) muss das DFM-File der abgeleiteten Klasse aber mit "inherited" anfangen, nicht mit "object". Wenn Du also den Ancestor im Editor manuell änderst mußt Du danach mal kurz "view form as text" machen, das erste object in inherited ändern, und dann wieder in den Form-View zurück gehen.

hzzm 20. Mär 2019 13:50

AW: Spezieller TFrame
 
Zitat:

Zitat von peterbelow (Beitrag 1428159)
Ich hab dein Problem offenbar nicht richtig verstanden. Dir fehlte wirklich nur eine Kleinigkeit zum Erfolg: Bei Designerklassen fängt das DFM-File mit dem Keyword "object" an, wenn direkt von TForm, TFrame, oder TDatamodule abgeleitet wird. Wenn von einer anderen Klasse abgeleitet wird, die schon ein DFM-File hat (dein Fall) muss das DFM-File der abgeleiteten Klasse aber mit "inherited" anfangen, nicht mit "object". Wenn Du also den Ancestor im Editor manuell änderst mußt Du danach mal kurz "view form as text" machen, das erste object in inherited ändern, und dann wieder in den Form-View zurück gehen.

Genau, das war die Loesung, auf die ich auch nach ewigem getanze gekommen bin, mit einem Wermutstropfen:

Es war notwendig, die TFrame-Abgeleitete Basisklasse, die man TMyCoolFrame zur Ableitung reicht, vollstaendig leer zu erstellen, dann als Basisklasse anzugeben.
Wenn dieses Konstrukt zusammen mit dem "inherited" in der .dfm statt "object" mal steht, kann man in der Basisklasse aendern, was man will.

peterbelow 21. Mär 2019 11:30

AW: Spezieller TFrame
 
Zitat:

Zitat von hzzm (Beitrag 1428169)
Genau, das war die Loesung, auf die ich auch nach ewigem getanze gekommen bin, mit einem Wermutstropfen:

Es war notwendig, die TFrame-Abgeleitete Basisklasse, die man TMyCoolFrame zur Ableitung reicht, vollstaendig leer zu erstellen, dann als Basisklasse anzugeben.
Wenn dieses Konstrukt zusammen mit dem "inherited" in der .dfm statt "object" mal steht, kann man in der Basisklasse aendern, was man will.

Es ist definitiv nicht nötig, mit einer leeren Basisklasse anzufangen. Die IDE hat aber ein Problem, wenn man eine Instanz eines Frames auf einem Form hat, daran dort etwas ändert, und danach dann an der Frameklasse selbst was ändert. Die IDE erkennt das nicht immer korrekt, mitunter muß man manuell im Designer aus dem Kontextmenu für das eingebettete Frame ein "revert to inherited" machen, damit die Änderungen an der Frameklasse übernommen werden. Visual form inheritance hat so seine Macken...

Rollo62 22. Mär 2019 06:41

AW: Spezieller TFrame
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1428135)
Nur wenn man auf Zugriffsverletzungen und ähnliches steht.

Nie Interface- und TObject-basierte Referenzierung mischen :warn:

Das mache ich auch nicht, und habe keinerlei Probleme damit.
Der Zugriff ist dann natürlich ausschliesslich über Interfaces.

Rollo62 22. Mär 2019 06:45

AW: Spezieller TFrame
 
Zitat:

Zitat von hzzm (Beitrag 1428139)
Zitat:

Zitat von Schokohase (Beitrag 1428136)
Delphi-Quellcode:
// Umbiegen der Klasse TFrame auf die gewünschte Klasse
type
  TFrame = CustomFrameApp.Frames.MyBaseFrame.TMyBaseFrame;


Hallo Schokohase,

ja, das ist eine super Lösung.
Ich benutze das "Umbiegen" in ähnlicher Form an einigen Stellen.

Ich frage mich wie das "Umbiegen" als Fachbegriff heisst, weil ich nenne das für mich manchmal Unit-Forwarding.

Hat diese Methode vielleicht einen klaren Namen ?

peterbelow 22. Mär 2019 12:27

AW: Spezieller TFrame
 
Zitat:

Zitat von Rollo62 (Beitrag 1428336)
Zitat:

Zitat von hzzm (Beitrag 1428139)
Zitat:

Zitat von Schokohase (Beitrag 1428136)
Delphi-Quellcode:
// Umbiegen der Klasse TFrame auf die gewünschte Klasse
type
  TFrame = CustomFrameApp.Frames.MyBaseFrame.TMyBaseFrame;


Hallo Schokohase,

ja, das ist eine super Lösung.
Ich benutze das "Umbiegen" in ähnlicher Form an einigen Stellen.

Ich frage mich wie das "Umbiegen" als Fachbegriff heisst, weil ich nenne das für mich manchmal Unit-Forwarding.

Hat diese Methode vielleicht einen klaren Namen ?

Interposer class

Rollo62 22. Mär 2019 14:34

AW: Spezieller TFrame
 
Aha, dankesehr.
Man lernt nie aus.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:25 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz