Problem mit dynamischen Fenstern
-
Dafür benutze ich selber eine Template-Funktion:
template <typename T> T* Create(WinControl *Owner, WinControl *Parent, int left, int top, int width, int height, String caption = String()) { T *t = new T(Owner); t->SetBounds(left, top, width, height); t->Parent = Parent; t->Caption = caption; return t; }
bzw. je nach Control entsprechend spezialisierte Funktionen z.B. beim Button gleich noch die Angabe des dynamischen OnClick-Ereignisses etc.
Edit: falsche Funktionssignatur sowie den Rückgabewert korrigiert...
-
Hallo Th69,
das sieht sehr gut aus. Sowas habe ich gebraucht. Doch eine Sache verstehe ich nicht: An was bzw. was gibts Du zurück? Da steht ich noch aufm Schlauch.edit: Außerdem haperts bei der Template Deklaration (ich habe noch nicht oft mit Templates gearbeitet)
... template <typename T> void CreateKomp<T>(TWinControl *Owner, TWinControl *Parent, int left, int top, int width, int height, String caption); ... void TCreateForm::CreateKomp<T>(TWinControl *Owner, TWinControl *Parent, int left, int top, int width, int height, String caption) { T *t = new t(Owner); T->SetBounds(left, top, width, height); T->Parent = Parent; //T->Caption = caption; }
Fehler:
Templates müssen Klassen oder Funktionen sein //CreateKomp ist doch eine Funktion
Größe von 'CreateKomp' unbekannt oder NullWisst ihr da auch bescheid
?
Vielen Dank
lg, freakC++
-
Vermutlich an irgendein member des beeinhaltenden Formulars. Oder der Rückgabewert wird ignoriert, wenn er nicht weiter benötigt wird. Die VCL übernimmt durch Angabe eines Owner Objekts den Besitz über das dynamisch erzeugte Objekt, sodass man es nicht mehr selbst zerstören muss.
-
Hallo
An was bzw. was gibts Du zurück? Da steht ich noch aufm Schlauch.
Das Template gibt einen Zeiger auf die in der Funktion erstellten Komponente zurück. Der Rückgabe-Typ entspricht dem konkreten Template-Parameter T.
Die Rückgabe kannst du weiterverwenden, wenn du sie noch brauchst, must du aber nicht.bis bald
akari/Edit : war wohl ein bißchen langsam.
-
Ahh...ok! Vielen Dank
Ich habe noch zu meinem vorigen Post etwas hinzugefügt
Danke!!
lg, freakC++
-
Ja, du brauchst die Methode TCreateForm::CreateKomp<T> nicht, dazu ist ja das Template CreateKomp<T> da. Abgesehen davon ist die Methodensignatur falsch, bei allen Methode, die einen Template Parameter erwarten, musst du ein template<typename T> voranstellen (T kann natürlich durch jeden anderen gültigen Namen ersetzt werden).
// non-member template Funktion template<typename T> void func() { ... } // member template Funktion class Test { template<typename T> void func() { ... } };
-
Sorry, ich hatte ein bißchen schlampig die Template-Funktion hingeschrieben, aber habe es nun korrigiert.
Diese (freie) Funktion mußt du dann in einen Header packen oder aber zu Beginn deines Quelltextes.Benutzung dann folgendermaßen:
TLabel *label = Creata<TLabel>(this, this, 10, 10, 100, 16, "Hello World!"); label->Color = clRed;
Der Rückgabewert ist, wie schon geschrieben, der Zeiger auf die Control-Instanz, so daß du dann weitere Eigenschaften setzen oder Methoden dafür aufrufen kannst.
Um die Freigabe brauchst du dich aber nicht kümmern (außer du übergibst 0 als ersten Parameter).Alternativ könntest du diese Funktion auch in ein Basisformular als Methode packen (und deine eigenen Formulare erben dann hiervon) - dann könntest du auf den ersten Parameter 'Owner' verzichten und direkt 'this' einsetzen.
-
Hallo,
vielen Dank für deine Antwort. Das Implementieren klappt soweit, doch eine Hürde kann ich leider noch nicht überwinden. Ich möchte, dass deine Methode Member einer Klasse wird. Meine Klassendeklaration sieht soweit so aus:class TCreateForm { private: int width, height, left, top; TColor clBack; TForm *form; //die zu erstellende Form public: TCreateForm(int width, int height, int left, int top, TColor clBack, String cap); ~TCreateForm(); template <typename T> T* CreateKomp(TWinControl *Owner, TWinControl *Parent, int left, int top, int width, int height, String caption); void DestroyKomp(); };
Mein Aufruf lautet:
TCreateForm f(300,300,500,600,clHotLight,"C++ ist cool"); //funktioniert f.CreateKomp<TLabel>(f,f,100, 100, 300, 106, "Hello World!"); //funktioniert nicht --> ich übergebe f, da ich das Label auf diesem Formular haben möchte
Das Problem ist nun, dass ich folgende Fehlermeldung bekomme:
Keine Übereinstimmung für 'TCreateForm::CreateKomp<T>(TCreateForm,TCreateForm,int,int,int,int,char *)' gefunden
Die Fehlermeldung ist mir schon irgendwie klar, da als Parameter ja ein Formular gefordert wird, doch ich nun ein Objekt des Types TCreateForm übergebe. Wie kann ich dieses Problem lösen? Ich habe bis jetzt die zu erstellende Form als private Member. Muss meine Klasse etwa von TForm abgeleitet sein oder wie muss ich vorgehen?
Vielen Dank
lg, freakC++
-
Hast dus mal so versucht?
f.CreateKomp<TLabel>(&f,&f,100, 100, 300, 106, "Hello World!");
-
Hallo Braunstein,
nein, das funktioniert nicht. Das kann ja auch gar nicht laufen, da die Instanz f vom Typ TCreateForm ist.Hat jemand eine Idee, was ich hier machen muss. Ich probiere mal von TForm abzuleiten....
Vielen Dank
lg, freakC++edit: Der Versuch von TForm abzuleiten bringt mich hier auch nicht weiter.
-
TCreatForm soll deine Form doch nur erzeugen und nicht besitzen. Deine neue Form kann darauf ja auch nicht dargestellt werden (Parent) da TCreateForm ja nicht von TWinControl abgeleitet ist. Willst du das überhaupt?
Wenn TCreateForm deine Form nur erzeugen aber nicht darstellen soll übergib doch die Zeiger von der Form in der dein Objekt dargestellt werden soll oder 0 wenn du es noch nicht weißt.
-
Hallo,
ja Du hast recht. Die Klasse TCreateForm soll nur das Formular erstellen und auf dieses dann Komponenten setzen. Die zu erstellende Form ist ja als private Member deklariert. Wie soll ich denn jetzt von außen einen Zeiger darauf übergeben??Ich verstehe zwar, was Du meinst, doch könntest Du mal ein bisschen Code zeigen. Das würde mich sehr helfen.
Vielen Dank für dein Bemühen.
lg, freakC++
-
Wenn die zu erstellende Form eine Membervariable deiner Klasse ist, dann brauchst die Parent und Owner ja nicht mehr an CreateKomp zu übergeben.
Wie ich sehe übergibst du ja auch schon die Position und die Caption für die neue Form im Konstruktor von TCreateForm. Dann übergibst du alles noch mal an CreateKomp.
So wie das aussieht sollte CreateKomp eigentlich eine freie Funktion sein. Wenn du die unbedingt in einer Klasse halten willst dann kannst du auch die membervariablen dieser Klasse verwenden.
z.Bsp. so:class TCreateForm { private: int width, height, left, top; TColor clBack; String caption; TForm *form; //die zu erstellende Form TForm* parent; public: TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap); ~TCreateForm(); template <typename T> void CreateKomp(); void DestroyKomp(); }; template<typename T> void TCreateForm::CreateKomp<T>() { // Initialisierung erfolgt hier mit den Membervariablen deiner Form T *t = new t(parent); t->SetBounds(left, top, width, height); t->Parent = parent; t->Caption = caption; form = t; }
Du solltest hier aber besser gleich deine Klasse zum Template machen statt nur die Funktion.
Nochmal die Frage. Warum eine Klasse und nicht eine freie Funktion?
-
Hallo Braunstein,
vielen Dank für den Code. Also ich möchte eine Klasse erstellen, weil diese ja verschiedene Aufgaben übernehmen soll. Über den Konstruktor wird dynmaisch ein neues Formular angelegt. Mittels der Methode CreateKomp soll dann auf dieses neue Formular beliebige Komponenten, wie TLabel, gesetzt werden. Ich kann die Klasse nicht ganz zum Template machen, da ja anfangs immer ein Fomular erstellt werden soll und dann beliebig andere Komponenten. Sonst müsste ich immer wieder den KOnstruktor aufrufen.Bei deinem Code ist mir jedoch noch eine Sache unklar:
TCreateForm f(f,300,500,600,clHotLight,"Form Caption");
Solch eine Definition funktioniert leider immer noch nicht. Damit sollte nun eigentlich der Konstruktor aufgerufen werden und ein Formular erstellt werden.
Der Konstruktor sieht so aus:
TCreateForm::TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap) { Application->CreateForm(__classid(TForm), &form); form->Show(); form->Width = width; form->Height = height; form->Left = left; form->Top = top; form->Color = clBack; form->Caption = cap; }
Weißt Du da auch bescheid
?
Danke!
lg, freakC++
-
Ach so. Da habe ich dich vorher missverstanden.
Ich würde das dann so machen.TCreateForm::TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap) { form = new TForm(0); // Wir brauchen hier keinen Owner da wir im Destruktor selber löschen können form->Parent = parent; form->Width = width; form->Height = height; form->Left = left; form->Top = top; form->Color = clBack; form->Caption = cap; form->Show(); }
Die Erzeugung könnte dann so laufen.
TCreateForm f(parentForm,300,500,600,clHotLight,"Form Caption");
parentForm ist dann ein Zeiger auf die Form in der deine Form dargestellt wird oder 0 falls deine Form ein eigenes Fenster ist.
-
Hallo Braunstein,
ich habe deinen Code mal umgesetzt und das dynamische Formular wird nun auch erstellt, doch nun möchte ich ja auch die beliebige Komponente auf diesem Formular erstellen,. Ich muss also die CreateKomp - Methode abändern, da ich sie Parameter benötigt (bei dir hätte sie die gleiche breite, höhe etc. wie das erstellte Formular).template<typename T> void TCreateForm::CreateKomp<T>(int left, int top, int width, int height, String cap) { T *t = new t(form); //durch Konstruktor dynamisch erstelltes Formular t->Parent = form; t->Show(); t->SetBounds(left, top, width, height); t->Caption = caption; }
So würde dann der Aufruf von CreateKomp aussehen:
TCreateForm f(0,500,300,400,400,clHotLight,"Form Caption"); //funktioniert f.CreateKomp<TLabel>(100, 100, 300, 106, "Hello World!"); //funktioniert leider noch nicht :(
Ich bin mir jetzt nicht sicher, ob ich genügend Parameter habe. Eigentlich sollte nun ein Label mit der Aufschrift "Hello World!" auf dem gerade erzeugten Formular erscheinen. Das tut es aber nicht. Es kommt ein Linkerfehler:
Unresolved external 'void TCreateForm::CreateKompStdctrls::TLabel(int, int, int, int, System::AnsiString)' referenced from ...
Ich glaube, dass da irgendwas mit den parents etc. nicht stimmt. Warum ist das alles für mich so viel komplizierter, wenn ich das in eine Klasse packe?
Vielen Dank
lg, freakC++
-
Hallo
Kann es sein, das du die Template-Methode in CreateForm.h deklariert hast, im CreateForm.cpp implementiert hast und der Aufruf f.CreateKomp<TLabel>( in einer anderen Unit-Implementation steht, die nur Createform.h included? Wenn ja : Bei Templates muß immer die ganze Implementation zugänglich sein, nicht nur die Deklaration. Deshalb must du auch die Implementation der Template-Methode in den Header CreateForm.h verschieben.
bis bald
akari
-
Hallo akari,
jaaa, daran hat es gelegen! Vielen, vielen Dank! Nun funktioniert alles so wie ich es haben möchte. Ist das bei templates allgemein so? Wenn die Funktionen dann aber schon in der Klassendeklaration implementiert sind, sind sie dann nicht automatisch inline?Ach, was würde ich geben, um euer Wissen zu haben
Bis zum nächsten Thread!
lg, freakC++
-
Hallo
Ist das bei templates allgemein so?
Ja, das ist bei allen Template-Funktionen so. Egal ob Member einer Klasse oder nicht.
Wenn die Funktionen dann aber schon in der Klassendeklaration implementiert sind, sind sie dann nicht automatisch inline?
Ja, das macht die Template-Methode automatisch inline. Templates müßen immer inline verfügbar sein, das folgt aus ihrer Funkionalität.
bis bald
akari
-
Hallo,
achso! Das ist gut zu wissen. Was wäre denn, wenn sie nicht inline wären?Vielen Dank
lg, freakC++