Entwickler-Ecke

Sonstiges (Delphi) - Ein String als Befehl?


LittleBen - Mo 18.04.11 22:59
Titel: Ein String als Befehl?
Hallo,
ich erzeuge zur Laufzeit mehrere Objekte.
Wenn ich nun mit einen dann erzeugten Objekte etwas machen will, muss ich doch mit Strings arbeiten, oder?
Angenommen:
Durch einen Klick auf einen Button(z.b Button1) erstelle ich ein Edit-Felde, zum Beispiel Edit1.
Nun will ich durch einen Klick auf einen weiteren Button(z.b Button2) den Textinhalt des erzeugten Edit-Feldes.
Nun kann ich ja nicht Einfach im Code schreiben:

Delphi-Quelltext
1:
showmessage(edit1.text);                    


Könnt ihr mir helfen?

Viele Danke & Grüße,
Benny


// EDIT: Damit ich nicht weiter missverstanden werde. ;)

Zuerst erzeuge ich beliebig viele Editfelder:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
var aEdit: TEdit;
    i: integer;
    anzahl: integer;
begin
 anzahl:= Random(20);

 for i:= 0 to anzahl do
 begin
  showmessage(inttostr(anzahl));
  aEdit:= TEdit.Create(nil);
  aEdit.Parent:= self;
  aEdit.Name:= 'Edit'+inttostr(i);
  aEdit.Text:= 'Test'+inttostr(i);
  aEdit.SetBounds(100,12+i*(aEdit.Height+4),aEdit.Width,aEdit.Height);
 end;
Und nun will ich den Text vom 12. Editfeld auslesen, wenn es dieses überhaupt gibt.

Moderiert von user profile iconNarses: Topic aus Windows API verschoben am Di 19.04.2011 um 00:11


trm - Mo 18.04.11 23:24

Wenn Du ein Object erzeugst, kennst Du dann nicht den Namen?

Mit Findcontrol kannst Du den Namen auch herausfinden.
Oder aber, Du hast generische Namen und nutzt ein Array of Strings, in dem Du die Namen speicherst, nachdem Du die Objecte erzeugt hast.
Oder aber, Du assoziierst zum Namen des Button einen Namen fürs Edit.
Oder aber, Du machst es ganz anders.

:)


jaenicke - Di 19.04.11 04:55

Am besten speicherst du dir in der Regel die erzeugten Objekte direkt, zum Beispiel in einem Array.

Je nachdem wie viele Objekte du erzeugst und wie diese zu deinen internen Daten dazugehören.


LittleBen - Di 19.04.11 13:00

Also, so erzeuge ich das Objekt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Edit:= TEdit.Create(self);
  Edit.Parent:= self
  Edit.Name:= 'Edit1';
  Edit.Text:= 'Test';
  Edit.Left:= 114;
  Edit.Top:= 48;
  Edit.Height:= 20;
  Edit.Width:= 177;

So, nun will ich den Text auslesen (durch Druck auf einen Button)

Delphi-Quelltext
1:
showmessage(Edit1.Text);                    

Das funktioniert natürlich nicht, da ich das Editfeld ja zur Laufzeit erzeuge!
Doch wie kann ich diesen Text trotzdem auslesen?

Grüße,
Benny

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

EDIT: Hab es jetzt dank dem Stichwort "FindControle" herraus gefunden! Vielen Dank!

Delphi-Quelltext
1:
showmessage(TEdit(FindComponent('edit1')).text);                    


Gausi - Di 19.04.11 13:12

Na, du erzeugst eine TEdit-Instanz zur Laufzeit und weist dieses Objekt der Variable Edit zu. Wie könnte man das dann später ansprechen?

Kleiner Tipp: In deinem Code

Delphi-Quelltext
1:
showmessage(Edit1.Text);                    

Ist genau eine Ziffer zuviel. :D

Vorausgesetzt natürlich, dass Edit keine lokale Variable ist.


whitef - Di 19.04.11 13:19

mal ne frage so nebenbei...^^

was ist denn der vorteil wenn man ein objekt zur laufzeit erzeugt? ressourcenschonender?
also ich hab bis jetzt immer ein objekt.visible := false gesetzt, und falls ich mal was brauchte, das objekt.visible := true gesetzt.

mfg


jaenicke - Di 19.04.11 13:27

user profile iconwhitef hat folgendes geschrieben Zum zitierten Posting springen:
was ist denn der vorteil wenn man ein objekt zur laufzeit erzeugt? ressourcenschonender?
Wenn es viele Komponenten sind, reduziert sich die Startzeit. Wobei das zwar oft sinnvollere Wege gibt, aber nicht immer.

user profile iconwhitef hat folgendes geschrieben Zum zitierten Posting springen:
also ich hab bis jetzt immer ein objekt.visible := false gesetzt, und falls ich mal was brauchte, das objekt.visible := true gesetzt.
Dann musst du aber vorher wissen wie viele Objekte du brauchen wirst. ;-)


Tranx - Mi 20.04.11 02:44

Mal eine bescheidene Frage: Warum nimmst Du nicht die Variable/Instanz Edit, um dieses Problem zu lösen?


Delphi-Quelltext
1:
  Showmessage(Edit.Text);                    


Wie Gausi schon richtig erwähnte. Denn Du hast ja Edit als TEdit erzeugt. Somit ist Edit der Zeiger auf das Objekt und Du kannst alle öffentlichen Eigenschaften und Methoden der Komponente TEdit nutzen, indem Du sie über Edit ansprichst. Schau die mal an, was Der Compiler dir bei Edit beim Debuggen anzeigt. Dann siehst Du das.


trm - Mi 20.04.11 10:33

Wenn Edit keine globale variable ist, funktioniert das nicht.
Daher sollte man das lieber mit FindControl/FindComponent lösen, weil hier auch getestet werden kann, ob es ein gültiges Control ist UND, ob es überhaupt ein Klassentyp ist, den man erwartet.


BenBE - Mi 20.04.11 10:51

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
Daher sollte man das lieber mit FindControl/FindComponent lösen, weil hier auch getestet werden kann, ob es ein gültiges Control ist UND, ob es überhaupt ein Klassentyp ist, den man erwartet.

Darf ich gleich prügeln, oder soll ich noch warten?

Wann immer man eine konkrete Instanz eines Objektes sowieso hat UND dazu sogar weiß, von welcher Klasse diese ist, sollte man dies dem Compiler auch mitteilen und auf solche Krücken wie FindComponent verzichten, da man wesentliche Vorteile einer typisierten Sprache verliert.

Also in dem Fall Edit als Variable entweder ans Objekt gebunden (im z.B. Private-Bereich des Forms) oder ausnahmsweise (wenn es SEHR GUTE GRÜNDE dafür gibt auch als globale Variable deklarieren.

FindComponent ist ein Anzeichen schlechten Codes!


trm - Mi 20.04.11 17:37

Auch wenn ich mich wieder unbeliebt mache:

Nein.

Zwei Beispiele:

In einem Programm von mir ist es dem Benutzer gestattet diverse Controls in n-facher Ausführung darzustellen. Da es nach oben (theoretisch) keine Grenze gibt, ist es Codetechnisch nicht vorhersehbar, ob ein Benutzer 100 oder nur 10 davon erzeugen lässt.

Die zweite Sache ist ein Setup mit vielen Panels, auf denen die Optionen abgebildet sind. Als Navigation nutze ich Panels als "Kopfbutton". Per Code erhalte ich den Namen des gedrückten Panel und füge dem String einen anderen Teilstring hinzu (z.B. aus "Setup_Farben" <- Kopfpanel wird "Panel_Farben_sub" <- Panel, welches angezeigt werden soll), wobei ich diesen String dann mittels Findcontrol/FindComponent suche. Wenn gefunden, ist alles ok und ich mache weiter.

Neulich hatte mir jemand etwas von wegen "Wiederverwertbarkeit" erklären wollen.
Warum dann also nicht die Möglichkeiten, die Delphi bietet nutzen und mit einer einzigen Procedure, welche in minimum einen 5-Zeiler darstellt (zwischen begin .. end;) den Code optimieren ?

Und bevor Du Dich prügeln willst, wisse bitte um die Umstände :(

Viele Grüße


Edit: RS gefunden


alzaimar - Mi 20.04.11 17:54

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
Nein.
...und das ist die Antwort auf welche Frage?
user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
In einem Programm von mir ist es dem Benutzer gestattet diverse Controls in n-facher Ausführung darzustellen. Da es nach oben (theoretisch) keine Grenze gibt, ist es Codetechnisch nicht vorhersehbar, ob ein Benutzer 100 oder nur 10 davon erzeugen lässt.
Also ich würde die Instanzen in einer TList ablegen.

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
Die zweite Sache ist ein Setup mit vielen Panels, auf denen die Optionen abgebildet sind. Als Navigation nutze ich Panels als "Kopfbutton". Per Code erhalte ich den Namen des gedrückten Panel und füge dem String einen anderen Teilstring hinzu (z.B. aus "Setup_Farben" <- Kopfpanel wird "Panel_Farben_sub" <- Panel, welches angezeigt werden soll), wobbei ich diesen String dann mittels Findcontrol/FindComponent suche. Wenn gefunden, ist alles ok und ich mache weiter.

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
FindComponent ist ein Anzeichen schlechten Codes!
q.e.d.

Die logische Verknüpfung könnte man auch über die Tag-Eigenschaft des Panels lösen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
Procedure TMyForm.OnPanelClick (Sender : TObject);
Begin
(*
  If Sender is TPanel Then
    If TObject (TPanel(Sender).Tag) is TPanel Then
*)

  TPanel(TPanel(Sender).Tag).Visible := True;
End;


Wenn Du für immer in deinem Kämmerlein für dich alleine programmierst, kann man solche Klimmzüge ja machen. Wenn aber jemand anderes deine implizite Verknüpfung durch Namen nicht kennt und den Code anfässt, ist deine gesamte Logik im Eimer.

Glaub BenBE: FindControl ist Müll.


jaenicke - Mi 20.04.11 18:18

user profile iconalzaimar hat folgendes geschrieben Zum zitierten Posting springen:
Also ich würde die Instanzen in einer TList ablegen.
Hier bietet sich meistens ein Dictionary an:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
private
  FEditControls: TDictionary<Integer,TEdit>;
...
  FEditControls := TDictionary<Integer,TEdit>.Create;
  FEditControls.Add(900344, NewEdit);
  FEditControls.Add(100, NewEdit);
...
  FEditControls[900344].Text := '90';
...
Als Zuordnung kann man natürlich statt Integerwerten auch z.B. Strings oder wieder Objekte (zum Beispiel Datenobjekte) nehmen.

Auf diese Weise ist man sehr flexibel und das ganze ist sehr schnell, da die Schlüssel gehasht werden.


trm - Mi 20.04.11 18:31


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Panel_Setup_Click(Sender: TObject);
var
  Dummy_String: string;
begin

  { Korrekte Setup-Seite anzeigen }
  if TControl(Sender).Parent is TPanel then
    Dummy_String := (TControl(Sender).Parent).Name;
  Dummy_String := Copy(Dummy_String, Pos('_', Dummy_String) + 1, Length(Dummy_String));

  if FindComponent('Panel_Setup_' + Dummy_String) is TPanel then
  begin
    TPanel(FindComponent('Panel_Setup_' + Dummy_String)).BringToFront;
    TPanel(FindComponent('Panel_Setup_' + Dummy_String)).Visible := True; //nur für den Fall, dass eins aus Versehen auf Visible false steht
  end;
  { Korrekte Setup-Seite anzeigen }

end;


Zwischen { Korrekte Setup-Seite anzeigen } ist das, was ich meinte. Die Tag-Eigenschaft ist für andere Sachen reserviert.
Ok, das sind jetzt 8 Codezeilen, aber dennoch sehr übersichtlich.
Wenn man in dem Beispiel jetzt noch die Strings in eine extra Datei auslagert, die man kommentiert, dann ist das die Beste Lösung für solch einen Fall, ist meine Meinung.

Und ein TPanel(TPanel(Sender).Tag).Visible := True; ist nicht nutzbar, da, sobald jemand anderes die Tag-Eigenschaft im Code ändert, ist dies sogar noch massiv schlimmer, das das, was Du mir vorgeworfen hast, alzaimar. Und "in einem Kämmerlein [..]" impliziert schon fast eine Beleidung. Nur, weil jemand (ich) "nur" ein Hobbyprogrammierer ist, der sich sein Wissen durch Lesen angeeignet hat, sollte man hier nicht persönlich werden, bitte!

Mein Standpunkt steht fest, ich NUTZE die Möglichkeiten, die Delphi bietet, sofern mir diese bekannt sind und lerne gern dazu.
Mehr schreibe ihc hier im Beitrag nicht.

Danke für die Aufmerksamkeit, schönes Osterfest.


Edit:

@jaenicke:
Ab welcher Delphiversion ist das, was Du da geschieben hast, nutzbar? ( [..] Hier bietet sich meistens ein Dictionary an [..] )
Mein Delphi 7 kennt sowas nicht.


jaenicke - Mi 20.04.11 18:42

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
@jaenicke:
Ab welcher Delphiversion ist das, was Du da geschieben hast, nutzbar? ( [..] Hier bietet sich meistens ein Dictionary an [..] )
Mein Delphi 7 kennt sowas nicht.
Das geht ich denke ab Delphi 2009, denn ab da gibt es Generics in Delphi. Ich selbst benutze das mit Delphi XE.

Vorher kann man es mit zwei TList-Objekten relativ gut nachbilden. Es sieht dann zwar nicht ganz so elegant aus, funktioniert aber auch sehr gut.
Es ist aber immer noch übersichtlicher (und natürlich deutlich schneller) als mit FindComponent.


alzaimar - Mi 20.04.11 19:34

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
Mein Standpunkt steht fest, ich NUTZE die Möglichkeiten, die Delphi bietet, sofern mir diese bekannt sind und lerne gern dazu.
Mehr schreibe ihc hier im Beitrag nicht.
Und deshalb bohrt man mit dem Hammer Löcher in die Wand. Man nutzt eben die Möglichkeiten, die einem die Werkzeugkiste bietet.


LittleBen - Mi 20.04.11 22:35

Vielen Dank für eure zahlreichen Ideen.
Habe mein "Problem" nun mit FindComponent gelöst. Durch diese Funktion kann man meiner Meinung nach ein Objekt einfach und schnell finden.

Grüße,
Benny


jaenicke - Mi 20.04.11 22:39

user profile iconLittleBen hat folgendes geschrieben Zum zitierten Posting springen:
Durch diese Funktion kann man meiner Meinung nach ein Objekt einfach und schnell finden.
Naja, ob das:

Delphi-Quelltext
1:
showmessage(TEdit(FindComponent('edit1')).text);                    
wirklich einfacher ist als...

Delphi-Quelltext
1:
showmessage(Edit.text);                    
Dazu sage ich mal nichts weiter... :lol:


beastofchaos - Mi 20.04.11 23:19

Seh ich auch so wie jaenicke - An dieser Stelle weist das doch eher auf einen schlechten Quelltext hin, wenn man bei nur einem Objekt schon FindComponent() benutzt.
.
Aber andererseits bohrt sich da bei mir eine Frage: Er hat doch den Namen des Objekts erst durch die Variable Edit definiert, aber danch hat er den Namen auf Edit1 umgeändert (zur LZ). Wie kann er dann ein Objekt (das Edit1 heißt) unter dem Variablennamen Edit verändern??
siehe:

Delphi-Quelltext
1:
2:
  Edit.Name:= 'Edit1';
  Edit.Text:= 'Test';  // hier müsste doch dann Edit1.Text stehen ??


trm - Mi 20.04.11 23:49

Nein.

Edit = Variable:

var
Edit:TEdit;
begin
..

Wenn Edit eine Variable/Object ist, kannst/musst Du dieser/m einen Namen zuweisen.
Aber da der Name erst generiert wird, gibt es das noch nicht "physisch" auf der Form.
Daher ansprechbar per Edit, nicht per Edit1.

Die Variable/das Object könnte man auch Huzeldurf nennen. Dennoch ist es nicht unter dem Namen als Object benutzbar im Designermodus.

Und gerade WEiL es im Designmodus nicht nutzbar ist, ist es möchlich in einer ANDEREN Procedure/Function / Unit auf dieses Object/Control, welches es dann inzwischen ist, per FindControl/FindComponent zuzugreifen.

Dies erspart ViEL Arbeit im Vorfeld, da man sich nicht Gedanken machen muss, wieviele Instanzen eines Objects/Controls erzeugt werden müssen. Es sei denn, es dürfen nur z.B. 4 sein. Dann könnte man sich die Arbeit machen, die fest zu legen im Code.
Aber selbst als Array oder in einer anderen Liste, müsste diese syncronisiert werden, sollte eines der Instanzen gekillt werden.
Somit muss also wieder Code erstellt werden, der eine Liste managt.

Auch wenn ich mich widerhole: Es ist eine legitime Art ein Object/Control anzusprechen, wenn man Find... nutzt. Es ist in Delphi verfügbar, nutzbar und effizient. Leistungseinbußen gibt es in keiner Weise für "normale" Anwender.

(Ich wollte ja eigentlich nichts mehr dazu schreiben..)
^- Zu spät.

Gute Nacht.

( Ich habe meinen Hammer verlegt, muss den erst nochmal suchen -.- )


jaenicke - Do 21.04.11 05:54

user profile icontrm hat folgendes geschrieben Zum zitierten Posting springen:
Aber selbst als Array oder in einer anderen Liste, müsste diese syncronisiert werden, sollte eines der Instanzen gekillt werden.
Umgekehrt, die GUI-Verwaltung, die auch die Liste enthält, bekommt den Befehl das entsprechende Feld zu entfernen. Den Rest übernimmt diese.
Das Feld selbst von der GUI zu entfernen und danach an die GUI-Oberflächenverwaltung den Befehl zu geben, dieses Feld auch dort zu löschen, ist natürlich doppelte Arbeit. ;-)

Ich benutze aber für den Zweck auch angepasste Klassen, die die entsprechenden Möglichkeiten bieten. Diese sind dann universell in meinen Programmen einsetzbar, so dass die Verwaltung einfacher wird. Und auch die vorhandenen Klassen können mit Hilfe von Class Helpern (ab Delphi 2006) auch um eine solche Funktionalität ergänzt werden (leider nicht bei generischen Typen).

Bei einzelnen Komponenten macht FindComponent jedenfalls keinen Sinn und bei einer Liste wird oft dann doch eine Schleife zum Ansprechen benutzt und in der ist FindComponent performancemäßig ungeeignet. Zudem reicht es aus aus Versehen den Namen der Komponente zu ändern und schon funktioniert vielleicht irgendetwas nicht mehr. Da man es aber auf diese Art und Weise macht, hat der Compiler keinerlei Möglichkeit, dies zu erkennen. Deshalb gibt es zwar Anwendungsfälle, in denen es Sinn macht, aber man muss aufpassen, dass man sich damit kein Bein stellt.


BenBE - Do 21.04.11 09:21

Also für den Fall mit "Nutzer kann beliebig viele Instanzen von Controls anlegen" fallen mir grad 2 Lösungen ein:

Nummer 1:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
type
    TUserControls = record
        L_Feld1: TLabel;
        E_Feld1: TEdit;
        L_Min: TLabel;
        E_Min: TMaskEdit;
        L_Max: TLabel;
        E_Max: TMaskEdit;
        L_Irgendwas: TLabel;
        C_Irgendwas: TCombobox;
    end;

var
    UserControls: array of TUserControls;


Einfügen und Entfernen von Einträgen aus einem Array ist recht trivial, Nutzung im Programm einfach durch:


Delphi-Quelltext
1:
UserControls[X].E_Max.Value := '12,5';                    


Und siehe da: KEIN FindComponent ... *staun*

Nummer 2:
Die andere wäre, ich definiere mir von TFrame abgeleitet meine Oberfläche und instantiiere das als Frame statt als Record. Dann kann ich das nämlich sogar soweit treiben, dass ich die Prüfungslogik für die Gültigkeit und die zugehörigen Nutzerdaten direkt von dem Frame verwalten lasse.

Die Frames dann entweder in ein Array, eine TList oder eine TObjectList und, warte, ging schon wieder ohne FindComponent.


Ach verdammt, und dabei wollte ich grad so schön zeigen, wie einfach FindComponent den Source unübersichtlich macht ;-)