Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Labels dynamisch erzeugen......


roka6803 - Mi 14.09.05 14:30
Titel: Labels dynamisch erzeugen......
Hallo alle zusammen,

ich habe ein Problem mit dem dynamischen erzeugen von 6 Labels.

Bemerkung: Der von mir gezeigte Quelltext ist gekürzt und enthält an der "fraglichen Stelle" lediglich Quelltext für das erzeugen von EINEM Label.

Ich habe vor diesen "fraglichen Teil" in eine weitere von der ersten nur durch ein boolschen Wert abhängige For-Do-Schleife zu packen, sodass auch die Captions der Labels sowie die zugehören OnKlick-Ereignisse variabel sein können (captions kommen dann aus einem array und die prozeduren für die onclick ereignisse werden durchnummeriert sein).

Ein besonderes Problem stellt bei mir die Stelle


Delphi-Quelltext
1:
OnClick := menu_label_click;                    


dar, weil mir dort immer Fehler angezeigt werden !

Bemerkung 2 : Es reicht mir wenn hier die Lösung zustande kommt nur ein Label dyn. zu erzeugen, aber wie gesagt unter der beachtung der "weiteren *pas" !

Jetzt meine Frage(n): Wie kann ich den Teil zum Erzeugen der sechs Label in eine weitere *.pas packen, welche dann auch die OnClick-Ereignis-Prozeduren enthält? Wie muss ich dann diese in der ersten unit deklarieren ?
Wie muss in dem "fraglichen Teil" die Zuweisung  Onlick := menu_label_click; richtig aussehen, damit sie funzt ?


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:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
unit ...;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, jpeg, ExtCtrls;

type
  Tform_loading_screen = class(TForm)
    .......
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ......
  menu_label : TLabel;

implementation

....

procedure Tform_loading_screen.Image1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var i,n : integer;
begin

if loading_boolean = true then
        begin
        n := 10;
        loading_status[1] := ........

        for i := 0 to n - 1 do
           begin
           loading_status_count := i+1;
           Label2.Caption := (loading_status[loading_status_count]);
           label2.Refresh;
           sleep(400);
           loading_progress.StepIt;
           sleep(400);
           end;


        loading_boolean := false;

//---------------------------HIER !!!!!!! Der fragliche Teil ! (Anfang)
        menu_label := TLabel.Create(Self);
    with menu_label do
     begin
        Parent:=form_loading_screen;
               Caption:='Beschriftung';
                Transparent := true;
                font.size := 11;
                font.color := clWhite;
               Left:=100;
               Top:=100;
               OnClick := menu_label_click;
     end;
//---------------------------HIER !!!!!!! Der fragliche Teil ! (Ende)

        // close;
        end;
end;

end.


ManuelGS - Mi 14.09.05 14:42

Wo ist die Deklaration von menu_label_click?


roka6803 - Mi 14.09.05 14:52

user profile iconManuelGS hat folgendes geschrieben:
Wo ist die Deklaration von menu_label_click?


Genau diese On-Click-Ereignis-Prozedur soll komplett in einer anderen Unit stehen.

In der ersten Unit soll sozusagen nur die erzeugende Prozedur stehen, die dann für das OnCklick-Ereignis des neu entstandenen Labels auf die zweite Unit "verweist" !


Grishnak - Mi 14.09.05 14:56

Du musst dem OnClick-Property eines TLabel-Objektes ein TNotifyEvent zuweisen. Dies hat folgende Form

Delphi-Quelltext
1:
type TNotifyEvent = procedure (Sender: TObject) of object;                    


D.h. dein menu_label_click muss ebenfalls diese Form haben!


ManuelGS - Mi 14.09.05 15:01

Du könntest eine zweite Unit schreiben, die die gewünschte Prozedur enthält, und dann mittels {$i dateiname.pas} (ich hoffe, ich irre mich nicht) inkludieren.
Oder aber du bindest sie "normal" bei der Uses-Klausel ein.


roka6803 - Mi 14.09.05 15:08

IN welche Uses-Klausel ? Der vor, oder der nach "Implementation" ?

Und: Wie genau und wo genau muss das mit dem "NotifyEvent" hin ?


ManuelGS - Mi 14.09.05 15:24

In diesem Fall würd es im Implementation-Teil ausreichen.
Wenn's nicht klappen sollte, dann halt in den Interface-Teil.


Grishnak - Mi 14.09.05 15:26

Unit, die die Prozedur enthält:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
unit Egal;

interface

[...]

procedure menu_label_click(Sender: TObject);

implementation

[...]

procedure menu_label_click(Sender: TObject);
begin
  [...]
end;


Unit, die die Form mit dem/den Label(s) enthält:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
unit MitLabel;

[...]

implementation

uses
  Egal[...];

[...]

  MeinLabel.OnClick:=menu_label_click;


So müsste es funktionieren!


roka6803 - Mi 14.09.05 15:37

Also ich würde das jetzt so schreiben:

Unit 1:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
...

        menu_label := TLabel.Create(Self);
    with menu_label do
     begin
        Parent:=form_loading_screen;
               Caption:='Beschriftung';
                Transparent := true;
                font.size := 11;
                font.color := clWhite;
               Left:=100;
               Top:=100;
               menu_label.OnClick := menu_label_click;
     end;

...

end.


Und Unit 2 wie vorher beschrieben.

OK, werds mal probieren !


roka6803 - Mi 14.09.05 15:45

OK, habe es ausprobiert. Eigentlich funzt alles, doch bei  menu_label.OnClick := menu_label_click; haut der den Fehler "[Error] loading_screen.pas(95): Incompatible types: method pointer and regular procedure" rein.

Ansonsten hab ich alles so gemacht wie ihr vorgeschlagen habt !


Grishnak - Mi 14.09.05 15:53

mhhh. Stimmt! Das klappt so leider nicht! :roll:

EDIT: Man kann der OnClick-Property nur Objekt-Methoden zuweisen! Du kannst aber in der Form ein OnClick-Ereignis-Prozedur für dein(e) Label(s) schreiben, in der du dann menu_label_click aufrufst:


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:
[...]

type
  Tform_loading_screen = class(TForm)
    .......
    procedure LabelOnClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

[...]

    with menu_label do
     begin
        Parent:=form_loading_screen;
               Caption:='Beschriftung';
                Transparent := true;
                font.size := 11;
                font.color := clWhite;
               Left:=100;
               Top:=100;
               menu_label.OnClick := form_loading_screen.LabelOnClick;
     end;

[...]

procedure Tform_loading_screen.LabelOnClick(Sender: TObject);
begin
  menu_label_click;
end;

[...]


Amateur - Mi 14.09.05 16:02

hatte ich auch schon. versuch ma sender:tobject als variable zusätzlich zu deiner eignene procedure zu schreiben. also bei deiner procedure: procedure meineprocedure (sender:tobject);
ich habs net getestet aber das liegt auf jeden fall an den parametern von onclick und deiner procedure. ansonsten ma alle parameter von ner onclick procedure bei deiner eigene procedure übergeben. hat bei mir hingehauen. sollte aber mit sender reichen


roka6803 - Mi 14.09.05 16:04

user profile iconGrishnak hat folgendes geschrieben:
mhhh. Stimmt! Das klappt so leider nicht! :roll:


Wie könnte man das mit dem Notify Event machen ?


roka6803 - Mi 14.09.05 16:07

user profile iconAmateur hat folgendes geschrieben:
hatte ich auch schon. versuch ma sender:tobject als variable zusätzlich zu deiner eignene procedure zu schreiben. also bei deiner procedure: procedure meineprocedure (sender:tobject);
ich habs net getestet aber das liegt auf jeden fall an den parametern von onclick und deiner procedure. ansonsten ma alle parameter von ner onclick procedure bei deiner eigene procedure übergeben. hat bei mir hingehauen. sollte aber mit sender reichen


geht auch nicht dann haut er noch mehr fehler raus wie "expected dass statt dem und expected dort weil da hier und son kram..."


ManuelGS - Mi 14.09.05 17:21

Ist zwar ein hässliches Stück Code, aber reicht wohl zu Demonstrationszwecken.


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:
unit Unit2;

interface

uses dialogs,stdctrls,classes,unit1;

procedure machdielabelsdrauf;

implementation

procedure klick(sender:tobject);
begin
showmessage('Eo Captain Jack, Heureka und Quantensprung - es klappt.');
end;

procedure machdielabelsdrauf;
var lab:tlabel;
ereignisprozedur:tnotifyevent;
begin
lab:=tlabel.create(form1);
lab.Parent:=form1;
lab.Caption:='GUTEN TAG - BITTE KLICKEN SIE MICH AN';
@ereignisprozedur:=@klick;
lab.onclick:=ereignisprozedur;
end;

end.


In der anderen Unit diese hier im Implementation-Teil einbinden; per Button etc. dann "Machdielabelsdrauf" aufrufen.


Amateur - Mi 14.09.05 17:29

du musst deine procedure ner klasse zuweisen. wenn ich ne procedure als methode von tform1 schreibe kann ich sie so wie du es willst zuweisen. musste ma probieren. jednefalls muss es ne methode ner klasse sein. ganz ohne was gehts wohl net.

könntest also ne klasse machen tmeineklasse und der die procedure zuweisen und dann machste label1.onclick:=meineklasse.click;

wirst auch noch den sender als parameter geben müssen bin aber net sicher

ok das von manuel sollte auch gehn. is sogar noch besser


roka6803 - Mi 14.09.05 18:27

OK, es klappt alles wunderbar.

Ich habe es jetzt so weit, dass dank einer For-Do-Schleife fünf Label erzeugt weren. Alle mit einer anderen Caption.

Jetzt würde ich es gerne so einrichten, dass jedes Label noch eine eigene OnClick-Prozedur besitzt.

Hier mein aktueller (gekürzter) Quelltext:

Unit 1


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:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
unit loading_screen;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, jpeg, ExtCtrls;

type
  Tform_loading_screen = class(TForm)
    ...
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  ...

implementation

uses loading_screen_menu_labels;  // <------Unit 2

{$R *.DFM}

procedure Tform_loading_screen.FormCreate(Sender: TObject);
begin
...
end;

procedure Tform_loading_screen.Image1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var i,n : integer;
begin
if loading_boolean = true then
        begin
        n := 10;
        loading_status[1] := ...

        for i := 0 to n - 1 do
           begin
           loading_status_count := i+1;
           Label2.Caption := (loading_status[loading_status_count]);
           label2.Refresh;
           sleep(400);
           loading_progress.StepIt;
           sleep(400);
           end;

        loading_boolean := false;

        menu_label_erzeugen;  // <----------- JA, das funzt, mit dem Aufruf aus Unit 2

        end;
end;

end.


Unit 2


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:
43:
44:
45:
46:
47:
48:
49:
unit loading_screen_menu_labels;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, jpeg, ExtCtrls;

procedure menu_label_erzeugen;

var menu_label_caption : Array [1..5of String;

implementation

uses loading_screen;  // <-------- Unit 1

procedure klick(sender:tobject);
begin
showmessage('Eo Captain Jack, Heureka und Quantensprung - es klappt.');
end;

procedure menu_label_erzeugen;
var     menu_label : tlabel;
        ereignisprozedur : tnotifyevent;
        i,e : integer;

begin

menu_label_caption[1] := ...

e := 100;

for i := 0 to 4 do
begin
menu_label := tlabel.create (form_loading_screen);
menu_label.Parent := form_loading_screen;
menu_label.Caption := menu_label_caption[i+1];
menu_label.Top := e;
inc(e,20);
menu_label.Left := 100;
menu_label.Transparent := true;
menu_label.Font.Size := 11;
menu_label.Font.Color := clRed;
@ereignisprozedur := @klick;
menu_label.onclick := ereignisprozedur;
end;
end;

end.


Wie kann jetzt jedes Label seine eigene OnKlick-Prozedur bekommen ?


MfG roka6803

PS Was hat das mit dem @ereignisprozedur := @klick; zu bedeute ? Ich weiss nur, dass es ohne nicht geht !


Grishnak - Mi 14.09.05 18:39


Delphi-Quelltext
1:
@ereignisprozedur := @klick;                    


ereignisprozedur ist im var-Block als TNotifyEvent deklariert. Mittels dieser Zuweisung lässt du sie auf die klick-Prozedur zeigen. Das ist zwar sehr schmutzig programmiert, aber es klappt!

Wenn du jedem Label eine eigene OnClick-Prozedur zuweisen willst (z.B. klick0(), klick2(), ... klick4()), dann kannst du einfach statt nur einem TNotifyEvent ein Array davon deklarieren:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
var
  ereignisprozedur: array[0..4of TNotifyEvent;
begin
  @ereignisprozedur[0]:=@klick0;
  @ereignisprozedur[1]:=@klick1;
  [...]
  @ereignisprozedur[4]:=@klick4;

  [...]

  for i:=0 to 4 do
    begin
      [..]
      menu_label[i].OnClick:=ereignisprozedur[i];
      [...]
    end;
  
  [...]
end;


roka6803 - Mi 14.09.05 18:45

Danke, Grishnak !

Dieses NotifyEvent, würde das auch mit OnMouseOver statt OnClick funzen ?

MfG roka6803

Edit:

Es hat sich ein neues Problem ergeben: Das mit dem Noti...Array funzt wunderbar nur in der Zeile  @ereignisprozedur := @klick bekomme ich ne Fehlerausgabe !

Ansonsten keine weiteren Fehler.

Hier mein (diesmal ungekürzt) Quelltext von Unit2 (der aus Unit1 ist noch wie in meinem vorigen Post):


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:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
unit loading_screen_menu_labels;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls, jpeg, ExtCtrls;

procedure menu_label_erzeugen;

var menu_label_caption : Array [1..5of String;
    ereignisprozedur : Array [0..4of TNotifyEvent;

implementation

uses loading_screen;

procedure klick_newgame(sender:tobject);
begin
showmessage('Neues Spiel starten...');
end;

procedure klick_loadgame(sender:tobject);
begin
showmessage('Gespeichertes (lokales) Spiel laden...');
end;

procedure klick_options(sender:tobject);
begin
showmessage('Optionen öffnen...');
end;

procedure klick_credits(sender:tobject);
begin
showmessage('Credits : Na wer hat das denn gemacht ?');
end;

procedure klick_close(sender:tobject);
begin
Application.Terminate;
end;

procedure menu_label_erzeugen;
var     menu_label : tlabel;
        i,e : integer;

begin

menu_label_caption[1] := 'Neues Spiel';
menu_label_caption[2] := 'Spiel laden';
menu_label_caption[3] := 'Optionen';
menu_label_caption[4] := 'Credits';
menu_label_caption[5] := 'Beenden';

@ereignisprozedur[0] := @klick_newgame;
@ereignisprozedur[1] := @klick_loadgame;
@ereignisprozedur[2] := @klick_options;
@ereignisprozedur[3] := @klick_credits;
@ereignisprozedur[4] := @klick_close;

e := 100;

for i := 0 to 4 do
begin
menu_label := tlabel.create (form_loading_screen);
menu_label.Parent := form_loading_screen;
menu_label.Caption := menu_label_caption[i+1];
menu_label.Top := e;
inc(e,20);
menu_label.Left := 100;
menu_label.Transparent := true;
menu_label.Font.Size := 11;
menu_label.Font.Color := clRed;
@ereignisprozedur[i] := @klick[i];  // <- [Error] loading_screen_menu_labels.pas(74): Undeclared identifier: 'klick'
menu_label[i].onclick := ereignisprozedur[i]; // <- [Error] loading_screen_menu_labels.pas(75): 
                                              // Class does not have a default property UND
                                              // [Error] loading_screen_menu_labels.pas(75): Not enough actual parameters  
end;
end;

end.


Christian S. - Mi 14.09.05 18:47

user profile iconGrishnak hat folgendes geschrieben:

Delphi-Quelltext
1:
@ereignisprozedur := @klick;                    


ereignisprozedur ist im var-Block als TNotifyEvent deklariert. Mittels dieser Zuweisung lässt du sie auf die klick-Prozedur zeigen. Das ist zwar sehr schmutzig programmiert, aber es klappt!
Normalerweise ist das Zuweisen von Evenst nicht schmutzig, sondern eigentlich eine Standardvorgehensweise. In diesem Fall wundert es mich, dass das überhaupt funktioniert.

Denn, wie Du selber geschrieben hast, ist ein TNotifyEvent eine Methode, also eine procedure of object. "Klick" ist jedoch eine einfache Prozedur. Ich denke, dass man deswegen die Klimmzüge mit @.. := @.. machen muss, im Normalfall weist man die Methode einfach zu und fertig.

Ich denke, man fängt sich durch dieses "Zuweisen mit der Brechstange" eine potentielle Fehlerquelle ein, da eine Methode noch einen versteckten Parameter "self" besitzt, den eine Prozedur nicht hat. Ich würde zusehen, es sauber hinzubekommen und keine Prozeduren und Methoden zwangsweise zu verheiraten.

Du findest hier [http://www.christian-stelzmann.de/index_delphi_artikel.html] übrigens einen Artikel zu Komponentenarrays von mir, das könnte Dir auch noch weiterhelfen.

Grüße
Christian