Entwickler-Ecke
Grafische Benutzeroberflächen (VCL & FireMonkey) - Frage bezüglich OOP und mehreren Forms
trm - Mi 11.04.12 23:13
Titel: Frage bezüglich OOP und mehreren Forms
Hallo,
ich versuche im Moment ein kleines Project zu erstellen und das ganze in strikter OOP. Nun besteht ein Problem:
Wie greife ich innerhalb der Unit von einer Hauptform (Form1) auf Elemente von einer anderen Form (Form2) zu, um z.B. Schalter auszulesen, damit in Form1 dann auf Grund der Schalterstellung z.B. Elemente visible sind oder nicht.
Früher habe ich direkt auf die Form2 zugeriffen und das Element ausgelesen. Aber, das ist kein echtes OOP, wurde mir erklärt.
Wie funktioniert das nun bitte korrekt?
Als kleines Beispiel sei ein panel auf Form1, welches über Checkbox1.checked auf Form2 entweder sichtbar ist oder nicht.
jaenicke - Mi 11.04.12 23:24
Es kommt darauf an. Ganz sauber wäre eine Klasse, die diese Einstellungen beinhaltet und zum Datentransport hin- und hergereicht wird. Du kannst aber auch einfach dem Formular Eigenschaften verpassen, mit denen du den Status von außen auslesen kannst.
Und wenn du in deinem ersten Formular auf etwas im zweiten reagieren willst, kannst du im zweiten Formular ein Event deklarieren, das im ersten Formular behandelt wird.
trm - Do 12.04.12 00:02
Hallo Sebastian,
danke für die schnelle Antwort.
Mein Problem, welches mir nicht in den Kopf will ist: Wenn ich eine Klasse erstelle, dann wird das bei einem Projekt irgendwann ausufern, da die Klasse ja ständig angepasst werden muss, wenn neue Elemente auf der Form2 plaziert werden (Bsp. Setupform).
So müsste ich ja bei 30 Checkboxen eine Klasse erstellen, die 30 Elemente beinhaltet.
Ist das wirklich soviel besser, als direkt auf die Elemente einer anderen Form zuzugreifen?
Bitte, verstehe, dass mir das einfach nicht in den Kopf will - ich sehe in dieser Art der Programmiereung keinen Vorteil, nur einen Nachteil, da ich alles mehrfach programmieren muss :/
Für OOP suche ich verzweifelt eine logische Erklärung - aber in diversen Tutorials wird für meine Verhältnisse immer viel zu viel angerissen, ohne auf den Punkt zu kommen, der einfach verständlich ist.
Für mein kleines OOP Beispiel mit dem Panel und der Checkbox ein Gedankengang:
Das Prgramm wird gestartet, dabei wird innerhalb der Form2-Unit der Zustand der Checkbox aus einer Ini-Datei eingelsen (formcreate oder formshow). Wenn die Checkbox nun checked ist, soll auf Form1 das Panel sichtbar sein. Wenn die Checkbox nicht checked ist, dann ist das Panel nicht sichtbar. Da der Code des Einlesens aber in der Syntax der Form2 ausgeführt wird, weiß doch Form1 gar nicht, was da passiert. Und der direkte Zugriff von der Form2-Unit auf die Form1 stellt ja auch wieder einen Bruch von OOP dar.
Wenn ich nun eine Klasse einsetze, um den Status von der Checkbox zur Form1 zu übertragen - wie sollte das erfolgen, so dass nach der Initialisierung der Checkbox und der Zuweisung der Klasse, dies an die Form1 übertragen wird, ohne dass diese weiss, dass da überhaupt was passiert ist?
jaenicke - Do 12.04.12 00:08
Du machst zu viel vom Formular abhängig. Die eingelesenen Daten aus der INI kannst du viel besser in einer Klasse kapseln, die unabhängig von Form2 benutzt werden kann. An Form2 übergibst du dann das entsprechende Objekt.
Genauso kannst du das Objekt aber auch in einem anderen Projekt nutzen. Eben weil es nicht von der Oberfläche abhängig ist.
Je mehr Beziehungen von Klassen untereinander du in deinem Quelltext hast, desto schwieriger werden Anpassungen und die Wiederverwendung von Teilen des Codes. Zudem kann man so die einzelnen Klassen nicht unabhängig voneinander testen (Stichwort Unittests, ...).
trm - Do 12.04.12 00:27
Daraus folgere ich, dass ich eine Klasse definieren muss, welche ich für alle Forms sichtbar mache, die diese Einstellungen benötigen. Wenn ich z.B. 30 Checkboxen habe, muss die Klasse 30 Boolean-Felder besitzen, die ich in einer eigenen Function/Procedure zuweise. Anschließend kann ich dann z.B. aus Form1 und Form2 die Werte ablesen.
Bsp:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:
| type TTest = record CheckBox_Panel1: Boolean; CheckBox_Panel2: Boolean; CheckBox_Panel3: Boolean; CheckBox_Button1: Boolean; end;
var Global_Test:TTest;
In einer extra Unit: function readsetup:boolean; var ini:tinifile; begin ini:=tinifile.create;
Global_Test.CheckBox_Panel1:=Readboolean('Test','checkbox1',False); Global_Test.CheckBox_Panel2:=Readboolean('Test','checkbox2',False); Global_Test.CheckBox_Panel3:=Readboolean('Test','checkbox3',False); [..] result:=true; end;
In Form1:
procedure form1.oncreate(sender:tobject); if readsetup then begin panel1.visiible:=Global_Test.CheckBox_Panel1; [..] end; end;
In Form2 dann ähnlich win in Form1. |
Waäre das so in etwa der Weg, der korrekt wäre?
(Den Code habe ich jetzt hier nur so reingeschrieben, k.A., ob da Fehler drin sind.)
jaenicke - Do 12.04.12 00:41
Globale Variablen sind keine so gute Idee. So furchtbar viele Alternativen gibt es bei Delphi 7 allerdings auch noch nicht. Heutzutage würde man das eher mit Klassenvariablen, Klassenkonstruktoren, ... machen.
Sinnvoller wäre bei Delphi 7, wenn du die Klasse beim Start der Anwendung erzeugst und die Instanz an die Fenster und Klassen weiterreichst, die die brauchen.
Bei so vielen Boolean Werten bieten sich aber auf jeden Fall Sets an. :gruebel:
Die lassen sich dann für die Speicherung in Einzelwerte zerlegen, aber es wäre dann nicht so viel Quelltext notwendig.
trm - Do 12.04.12 01:01
Ich glaube, mir fehlt einfach das Grundwissen, um das zu verstehen, warum dies so gemacht werden soll :(
(zu gut deutsch: ich bin zu blöd, das zu Verstehen)
Meine bisherigen Programmierergebnisse liefen auch ohne OOP gut. Die direkte Ansteuerung von Checkboxen, Editfeldern usw. waren immer schnell und unkompliziert.
Wenn ich ein Element von einer Form entfernt hatte oder modifiziert hatte, wurde ich vom Compiler aufmerksam gemacht.
Das Lesen und Speichern von Einstellungen hatte ich vereinfacht, indem ich z.B. alle Controls von einer Form durchlaufen liess, da die Namen eindeutig und ohne Sonderzeichen bestanden, konnte ich diese auch als statische Werte für Operationen nutzen.
Bsp. aus einer Setup-Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
| function TForm_Setup.Load_Setup(Pfad, Dateiname: string): Boolean; var x, y: Integer; Dummy_Setup: TXMLConfig; begin
try Result := True; Dummy_Setup := TXMLConfig.Create(Pfad + Dateiname);
with Dummy_Setup do begin for x := 0 to PageControl_Setup.PageCount - 1 do for y := 0 to PageControl_Setup.Pages[x].ControlCount - 1 do begin if PageControl_Setup.Pages[x].Controls[y] is TCheckBox then (PageControl_Setup.Pages[x].Controls[y] as TCheckBox).Checked := ReadBoolean(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TCheckBox).Name, (PageControl_Setup.Pages[x].Controls[y] as TCheckBox).Checked);
if PageControl_Setup.Pages[x].Controls[y] is TEdit then (PageControl_Setup.Pages[x].Controls[y] as TEdit).Text := ReadString(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TEdit).Name, (PageControl_Setup.Pages[x].Controls[y] as TEdit).Text);
if PageControl_Setup.Pages[x].Controls[y] is TRadioGroup then (PageControl_Setup.Pages[x].Controls[y] as TRadioGroup).ItemIndex := ReadInteger(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TRadioGroup).Name, (PageControl_Setup.Pages[x].Controls[y] as TRadioGroup).ItemIndex);
if PageControl_Setup.Pages[x].Controls[y] is TRadioButton then (PageControl_Setup.Pages[x].Controls[y] as TRadioButton).Checked := ReadBoolean(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TRadioButton).Name, (PageControl_Setup.Pages[x].Controls[y] as TRadioButton).Checked);
if PageControl_Setup.Pages[x].Controls[y] is TUpDown then (PageControl_Setup.Pages[x].Controls[y] as TUpDown).Position := ReadInteger(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TUpDown).Name, (PageControl_Setup.Pages[x].Controls[y] as TUpDown).Position);
if PageControl_Setup.Pages[x].Controls[y] is TColorBox then (PageControl_Setup.Pages[x].Controls[y] as TColorBox).Selected := ReadInteger(PageControl_Setup.Pages[x].Name, (PageControl_Setup.Pages[x].Controls[y] as TColorBox).Name, (PageControl_Setup.Pages[x].Controls[y] as TColorBox).Selected);
end; end; Dummy_Setup.Free;
except Result := False; end;
end; |
Das Beispiel speichert in einer xml-Datei ab es läuft automatisiert, um direkte Werte brauche ich mich nicht zu kümmern.
Sollte ich Werte nicht speichern wollen, könnte ich die Controls dazu auf eine eigene Page packen und diese Page (TabSheet) dann bei der Iteration ausklammern:
Delphi-Quelltext
1: 2: 3: 4:
| [..] for y := 0 to PageControl_Setup.Pages[x].ControlCount - 1 do if PageControl_Setup.Pages[x] is not TabSheet5 then [..] |
Wenn ich nun per OOP das ganze umsetzten müsste, dann müsste ich jedes einzelne Elment auf der Form beachten und per Hand auslesen, um es einer Klasse zuzuordnen. Beim Speichern dann umgekehrt.
Sebastian, vielen Dank dennoch für Dein Verständnis für mein Unverständnis, ich glaube, ich werde weiterhin nicht OOP programmieren, weil es mir einfach leichter fällt.
Viele Grüße
~Mathias.
jaenicke - Do 12.04.12 05:57
Eine saubere Struktur bedeutet auch durchaus öfter mal mehr Code, das stimmt. ;-) Das hat aber weniger mit OOP oder nicht OOP zu tun.
Wenn man solche Strukturen aber nicht erst im Nachhinein einbaut, fällt das nicht so sehr auf. Man gewöhnt sich dran.
Einmal zu deinem Beispiel:
Wie lädst du denn die entsprechenden Einstellungen wieder? Vermutlich erstellst du wieder das Formular und liest dann aus dessen visuellen Komponenten aus, oder? Nur wird das Formular ja eigentlich an der Stelle gar nicht gebraucht.
Was machst du jetzt, wenn du eine Konsolenanwendung oder einen Dienst entwickelst, der diese Einstellungen laden soll? Wieder das Formular dafür erstellen nehme ich an, oder? Dabei hat ein Formular, auch wenn es unsichtbar bleibt, in solchen Anwendungen eigentlich nichts verloren. Aber dir bleibt dann ja keine Wahl.
Oder ein anderer Fall:
Stell dir einmal vor du hättest auch Einstellungen, die schon für das Eingeben von Einstellungen gelten. Wenn du diese nun aus dem Formular ausliest, in dem der Benutzer auch gerade Änderungen macht, werden diese verwendet, obwohl der Benutzer diese noch gar nicht bestätigt hat. Das möchte man oft aber nicht.
Wenn du hingegen eine eigene Klasse für die Einstellungen hast, dann ist es richtig, dass du die ganzen Eigenschaften erstellen und lesen musst und dann daraus die Komponenten ansteuern musst. Dafür kannst du aber im Programm einfach ein solches Objekt benutzen und dessen Eigenschaften abfragen ohne dafür das Formular zu benötigen. Und einfach nur diese Klasse zu erstellen und die Werte zu lesen geht natürlich schneller als das ganze mit einem Formular und visuellen Komponenten zu machen.
trm - Do 12.04.12 07:58
Hm, das sind mal Argumente, mit denen ich bisher noch nicht gedanklich gespielt habe :)
Ich denke noch ein wenig drüber nach.
Recht herzlichen Dank :)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!