Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Zur Laufzeit erzeugte Editierfelder (https://www.delphipraxis.net/207701-zur-laufzeit-erzeugte-editierfelder.html)

Kurt Wallander 23. Apr 2021 20:10

Zur Laufzeit erzeugte Editierfelder
 
Glück auf!

In meiner geplanten app habe ich Matrizen unterschiedlicher Größe (Rang) zu verarbeiten. Deshalb erzeuge ich die Editierfelder für die Komponenten ABC[i,k] zur Laufzeit durch folgenden Code:
Delphi-Quellcode:
procedure TForm1.CreateMatrix(rang:integer);
var
   Edt  : TEdit;
   i,k  : integer;
begin
   SetLength(ABC,rang,rang);
   for i:=0 to rang-1 do
      for k:=0 to rang-1 do
      begin
         if TEdit(FindComponent('ABC'+IntToStr(i)+IntToStr(k))) = nil then
         begin
            Edt:=TEdit.Create(Self);
            Edt.Parent:=GroupBox1;
            Edt.Name:='ABC'+IntToStr(i)+IntToStr(k);
            Edt.Width:=75;Edt.Height:=24;
            Edt.Top:=30+i*35;
            Edt.Left:=15+k*90;
            Edt.Alignment:=taRightJustify;
            Edt.BiDiMode:=bdLeftToRight;
            Edt.Visible:=true;Edt.Enabled:=true;
            Edt.Text:=''
         end;
      end;
   if rang=3 then
   begin
      GroupBox1.Width:=290;GroupBox1.Height:=145;
   end
   else
   begin
      GroupBox1.Width:=195;GroupBox1.Height:=100;
   end;
end;
Das funktioniert einwandfrei. Aber ich habe keine Idee, wie ich die Behandlungsroutinen, z. B. Edt.OnExit, ebenfalls zur Laufzeit erzeugen kann. Statisch geht es nicht, da der Compiler die TEdiit-Felder nicht kennt - sie werden ja erst zur Laufzeit erzeugt - und meckert daher (berechtigt).
Hat jemand da eine Idee?
Ein schönes WE
Kurt

Dalai 23. Apr 2021 20:56

AW: Zur Laufzeit erzeugte Editierfelder
 
Füge eine entsprechende Ereignisroutine in deine Klasse (hier wohl TForm1) hinzu und weise diese in der Schleife zu. D.h. alle TEdits verwenden dieselbe Methode.

Grüße
Dalai

TomyN 23. Apr 2021 21:12

AW: Zur Laufzeit erzeugte Editierfelder
 
Und gib den Editfeldern eine individuellen Tag, dann weißt Du, welches Feld das Ereignis ausgelöst hat.

Code:
id:= (Sender As TEdit).Tag

Kurt Wallander 24. Apr 2021 11:04

AW: Zur Laufzeit erzeugte Editierfelder
 
Glück auf!

Vielen Dank für die Replies.
Noch einmal, mein Problem ist, daß ich die Editierfelder zur Laufzeit erzeugen kann. Die Editierfelder haben Namen:ABC00_Edt, ABC01_Edt usw. usf. In diese Editierfelder gebe ich die Werte der Matrixkomponenten ein. Wenn ich nun mit der TAB-Taste zum nächsten Editierfeld "wandere", soll ein OnExit-Ereignis ausgelöst werden, welches ich benutzen will, um den Wert im Editierfeld den Variablen ABC[0,0], ABC[0,1] ... zuzuweisen. Zur Programmierzeit sind ja weder die ABC00_Edt, ABC01_Edt noch ABCOnExit dem Compiler bekannt.
Den Code habe ich inzwischen leicht modifiziert, so daß ich ihn wieder einstelle.
Delphi-Quellcode:
procedure TForm1.CreateMatrix(rang:integer);
var
   Edt  : TEdit;
   i,k  : integer;
begin
   SetLength(ABC,rang,rang);
   for i:=0 to rang-1 do
      for k:=0 to rang-1 do
      begin
         if TEdit(FindComponent('ABC'+IntToStr(i)+IntToStr(k)+'_Edt')) = nil then
         begin
            Edt:=TEdit.Create(Self);
            Edt.Parent:=GroupBox1;
            Edt.Name:='ABC'+IntToStr(i)+IntToStr(k)+'_Edt';
            Edt.Width:=75;Edt.Height:=24;
            Edt.Top:=30+i*35;
            Edt.Left:=15+k*90;
            Edt.Alignment:=taRightJustify;
            Edt.BiDiMode:=bdLeftToRight;
            Edt.Visible:=true;Edt.Enabled:=true;
            Edt.Text:='';
            Edt.OnExit:=ABCOnExit;
         end;
      end;
   if rang=3 then
   begin
      GroupBox1.Width:=290;GroupBox1.Height:=145;
   end
   else
   begin
      GroupBox1.Width:=195;GroupBox1.Height:=100;
   end;
end;
Viele Grüße
Kurt

Aviator 24. Apr 2021 11:20

AW: Zur Laufzeit erzeugte Editierfelder
 
Ich bin ehrlich. Ich verstehe dein Problem aktuell nicht. In deinem letzten Beitrag steht auch keine wirkliche Frage mehr.

Das was du hier ja schon gemacht hast sieht soweit alles ok aus. Du musst jetzt nur noch den Event Handler für OnExit() programmieren.

Delphi-Quellcode:
procedure TForm1.EditExitHandler(Sender: TObject);
begin
  // Alternativ den Tag statt des Namens verwenden
  if (Sender.Name = 'ABCxx') then begin
    // Do something here
  end;
end;
So in der Art könnte der aussehen. Dann kompiliert dein Programm auch. Es sei denn du hast uns in deinem Code Snippet die OnExit() Methode vorenthalten.

himitsu 24. Apr 2021 14:41

AW: Zur Laufzeit erzeugte Editierfelder
 
nicht namen vergleichen ... maximal Konstante, aber niemals "irgendwelche" losen strings!!!!!!
niemals mit = ... wenn, dann SameText oder so
und besser immer Variable mit Instanz vergleichen! (z.B. array oder liste mit sprechenden kostanten)

Kurt Wallander 26. Apr 2021 16:46

AW: Zur Laufzeit erzeugte Editierfelder
 
Glück auf!

Ich kämpfe noch.
Frage an TomyN: Welchen Typ hat id? Integer oder pointer? Ich muß ja dem Compiler sagen, was es ist.
Und zu Aviator: Ein Code-Snippet habe ich nicht verheimlicht. Genau den suche ich. Aber die Anweisung
Delphi-Quellcode:
if (Sender.Name = 'ABCxx') then
bringt den Compiler ins Stolper: Er kennt nämlich die oder das ABCxx nicht. Die werden ja erst zur Laufzeit aktuell.
Die Editierfelder, die mich da gerade interessieren, haben alle "GroupBox1" als parent. Aber iw kann ich das erfragen? Eine Methode sender.parent existiert nicht.
Einen guten Start in die neue Woche
Kurt

runningsoft 26. Apr 2021 17:19

AW: Zur Laufzeit erzeugte Editierfelder
 
Tags von Komponenten sind immer vom Typ integer. Demzufolge müsste die Variable "id" auch integer sein.

Den Codevorschlag von Aviator könntest du folgendermaßen abwandeln, wenn alle deine Editfelder einen eindeutigen Tag erhalten haben:

Delphi-Quellcode:
procedure TForm1.EditExitHandler(Sender: TObject);
begin
  // Alternativ den Tag statt des Namens verwenden
  if (Sender as TEdit).Tag = 1 then begin
    // Do something here
  end;
end;

KodeZwerg 26. Apr 2021 17:27

AW: Zur Laufzeit erzeugte Editierfelder
 
Zitat:

Zitat von runningsoft (Beitrag 1487895)
// Alternativ den Tag statt des Namens verwenden

Anstelle alternativ, immer so vorgehen, in allen events, ist mE. die beste Universallösung. Jedenfalls nutze ich es so in meinen dynamischen krams seitdem mich jemand aus der DP darauf gebracht hatte.

himitsu 26. Apr 2021 17:42

AW: Zur Laufzeit erzeugte Editierfelder
 
In mindestens Win32 könnte man zwar auch zur Laufzeit ein Datenobjekt zu Integer casten und im Tag ablegen.
In der eigenen Datenklasse kann man dann ALLES speichern, was man will.
Aber im Formdesigner lässt sich sowas natürlich nicht nutzen.


Man kann die Komponenten ableiten und eigene Property einbauen.
Ist auch super, um mal bei allen gleichen Komponenten im später einen Bugfix zu integrieren oder Weiteres einzubauen.

Alternativ z.B. ein zusätzliches TDictionary<>, wo man Verknüpfungen zwischen Objekten (Objektzeiger) und irgendwas Anderem speichern kann.



Ach ja, INTEGER wurde eingefroren und wird im 64-Bit-Compiler nicht zu 64 Bit, was vorallem bei Casts zwischen Integer und Pointer/TObject knallen wird.

Aber Tag wurde zum Glück auf NativeInt geändert, womit es 64 Bit werden würde, damit solche bösen Castst nicht gleich fieß abrauchen, aber der Cast muß dafür natürlich auch den "richtigen" Typen verwenden. :zwinker:

Neumann 26. Apr 2021 17:52

AW: Zur Laufzeit erzeugte Editierfelder
 
Man könnte auch ein simples Array für die Edits machen, etwa so:

Delphi-Quellcode:
tform
 edarray:array[1..10] of array[1..10] of tedit;

procedure TForm1.CreateMatrix(rang:integer);
var
     i,k : integer;
begin
  for i:=1 to rang do
   for k:=1 to rang do
   begin
     edarray[i,k]:=tedit.create(self);
     ...
     editarray[i,k].onexit:=exithandler;
   end
end;
procedure TForm1.EditExitHandler(Sender: TObject);
begin
  for i:=1 to rang do
   for k:=1 to rang do
   begin
     if tedit(sender)=edarray[i,k] then
      // Do something here
   end;

 
end
Mann sollte die Edits dann auch wieder freigeben.

Nur mal schnell hingeschrieben, kann man sicher auch schöner machen.

himitsu 26. Apr 2021 18:04

AW: Zur Laufzeit erzeugte Editierfelder
 
Der Vorteil an Tag oder Dictionary ist, dass man im OnClick/OnMouseMove/sonstwo nicht erst in der Liste/Array suchen muß, da die Komponente (Sender) es einem direkt sagt oder man mit nur einem Zugriff das Dictionary fragen kann.




Wenn die Edits einen passenden Owner im Create mitbekommen, wie z.B. die Form oder ein Panel,
dann werden sie bei Freigabe der Form auch mit automatisch freigegeben.


Und auch wenn es "eigentlich" ein klein bissl "falsches" Verhalten ist (aber die WinAPI macht es mit seinen Komponenten auch so, also wurde es auch in die VCL eingebaut und hat mich schonmal echt erschrocken, weil ich damit nicht gerechnet hab, denn "eigentlich" ist ja der Owner für die Freigabe zuständig)
So gibt die VCL ihre Komponenten auf Forms ebenfalls/zusätzlich über die Parent-Beziehung frei,
also z.B. verschwindet das Edit, wenn man das Panel löscht, auf welcher das Edit angezeigt wird.

KodeZwerg 26. Apr 2021 18:40

AW: Zur Laufzeit erzeugte Editierfelder
 
Beim erzeugen Speicher ich mir die Referenz in einer "private" liste ab, beim beenden iterier ich die durch und gebe Speicher wieder frei. Das mit "owner panel" gefällt mir, habe ich noch nicht versucht. Danke für den Tipp!

himitsu 26. Apr 2021 18:46

AW: Zur Laufzeit erzeugte Editierfelder
 
In dem Panel kannst dann auch mit Panel.FindComponent suchen.

oder
Delphi-Quellcode:
for P in Panel do
(TComponent hat einen Standard-Enumerator für .Components),
bzw. selbst über die .Components iterieren.


.Controls sind die Parent-Beziehungen
und .Components die Owner-Beziehungen.

bcvs 27. Apr 2021 06:57

AW: Zur Laufzeit erzeugte Editierfelder
 
Nach all den richtigen Hinweisen mal ne dumme Frage:
Zitat:

In meiner geplanten app habe ich Matrizen unterschiedlicher Größe (Rang) zu verarbeiten. Deshalb erzeuge ich die Editierfelder für die Komponenten ABC[i,k] zur Laufzeit durch folgenden Code:
Warum nimmst du eigentlich kein StringGrid? Wäre doch für soche Matrix-Sachen prädestiniert.

jziersch 27. Apr 2021 07:07

AW: Zur Laufzeit erzeugte Editierfelder
 
wenn Du sowieso Deine Edits zur Laufzeit erzeugts, kannst Du auch gleich eine TEdit Ableitung erzeugen, die zu der Aufgabenstellung passt. Dort kannst Du dann eine Referenz des passenden Typs verwalten und bist nicht auf die Tags beschränkt und brauchst auch nicht unbedingt Event handler zuzuweisen.

Edt:=TMySpecialEdit.Create(Self);

Wenn man Komponenten zur Laufzeit erzeugt, macht es keinen Sinn dort auch Namen zuzuweisen. Man riskiert nur Fehler, wenn Namen bereits existieren.

FindComponent brauchst Du nicht, wenn Du alle erzeugten Edits in einer TList<TMySpecialEdit> speicherts. Du kannst So auch einen Pool von Edits haben, deren Funktion Du nach Bedarf neu zuweisen kannst oder sie verstecken.

himitsu 27. Apr 2021 07:23

AW: Zur Laufzeit erzeugte Editierfelder
 
dem stimme ich aber nicht ganz zu.
selbst wenn der Name nicht verwendet wird, so ist er beim Debuggen oft sehr hilfreich, z.B. kann man so an der Instanz in einem Eventhandler den Namen auslesen, was leichter zu verstehen ist, als irgendein ObjektZeiger.

aber ja, beim automatischen Erstellen muß man natürlich aufpassen, dass es keine Namenskonflikte gibt,
aber hier kann man z.B. den Namen aus einem Prefix und der Position in der Matrix gut generieren lassen.

Blup 27. Apr 2021 08:09

AW: Zur Laufzeit erzeugte Editierfelder
 
Auch wenn ein Teil davon bereits erwähnt wurde, zähle ich das noch mal kurz mit auf:

Warum nicht eine Tabelle (Grid) für die Eingabe verwenden?

Wenn man Komponenten zur Laufzeit erzeugt, kann man denen auch passende Eigenschaften verpassen:
Delphi-Quellcode:
type
  TMyEdit = class(TEdit)
  private
    FPosition: TPoint;
  public
    property Position: TPoint read FPosition write FPosition;
  end;
{...}

Edt := TMyEdit.Create(Self);
Edt.Position.X := k;
Edt.Position.Y := i;
Edt.OnExit := ABCOnExit;
{...}

procedure TForm1.ABCOnExit(Sender: TObject);
begin
  k := TMyEdit(Sender).Position.X;
  i := TMyEdit(Sender).Position.Y;
  {...}
end;
Speichert man die Komponenten beim Erzeugen in einem Array, wird FindComponent nicht benötigt.
Delphi-Quellcode:
TForm1 = class(TForm)
private
  FMyEditABC: array of array of TMyEdit;
end;

Edt := TMyEdit.Create(Self);
FMyEditABC[k, i] := Edt;

Kurt Wallander 29. Apr 2021 10:01

AW: Zur Laufzeit erzeugte Editierfelder
 
Glück auf!

Vielen Dank für die reichlichen Zuschriften. Habe mich an Ralfs Neumanns Vorschlag gehalten. Special thanks an ihn.
Aber gelegentlich tritt ein ganz anderes Problem auf. Beim Linken wird der Laufzeitfehler F2039: ...Ma-Orthogonalisieren.exe kann nicht erstellt werden.
Embacadero hat es offensichtlich nicht geschafft, das seit XE2 auftretende Phänomen auf den Leibe zu rücken. (Für Sezialisten: Die dcu-Dateien haben keinen zusätzlichen Punkt.)
Komischerweise werden andere Projekte anstandslos gelinkt - aber bei denen wird ja auch nichts geändert.



Viele Grüße
Kurt


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:52 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