![]() |
Form + DataModule mehrere Instanzen
Moin,
ich habe zu fast jedem Formular auch ein entsprechendes Datenmodul, wo das nichtvisuelle Zeugs liegt. Also Queries, Datasets, DataSources und sowas. Auch mal ne Imagelist, usw. Nun möchte ich gern von einigen Formularen mehrere Instanzen öffnen und dazu soll auch jede Instanz des Formulars eine eigene Instanz des Datenmoduls bekommen. Wie stelle ich da die Verbindung zwischen der Instanz des Datenmoduls und der Instanz der Formulars her? Danke! |
AW: Form + DataModule mehrere Instanzen
Mach' Dir 'nen Nachfahren von TForm (oder Deiner aktuell genutzten Formularvariante), gönne dem Nachfahren ein Attribut DataModule vom Typ TDataModule.
Dann kannst Du im Objektinspektor dem Formular ein DataModule zuweisen, analog z. B. zu der Eigenschaft DataSet bei 'ner DataSource. Grob in Delphi:
Delphi-Quellcode:
Im Programmquelltext wäre dann ein Zugriff auf das DataModule in der Form
Type
TfmMeinDataModulForm = class(TForm) fDataModule : TDataModule; ... public property DataModule : TDataModule read GetDataSource write SetDataSource; ... end; implementation ... function TfmMeinDataModulForm.GetDataModule : TDataModule; begin Result := fDataModule; end; procedure TfmMeinDataModulForm.SetDataSource(ADataModule : TDataModule); begin fDataModule := ADataModule; if Assigned(fDataModule) then begin ... // eventuell erforderliche weitere Zuweisungen, Plausibilitätsprüfungen ..., // in Bezug auf das DataModule. // Z. B.: Sind dort die erforderlichen DataSets ... vorhanden ... end; end;
Delphi-Quellcode:
möglich.
...
Self.DataModule.JedePublicEigenschaftDesDataModules.UndDerenEigenschaftenUndOderMethoden(ggflsMitParametern); ... Self.DataModule.DataSource1.DataSet.Open; while not Self.DataModule.DataSource1.DataSet.EoF do begin // ... irgendwas mit den Daten machen ... Self.DataModule.DataSource1.DataSet.Next; end; Wenn's ohne extra Nachfahren sein soll, kannst Du auch unter private eine Eigenschaft FDataModule vom Typ TDataModule anlegen und im FormCreate (oder sonstiger passender Stelle) ein DataModule erstellen und zuweisen. Der Zugriff geht wie oben. Das ist jetzt nur grob als Idee beschrieben, es gibt bestimmt auch noch andere Lösungen. |
AW: Form + DataModule mehrere Instanzen
Zitat:
Public wird sowieso niemals im OI angezeigt. (nur Published) Und Published-Properties, welche man direkt in der aktuell bearbeiteten Formklasse deklariert hat, sind eh nicht sichtbar. Maximal immer nur die Property des Vorfahren, aber hier fällt die IDE fast immer auf TForm als Vorfahre zurück, auch wenn man dazwischen noch weitere Ableitungen hat. Vermutlich muß man den Vorfahren mit diesem Property als "Vorlage" bei Delphi registrieren, damit man das Property im OI sieht. |
AW: Form + DataModule mehrere Instanzen
Selbst wenn man die Eigenschaften nicht im Objektinspektor zu sehen bekommt, die "händische" Variante funktioniert auf jeden Fall.
Und da ja von einem Formular mehrere Exemplare erstellt werden sollen, müssen entsprechend gleichviele Datenmodule erstellt werden. Eine Zuweisung im Objektinspektor wäre hier (vermutlich) eh nicht hilfreich, da dort ja alle Formulare auf das gleiche Datenmodul verweisen würden. Also würde (vermutlich) eine Eigenschaft FDataModule unter private mit 'ner Zuweisung das Datenmoduls nach seiner Erstellung in FormCreate (oder ähnlich) ausreichen. Oder ginge (sinngemäß) auch sowas?
Delphi-Quellcode:
Dann kann über fDataModule auf das Datenmodul zugegriffen werden.
Type
TForm1 = class(TForm) ... private fDataModule : TDataModule; ... end; implementation procedure TForm1.FormCreate(Sender: TObject); begin fDataModule := TMeinDatenModul.Create(Self); end; |
AW: Form + DataModule mehrere Instanzen
Ich glaube, mein Problem ist nicht richtig verstanden worden.
Ich hab ein Formular (TfrmFoo) und ein DatenModul (TdmFoo). Im Formular liegt z.B. ein Grid. Als Datenquelle für das Grid gebe ich im Objektinspektor dmFoo.dsDaten an. Wenn ich nun eine Instanz von frmFoo erzeuge, erzeuge ich in der Regel im FormCreate die dazugehörige Instanz von dmFoo. So funktioniert das gut.
Delphi-Quellcode:
(dmFoo ist die globale Variable, die Delphi bei jedem Datenmodul im Quelltext des Datenmoduls erzeugt)
dmFoo := TdmFoo.Create(Self)
Das funktioniert so lange gut, solange ich von frmFoo nur einen Instanz erzeuge. Erzeuge ich eine weitere Instanz von frmFoo, wird dmFoo erneut erzeugt. Damit hab ich 2 Probleme: 1. Ein Speicherleck (das ist aber hier nicht mein Problem) 2. in beide Instanzen von TfrmFoo benutzen die zuletzt erzeugte Instanz von TdmFoo. Das ist mein eigentliches Problem. Ich möchte, dass die erste Instanz von frmFoo auf eine Instanz von dmFoo hat. Sonst zeigen die Grids ja in beiden Formularen das selbe an. |
AW: Form + DataModule mehrere Instanzen
Nee, dein Problem ist hierbei eher, dass du bei
Delphi-Quellcode:
die globale Variable verwendest, anstatt einer lokalen Variable (ein Feld in TfrmFoo).
dmFoo := TdmFoo.Create(Self)
Tipp: Am Einfachsten immer als Erstes diese globale Variable löschen (außer "eine" Instanz davon lässt du automatisch von Delphi erzeugen > siehe DPR/Projektoptionen) Und schon kommt man garnicht erst auf die Idee diese Variable ausversehn nutzen zu wollen. |
AW: Form + DataModule mehrere Instanzen
Deshalb ja keine globale Variabel dmFoo, sondern die private Eigenschaft fDataModule.
Im FormCreate dann statt
Delphi-Quellcode:
halt
dmFoo := TdmFoo.Create(Self)
Delphi-Quellcode:
und im übrigen Quelltext alle Zugriffe auf dmFoo durch fDataModule ersetzen.
fDataModule := TdmFoo.Create(Self)
|
AW: Form + DataModule mehrere Instanzen
Zitat:
Natürlich könnte ich im Quellcode die Properties setzen. Aber dann verliere ich ja alle Vorteile, die das "R" im Wort "RAD" repräsentiert. |
AW: Form + DataModule mehrere Instanzen
In dem Fall wäre es vllt. nötig, auf das Datenmodul zu verzichten und den ganzen Nichtvisuellen Teil auch auf die Form zu packen. Vielleicht nicht schön/übersichtlich, aber es funzt. Dies ist mMn ein Problem der Kategorie: "Irgendeinen Tod musst du sterben". :-D
|
AW: Form + DataModule mehrere Instanzen
Das leider garnicht.
Praktisch kannst du im Formdesigner immer nur mit einer globalen Instanz (Signleton) deines DatenModuls arbeiten. Du kannst aber im Create, vor dem Inherited eine Instanz deines Datenmoduls erzeugen. (es darf aber zur Laufzeit keine automatisch erzeugte Instanz dieses Moduls existieren, bzw. diese wird vorher umbenannt oder freigegeben) DatenModule registrieren sich global im Delphi und der Form-Loader findet sie dann über ihren Namen, bzw. mann kann sich selber ein GetGlobalDataModule-Event registrieren. Nach dem Laden (nach dem Create-Inherited oder spätestens im TForm.Loaded) dann das Datenmodul umbenennen (oder den Namen löschen >
Delphi-Quellcode:
), damit es mit späteren Instanzen deiner Form keine Überschneidungen gibt.
''
In der IDE muß aber eine "globale" Instanz vorhanden sein. Gegen diese kannst du dann Designen (in der DFM wird der Name gespeichert) und beim Laden deiner Form im laufenden Programm wird dann jeweils die aktuelle/individuelle Instanz mit diesem Namen verwendet. Statt vorher eine neue Datenmodul-Instanz zu erzeugen erstmal die globale Instanz verwenden und dann nach dem Laden der Form die "Globale" umbenennen, eine neue Instanz mit dem alten Namen erzeugen und der globalen Variable zuweisen (für ein eventuell nachfolgendes Laden weiterer Formen) Tipp: Wir haben bei uns ein paar DatenModule, die aber in verschiedenen Packages liegen und von der IDE leider nicht immer gefunden werden, wenn man nicht zufällig die Unit dieses DatenModules geöffnet hat, wenn gerade ein anderes Package aktiv ist. Delphi scheint da nicht immer alle Packages "geladen" zu haben und dann existiert keine Instanz dieses Datenmoduls, was dann beim Öffnen der Form im FormEditor gern mal knallt. Es gibt zwar in den geladenen DesignPackages nochmal eine Instanz, aber diese wird nicht vom FormDesigner gefunden, bzw. diese manuell erstellten Module werden nicht gefunden- Aus diesem Grund hab ich unsere Instanzen der DesignTime-Packages auch nochmal manuell in der IDE "registriert".
Delphi-Quellcode:
RegisterFindGlobalComponentProc(FindGlobalModules); // im Class-Constructor des TIDEHelperModul
UnregisterFindGlobalComponentProc(FindGlobalModules); class function TIDEHelperModul.FindGlobalModules(const Name: string): TComponent; begin Result := nil; if SameText(Name, 'DM1') then Result := UDM1.DM1; if SameText(Name, 'DMSys') then Result := USysModule.DMSys; end; Alternativ eben nochmal der Vorschlag von nahpets. Zitat:
Im OI nichts zuweisen, sondern das im Setter der Form an die Unterkomponenten durchreichen. |
AW: Form + DataModule mehrere Instanzen
Zitat:
Wenn man zuerst das Datenmodul erzeugt, dann das Formular und dann den Namen des Datenmoduls ändert, kann man weitere Instanzen erzeugen. Ich hab das jetzt mal so gemacht:
Delphi-Quellcode:
Freigabe des Datenmoduls dann im Destructor des Formulars.
function CreateFoo(AOwner : TComponent) : TfrmFoo;
var dm : TdmFoo; begin dm := TdmFoo.Create(nil); try result := TfrmFoo.Create(AOwner); result.DataModule := dm; // für Zugriffe auf das Datenmodul im Code dm.Name := ''; except dm.Free; raise; end; end; Kann man bestimmt noch hübscher lösen, aber es zeigt erst mal, worauf es ankommt (nämlich den Namen des Datenmoduls an der richtigen Stelle zu manipulieren). |
AW: Form + DataModule mehrere Instanzen
Delphi-Quellcode:
Ich weiß, das Owner-Property ist ReadOnly, aber eigentlich ist die Owner-Beziehung das nicht. :angle:
dm := TdmFoo.Create(nil);
try result := TfrmFoo.Create(AOwner); result.DataModule := dm; // für Zugriffe auf das Datenmodul im Code dm.Owner := result; // oder result.InsertComponent(dm); mit dm.Owner.RemoveComponent(dm) wenn dm.Owner<>nil dm.Name := ''; except dm.Free; raise; end;
Delphi-Quellcode:
type
TComponentHelper = class helper for TComponent private function GetOwner: TComponent; procedure SetOwner(NewOwner: TComponent); public property Owner: TComponent read GetOwner write SetOwner; function SecureOwner: TComponent; // if Assigned(Self) then Result:=Owner else Result:=nil; function SecureName: string; {$REGION 'Documentation'} /// <summary> /// Setzt C.Name für das Error-Logging, wenn das nicht geht, dann wird eine ID angehängt. (z.B. zwei mal /// DB.LoadBool zur selben Zeit) /// </summary> {$ENDREGION} procedure SetErrorName(NewName: string); end; { TComponentHelper } function TComponentHelper.GetOwner: TComponent; begin Result := inherited Owner; end; function TComponentHelper.SecureName: string; begin if not Assigned(Self) then Result := '(nil)' else try Result := Name; except Result := '(except)'; end; end; function TComponentHelper.SecureOwner: TComponent; begin if not Assigned(Self) then Result := nil else try Result := inherited Owner; except Result := nil; end; end; procedure TComponentHelper.SetErrorName(NewName: string); begin try if Name = NewName then Exit; if (NewName <> '') and Assigned(Owner) and Assigned(Owner.FindComponent(NewName)) then Name := NewName + Format('_%p', [Pointer(Self)]) else Name := NewName; except end; end; procedure TComponentHelper.SetOwner(NewOwner: TComponent); begin if Assigned(NewOwner) then NewOwner.InsertComponent(Self) else if Assigned(Owner) then Owner.RemoveComponent(Self); end; |
AW: Form + DataModule mehrere Instanzen
Ah, super danke! :thumb:
Das erspart das Freigeben des Datenmoduls im Destructor (was sicher sonst hier und da mal vergessen wird... :cyclops: ) |
AW: Form + DataModule mehrere Instanzen
Es geht auch noch etwas einfacher. Beim Auflösen der Referenzen haben Komponenten der Form-Instanz Vorrang. Hier ein Form mit Referenz auf ein globales Datenmodul, die zur Laufzeit auf die lokale Instanz umgebogen wird.
Delphi-Quellcode:
type
TMyDataModule = class(TDataModule) HostdbConnection: TFDConnection; TestTable: TFDQuery; private public end; var MyDataModule: TMyDataModule;
Delphi-Quellcode:
object DataSource1: TDataSource DataSet = MyDataModule.TestTable Left = 304 Top = 64 end
Delphi-Quellcode:
type
TForm198 = class(TForm) DataSource1: TDataSource; DBGrid1: TDBGrid; private FMyDataModule: TMyDataModule; protected property MyDataModule: TMyDataModule read FMyDataModule; public constructor Create(AOwner: TComponent); override; end; constructor TForm198.Create(AOwner: TComponent); begin FMyDataModule := TMyDataModule.Create(Self); { Wenn die globale Instanz von MyDataModule hier bereits existiert, wird der Name dieser lokalen Instanz beim Laden der DFM verändert. Daher setzen wir den wieder zurück, sonst werden die Referenzen nicht richtig aufgelöst. Da es eine lokale Instanz des Forms ist, führt das hier zu keinerlei Kollisionen } FMyDataModule.Name := 'MyDataModule'; { Wichtig! Das muss vor dem inherited geschehen. } inherited; end; EDIT: Die Forms kann man damit wieder ganz normal über TForm198.Create(???) erzeugen und muss sich um die Datenmodule gar nicht kümmern. |
AW: Form + DataModule mehrere Instanzen
Die globale Variable braucht es dafür nicht. Es gilt ausschließlich der Name und die "automatische" globale Registrierung der TDataModule.
Und ja, diese Variante hatte ich auch schon beschrieben und wie bereits gesagt, muß man nachher das DataModul umbenennen, da sonst mehrere Datenmodule mit dem selben Namen global registriert sind und welches davon dann für die Verlinkung genommen wird, das ist potentiell zufällig. Erstmal gibt es die Liste in TScreen.DataModule Der DFM-Loader verwendet nun Classes.FindGlobalComponent, welches über RegisterFindGlobalComponentProc(Forms.FindGlobalComponent) in Forms.pas auf Screen.DataModules geht, und schnappt sich die erste Instanz aus Screen.DataModules, mit dem kleinsten Index und dem gewünschten Name, aber das muß nicht unbedingt die von deinem letzten TDataModule.Create sein. |
AW: Form + DataModule mehrere Instanzen
Zitat:
Und das lokale Datenmodul kann sehr wohl seinen Namen behalten, solange man das beschriebene Verfahren konsistent in allen Forms durchführt. |
AW: Form + DataModule mehrere Instanzen
Ahhhhh, garnicht dran gedacht.
Der böse Owner und FindComponent. Im FormDesigner wird aber auch hier dennoch die globale Instanz verwendet. (außer man leitet die Form ab und baut das in den Vorfahren ein ... vorausgesetzt die Ableitung wird richtig im FormDesigner geladen) |
AW: Form + DataModule mehrere Instanzen
Die Anforderung nach eigenen Instanzen für das Datenmodul habe ich noch nicht verstanden.
Sollte es nicht reichen, die Datensourcen auf das Formular zu setzen, dann können die Formulare unabhängig voneinander in den Daten scrollen. Das Datenmodul darf Singleton bleiben. |
AW: Form + DataModule mehrere Instanzen
Jedes Formular mit seinem eigenen DatenModul und den darauf liegenden DataSources,
sonst zeigt doch jede Form das Gleiche an, wenn die alle mit der selben DataSource arbeiten. :zwinker: |
AW: Form + DataModule mehrere Instanzen
Ein DesignTimePackage mit
Delphi-Quellcode:
und schon kannst du das Datenmodul als Non-visuelle Komponente auch direkt auf die Form pappen, aber die Datenmodul-Komponente muß dort unbedingt einen anderen Namen bekommen, wie in der DFM des Datenmoduls steht.
RegisterComponents('DatenModule', [TMyDataModule]);
(sicherheitshalber SetName des DatenModule überschreiben und das unterbinden) Ob die DFM des Datenmoduls hier auch richtig geladen wird, kann ich jetzt nicht beurteilen, aber ich denke es könnte funktionieren. |
AW: Form + DataModule mehrere Instanzen
Zitat:
Wenn ich nun 2 Instanzen mit verschiedenen Parametern öffnen möchte, benötige ich diese 2 Instanzen des Datenmoduls. Sonst müsste ich noch das ClientDataSet auf das Formular packen. Das möchte ich nicht, das finde ich unübersichtlich. |
AW: Form + DataModule mehrere Instanzen
Wir haben einen TDataSource-Nachfahren der ein DataSet (pgDAC) drin hat.
Im Create holt der sich auch selber standardmäßig erstmal die Connection von einem globalen DataModul. (kann man notfalls danach was Anderes zuweisen ... man könnte sich da auch die erste Connection auf dem gemeinsamen Owner suchen, wenn im BeforeOpen noch keine Connection zugewiesen) Dann noch paar Dinge, wie z.B. das SQL-Property, durchgereicht und als Published in die DataSource. Nur eine Komponente auf der Form. (inkl. Preload der Default-Werte im AfterInsert uvm.) Ist wie ![]() Zusammen kann/sollte man aber TLabeledEdit nochmal ableiten und die Label-Caption etwas schöner in dem Edit platzieren. |
AW: Form + DataModule mehrere Instanzen
Zitat:
Die Lösung ist vom Programmieraufwand her recht simpel und eingängig. Man muss lediglich im TForm.Create eine lokale Datenmodul-Instanz erzeugen und entsprechend benennen bevor man inherited aufruft. Eigentlich kann das Datenmodul-Property im Form auch ganz anders heißen, solange der Name der Instanz passt. Will man diese Instanz nicht auch noch im Code selbst ansprechen, muss man die nicht mal als Property oder Feld deklarieren. Eine einzige Zeile reicht dann schon:
Delphi-Quellcode:
constructor TForm198.Create(AOwner: TComponent);
begin TMyDataModule.Create(Self).Name := 'MyDataModule'; inherited; end; |
AW: Form + DataModule mehrere Instanzen
Delphi-Quellcode:
{FFormVariableMyDataModuleOderSo :=} TMyDataModule.Create(Self, 'MyDataModule');
Wenn FFormVariableMyDataModuleOderSo ein Published-Feld mit dem Namen 'MyDataModule' ist, dann kümmert sich die VCL um das Befüllen dieser Variable. TComponent.Name setzt automatisch ein eventuell vorhandenes gleichnamiges Published-Feld im Owner. Und beim Free/Destroy setzt es das auch wieder auf nil. Ich hätte hier nur wegen des Owners leichte Bedenken gehabt. Die Child-Liste existiert ja noch nicht, aber scheinbar wird sie hier angelegt. Vor dem Inherited ist ja noch nichts initialisiert. (alle Felder stehen auf 0 bzw. NIL, da vor der Constructor-Methode, zumindestens schonmal der Speicher mit NewInstance besorgt wurde) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:45 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-2025 by Thomas Breitkreuz