Autor Beitrag
trm
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 491
Erhaltene Danke: 19

Windows 7x64
Delphi 7
BeitragVerfasst: Mi 11.04.12 23:13 
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.

_________________
In Erfurt gibt es eine Pension, in der es gemütlich ist, Google einfach nach Pension Fiege ;)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 491
Erhaltene Danke: 19

Windows 7x64
Delphi 7
BeitragVerfasst: 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?

_________________
In Erfurt gibt es eine Pension, in der es gemütlich ist, Google einfach nach Pension Fiege ;)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 491
Erhaltene Danke: 19

Windows 7x64
Delphi 7
BeitragVerfasst: 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:
ausblenden volle Höhe 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.)

_________________
In Erfurt gibt es eine Pension, in der es gemütlich ist, Google einfach nach Pension Fiege ;)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 491
Erhaltene Danke: 19

Windows 7x64
Delphi 7
BeitragVerfasst: 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:

ausblenden volle Höhe 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:
ausblenden 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.

_________________
In Erfurt gibt es eine Pension, in der es gemütlich ist, Google einfach nach Pension Fiege ;)
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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.

Für diesen Beitrag haben gedankt: trm
trm Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 491
Erhaltene Danke: 19

Windows 7x64
Delphi 7
BeitragVerfasst: 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 :)

_________________
In Erfurt gibt es eine Pension, in der es gemütlich ist, Google einfach nach Pension Fiege ;)