Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Control " has no parent window bei TCombobox


catweasel - Mo 17.11.03 21:04
Titel: Control " has no parent window bei TCombobox
Hi,

Zunächst vorneweg:

Eine ähnliche Frage hat es hier schonmal im VCL Forum gegeben. Ich finde aber hier ist sie auch gut aufgehoben. (Bitte lieber Moderator, hau mich nich :wink: )

Bei dem anderen Post mit dieser Frage trat das Problem zur Designzeit auf. Bei mir tritt es zur Laufzeit auf :-(

Ich habe folgendes Problem:

Ich erstelle eine Combobox auf einem Panel, nicht zur Designzeit, sondern zur Laufzeit:
Das sieht in etwa so aus :


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:
82:
83:
84:
85:
86:
87:
88:
89:
90:
unit Superchart;

interface

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

type
  TSuperchart = class(twincontrol)
private
  heimtore : integer;
  gasttore : integer;
  anzeige : TPanel;
  heimdisplay : TCombobox;
  gastdisplay : TCombobox;
  heimtordisplay : TEdit;
  gasttordisplay : TEdit;
  abpfiff : TButton;
  spiellabel : TLabel;
  heimlabel : TLabel;
  gastlabel : TLabel;
  heimtorlabel : TLabel;
  gasttorlabel : TLabel;
  procedure setlabel(labeltext :string);
  function getlabel : string;
  procedure abpfiffklick(Sender : TObject);
  procedure heimtordisplayklick(Sender : TObject);
  procedure gasttordisplayklick(Sender : TObject);

protected
  { Protected declarations }

public
  constructor Create(Owner: TComponent; xalign, yalign : integer);
  destructor Destroy; override;
  procedure setfarbe(farbe :TColor);
  function wersieger : string;
  function torvorsprung : integer;

published
  property spielname: string read getlabel write Setlabel;
end;


... (einiges irrelevantes weggekürzt)

constructor TSuperchart.Create(Owner: TComponent; xalign, yalign : integer);
var
topalign_head : integer;
topalign_display : integer;
begin
  inherited create(Owner);
topalign_head := 20;
topalign_display := 5;
heimtore := 0;
gasttore := 0;

  left := xalign;
  top := yalign;
  height := 75;
  width := 630;

anzeige := TPanel.Create(Self);
with anzeige do begin
  Parent := self;
  height := self.height -15;
  width := self.Width;
  Top := 15;
end;

heimdisplay := TCombobox.Create(self);
with heimdisplay do begin
  Parent := anzeige;
  Width := 180;
  left := 5;
  Top := topalign_head;
end;

gastdisplay := TCombobox.Create(self);
with gastdisplay do begin
  Parent := anzeige;
  Width := 180;
  left := 225;
  Top := topalign_head;
end;

heimdisplay.Text := 'Testmannschaft';
//  heimdisplay.Items.LoadFromFile(extractfilepath(paramstr(0))+'mannschaften.txt');
end;

Es soll also beim erzeugen des Objekts der Inhalt der Textdatei in die Comboboxen geladen werden...

aber jeder Aufruf einer Methode der Combobox wie z.b.

Delphi-Quelltext
1:
//  heimdisplay.Items.LoadFromFile(extractfilepath(paramstr(0))+'mannschaften.txt');                    

oder

Delphi-Quelltext
1:
heimdisplay.items.add('Mannschaftsname');                    

führt zu der Fehlermeldung:
Zitat:
Control " has no parent window.

Obwohl doch Anzeige als Parent festgelegt wurde. :(

Aus den Postsa zu dem Thema bin ich nicht schlau geworden.
Holf mir mal bitte einer.....

Catweasel

Moderiert von user profile iconTino: Delphi- & Quote-Tags hinzugefügt.


barfuesser - Di 18.11.03 17:47

Kann es sein, daß Dein SuperChart kein ParentWindow hat?

barfuesser


Mossi - Di 18.11.03 20:44

Ich hab jetzt zwar gerade kein Delphi zur Hand aber versuch mal folgendes:


Quelltext
1:
heimdisplay := TCombobox.Create(anzeige);                    


Bin mir zwar jetzt nicht hundertprozentig sicher, ob es so klappt, aber wenn ich mich recht erinnere, hab ich es auch so gemacht (siehe oben genanntes Problem)

EDIT: Zusätzlich aber auch noch die Parenteigenschaft setzen


Tendl - Fr 23.01.04 15:03

Ich kämpfe momentan mit dem gleichen Problem, habe aber sicher einen Parent definiert:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
  ComboBox1 := TComboBox.create(self);
  ComboBox1.top := 10;
  ComboBox1.left := 80;
  ComboBox1.parent := self;
  combobox1.Items.Add('Diashow');
  combobox1.Items.Add('Unerase');
  combobox1.Items.Add('Move');
  combobox1.Items.Add('Copy');
  combobox1.Items.Add('Save');
  combobox1.Items.Add('Undo');
  //combobox1.ItemIndex:=0;
  //combobox1.Text:=combobox1.Items[0];


Die Fehlermeldung kommt nach den ersten Items.Add. Wenn ich auf die Items.Adds verzichte funktioniert es soweit (mit fehlender Textliste).

Wie war den Eure Lösung?

Thomas


Motzi - Fr 23.01.04 16:14

Das Problem ist wahrscheinlich, dass dein SuperChart zu dem Zeitpunkt zu dem du die Controls erzeugst selbst noch keinen Parent hat...

@Tendl: wo steht dieser Code? Also wann erzeugst du die Combobox? Was ist Self für ein Objekt?


Tendl - Fr 23.01.04 17:04

Hallo Motzi,

der Code steht in einer abgeleiteten Klasse von TCustomPanel


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:
  TButton_Setup = class(TCustomPanel)
  private
    Label1: TLabel;
    ComboBox1: TComboBox;
  protected
  published
  public
    constructor create(AOwner: TComponent); override;
  end;

.....


constructor TButton_Setup.create(AOwner: TComponent);
begin
  inherited create(AOwner);
  align := alClient;

  Label1 := TLabel.create(self);
  with label1 do
  begin
    top := 10;
    left := 20;
    height:=20;
    caption := 'Action:';
    parent := self;
  end;

  ComboBox1 := TComboBox.create(self);
  ComboBox1.top := 10;
  ComboBox1.left := 80;
  ComboBox1.parent := self;
  combobox1.Items.Add('Diashow');
  combobox1.Items.Add('Unerase');
  combobox1.Items.Add('Move');
  combobox1.Items.Add('Copy');
  combobox1.Items.Add('Save');
  combobox1.Items.Add('Undo');
end;



Das Instanz von TButton_Setup liegt seinereits auf eine Tabsheet auf einem Panel auf einem Formular;


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:
  TPanelbar_Setup = class(TCustomForm)
  private
  protected
  public
//...
    Panel2: TPanel;
    PageControl1: TPageControl;
    TabSheets: array[1..10of TTabSheet;
    TButton_Setups: array[1..10of TButton_Setup;
    constructor create(AOwner: TComponent); override;
  published
  end;

.....

constructor TPanelbar_Setup.create(AOwner: TComponent);
var i: integer;
begin
  inherited createnew(AOwner);
  caption := 'Setup Workarea';
  self.Height := 300;
  self.Width := 600;
  self.Top := 200;
  self.Left := 200;

//....

  Panel2 := TPanel.Create(Self);
  Panel2.Name := 'Panel2';
  Panel2.Parent := self;
  Panel2.Align := alBottom;
  Panel2.Height := 200;
  //Panel2.TabOrder := 1;
  panel2.Caption := 'Panel2';
  //panel2.Color := clred;

  PageControl1 := TPageControl.Create(Panel2);
  PageControl1.Parent := panel2;
  PageControl1.Name := 'PageControl1';
  PageControl1.Align := alClient;


//exit;
  for i := 1 to 10 do
  begin
    TabSheets[i] := TTabSheet.Create(PageControl1);
    TabSheets[i].Parent := PageControl1;
    TabSheets[i].PageControl := PageControl1;
    TabSheets[i].Caption := 'Button ' + inttostr(i);
    TabSheets[i].Name := 'TabSheet' + inttostr(i);
    TButton_Setups[i] := TButton_setup.create(self);
    TButton_Setups[i].Parent := TabSheets[i];
  end;

end;


Die Instanz von TPanelbar_Setup wird ebenfalls zur Laufzeit erzeugt mit


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TSpecThumbWorkArea.BtnClick1(Sender: TObject);
var setup: TPanelbar_Setup;
begin
  setup := TPanelbar_Setup.create(setup);
  setup.ShowModal;
  setup.Free;
end;




Wie beschrieben klappt alles, wenn ich die Items.Add() weglasse.
Der Fehler kommt erst, wenn ich versuche die Items beim Create vorzubelegen.

Sorry, für den vielen Code, aber so werden die Zusammenhänge klarer.

Wenn Du Zeit und Lust hast, dann schau bitte mal rein.

Danke Thomas


Tendl - Mo 26.01.04 13:53

Das Problem ist gelöst:

In der Routine befinden sich die Zeilen


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
constructor TPanelbar_Setup.create(AOwner: TComponent); 
var i: integer; 
begin 
  inherited createnew(AOwner); 
//....

  for i := 1 to 10 do 
  begin 
    //....
    TButton_Setups[i] := TButton_setup.create(self); 
    TButton_Setups[i].Parent := TabSheets[i]; 
  end
;

Die vorletzte Zeile weist den Parent zu, d.h. im TButton_setup.create wird dies nicht getan. Damit ergeben sich die genannten Probleme, nicht nur bei Comboboxen sondern z.B auch bei Radiogroups.

Die Korrektur lautet:

Delphi-Quelltext
1:
    TButton_Setups[i] := TButton_setup.create(TabSheets[i]);                    


Und im Create von TButton_setup:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
constructor TButton_Setup.create(AOwner: TComponent);
var itemlist:TStringlist;
begin
  inherited create(AOwner);
  parent:=TWINControl(AOwner);
 /....
end;


Mir ist das Problem damit klar, ich hoffe anderen auch
Thomas


catweasel - Mo 26.01.04 14:34

ja, dieses Problem lässt sich so tatsächlich lösen. Aber da gibts noch ein paar andere Klippen...

hab das mit dem Control " has no parent window mal ein bischen unter die Lupe genommen....

Es sieht für mich folgendermassen aus:
Klingt vielleicht ein bischen Konfus, aber so stellt sich das durch rumprobieren für mich dar...:


These: Ein Objekt existiert erst wirklich dann wenn der Constructor wirklich und endgültig abgeschlossen ist....
weil : keine Methode die NUR als Methode implementiert ist funktioniert..... Man kann keine Listboxen füllen, nicht auf den Canvas einer Paintbox malen, etc....
Nur was als Property deklariert ist (width, top, etc.....) lässt sich im Constructor verwenden. (Als Ausnahme konnte ich nur die LoadFromFile() funktion von TImage finden).

@Tendel

auch wenn man die Parent Zuweisung NICHT vergessen hat, kann einem diese Meldung begegnen.....

Das wäre auch möglich...

Verlagere deinen Code mit den items.Add() einfach in eine eigene Funktion die du im Hauptprogramm direkt nach dem create() ausführen lässt....

combo1 := TComboBox.create(self);
combo1.fuellen;

dann funktioniert es.....

Das Unschöne dabei ist: Dadurch das man "extra nur deswegen" eine Prozedur oder Funktion öffentlich machen muss :-(
Nach meiner Auffassung von OOP wäre es am elegantesten wenn nichts ausser "Übergaberoutinen" öffentlich sind.....

Catweasel


Motzi - Mo 26.01.04 16:21

@catweasel: Methoden die erst nach Abarbeitung des Constructors ausgeführt werden sollen kann man auch anders lösen...

TObject führt nämlich eine virtuelle Methode namens "AfterConstruction" ein, die immer automatisch sofort nach dem Constructor aufgerufen wird (zB löst die TForm das OnCreate-Event in ihrer überschriebenen Version dieser Methode aus).

Aber die Meldung "Control xxx has no parent window" kann einem auch durchaus außerhalb des Constructors begegnen...!


catweasel - Mo 26.01.04 19:12

Zitat:
Aber die Meldung "Control xxx has no parent window" kann einem auch durchaus außerhalb des Constructors begegnen...!


Ja, das kann sie.. Da aber, im Falle des Auftauchens während der Abarbeitung eines Constructors deuten die " zwei Einfachen Anführungszeichen auf einen leeren Komponentennamen hin......
Eigentlich sollte der aber bereis existieren, oder?

es ist ja eben nicht ...control 'xxx' has no... , sondern control " has no...

Catweasel


Motzi - Mo 26.01.04 21:22

Ja, richtig... das '' deutet auf einen leeren Komponentennamen hin, aber wo sollte zum Zeitpunkt der Ausführung des Constructors denn bereits ein Name existieren? Bei zur Laufzeit erstellten Komponenten bist schließlich du dafür verantwortlich ihnen einen Namen zu verpassen (sofern es überhaupt notwendig ist - man kann den Namen auch einfach leer lassen).

Du darfst an dieser Stelle halt nicht Klassenname (zb TButton) und Komponentenname (zb Button1) verwechseln...!


XelaRellum - Mi 15.03.06 13:02
Titel: Überschreiben von "SetParent" hat bei mir gut funktioniert.
Ich hatte eben ein ganz ähnliches Problem: ich hatte ein eigenes Control erzeugt, dass auf einem Tabsheet wiederum eine TComboBox anlegt und der dann auch gleich ein paare Werte zuweist. Bei mir liess sich das Problem (ohne Änderungen an anderen Programmteilen) entfernen, indem ich "SetParent" überschrieben habe und dort dann die ComboBox Werte hinzugefügt habe. Dies nur zur Info