Problem mit dynamischen Fenstern



  • 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++



  • Hallo

    Wenn beim konkreten Einsatz eines Templates die Implementation des Templates fehlt, kommt der Linker-Fehler den du ja schon kennst. Der Compiler kann nämlich erst beim konkreten Einsatz des Templates aus der Implementation des Template eine konkrete Funktion zusammensetzen, nach Übersetzung von Markos und Templates darf es nur noch exakt festgelegte Datentypen geben. Wenn du also dein CreateKomp-Template in deinem gesamten Projekt für insgesamt 3 verschiedene Datentypen spezifizierst, sind nach dem Kompilieren dann drei verschiedene Funktionen vorhanden, und das Template ist verschwunden.
    Kann der Compiler aber das deklarierte Template für einen konkreten Einsatz nicht auflösen kann weil die Implementation fehlt, merkt das der Linker und bricht ab. (Warum nicht gleich der Compiler den Fehler findet und abbricht ist mir jetzt auch nicht klar)

    bis bald
    akari



  • akari schrieb:

    Kann der Compiler aber das deklarierte Template für einen konkreten Einsatz nicht auflösen kann weil die Implementation fehlt, merkt das der Linker und bricht ab. (Warum nicht gleich der Compiler den Fehler findet und abbricht ist mir jetzt auch nicht klar)

    Weil du etwa eine explizite Template-Spezialisierung durchaus in einer anderen Quelldatei unterbringen kannst.



  • Hallo,
    ja, das klingt logisch. Ich verstehe dann nur nicht, warum ich die template Funktionen nicht mit dem Schlüsselwort "inline" versehen kann und sie dann auch in die .cpp Datei schreiben kann.

    edit: Mir ist noch eine Frage aufgekommen, die ich euch gerne stellen möchte.

    Wenn ich nun auf mein dynamisches Forumal ein Label setzen, dann kann ich darauf zugreifen, indem ich den Rückgabetyp von CreateKomp nutze. Also so:

    TLabel *label = f.CreateKomp<TLabel>(10,10,f.width-40,200,"");
     label->AutoSize = false;
    

    Wenn ich nun jedoch auf das Formular zugreifen möchte, dann habe ich ein Problem. Dieses erstelle ich nämlich im Konstruktor:

    TCreateForm f (0,Form1->Width/2,Form1->Height/2,400, 350, clHighlight, "Hello");
    

    Da ein Konstruktor keinen Rückgabetyp hat, kann hier nicht so arbeiten wie bei CreateKomp. Wie könnte ich dennoch auf das dynamisch erstelle Formular zugreifen? Führt nichts um eine Methode herum, die das Formular erstellt? Ich finde es nämlich schöner, wenn die Form im Konstruktor gemacht wird.

    Vielen Dank für eure Hilfe
    lg, freakC++



  • ...gelöscht...



  • Hallo

    freakC++ schrieb:

    Hallo,
    ja, das klingt logisch. Ich verstehe dann nur nicht, warum ich die template Funktionen nicht mit dem Schlüsselwort "inline" versehen kann und sie dann auch in die .cpp Datei schreiben kann.

    Soweit ich weiß, sollte laut C++ Standard mit dem Schlüsselwort export genau das funktionieren, aber die meisten Compiler (auch der von Borland) unterstützen das nicht. Für weitere Fragen zum Hintergrund solltest du aber besser mit einem neuen Thread im C++ Forum selber nachfragen.

    Da ein Konstruktor keinen Rückgabetyp hat, kann hier nicht so arbeiten wie bei CreateKomp. Wie könnte ich dennoch auf das dynamisch erstelle Formular zugreifen? Führt nichts um eine Methode herum, die das Formular erstellt? Ich finde es nämlich schöner, wenn die Form im Konstruktor gemacht wird.

    Doch ein Konstruktor hat einen Rückgabetyp, nämlich die erstellte Instanz. Ich verstehe dein Problem nicht. In deinem Beispiel-Code erstellt du zunächstmal keine dynamische Instanz, sondern eine statische (in der Variablen f). Und warum du f nicht nutzen können solltest oder warum du eine Funktion haben willst, verstehe ich auch nicht.

    bis bald
    akari



  • Hallo akari,
    aus einem geraden ebenfalls aktuellen Thread habe ich eine Idee bekommen: Ich möchte ein dynamisches Formular erstellen, darauf einen Button kleben und wenn dieser Button gedrück wird, soll nur das dynmische Formular geschlossen werden. Ich kann der OnClick Methode des Buttons nicht einfach "Close()" zuweisen, da sonst das ganze Programm geschlossen wird. Ich muss also einen Zeiger auf mein dynamisches Fenster übergeben.

    TButton *button = f.CreateKomp<TButton>(10,210,f.width-40,60,"Ok");
     button->OnClick = Button;
    
    ...
    void __fastcall TForm1::Button(TObject *Sender)
    {
     Close(): //Das ganze Programm würde geschlossen
    }
    

    Ich muss also der Methode Button meine dynamische Form übergeben. Da die Instanz "f" aber vom Typ TCreateKomp ist, kennt sie nicht "Close()". Daher muss ich erstmal die Form zurückgeben. Ich habe das dann so gelöst:

    TForm* GiveForm() //Member von TCreateForm
      {
       return form; //form ist das dynamisch erzeugte Formular
      }
    

    und dann:

    TButton *button = f.CreateKomp<TButton>(10,210,f.width-40,60,"Ende");
     TForm *form = f.GiveForm();
     button->OnClick = Button(Sender,form); //FEHLER!
    ...
    void __fastcall TForm1::Button(TObject *Sender, TForm *form)
    {
     form->Close():
    }
    

    Das geht aber nicht. Warum? Es sei "kein zulässiger Typ". Was muss ich hier machen?

    Ich hoffe, dass Du mein Problem verstehst.
    lg, freakC+



  • Hallo

    Du bringst da einiges durcheinander. Du kannst Eventmethoden nicht einfach um einen Parameter erweiteren, der Typ ist festgelegt. Sondern deine Close-Methode muß ein Member von TCreateForm sein.

    // CreateForm.h
    class TCreateForm : ...
    {
      public :
        void __fastcall CloseMe(TObject* Sender); // Manuell deklarieren
    }
    // CreateForm.cpp
    void __fastcall TCreateForm::CloseMe(TObject* Sender)
    {
      Close();
    }
    // Form1.cpp
    TButton *button = f.CreateKomp<TButton>(10,210,f.width-40,60,"Ende");
    button->OnClick = f.CloseMe;
    

    bis bald
    akari



  • Hallo,
    oh ja, da habe ich tatsächlich etwas durcheinander gebracht. Doch der Code funktioniert nicht ganz, da "Close()" auch in der Methode "CloseMe()" als Member von TCreateForm unbekannt ist, da TCreateForm nicht vom TForm abgeleitet ist.

    Nichtsdetotrotz dachte ich an diese Lösung:

    void __fastcall TCreateForm::CloseMe(TObject *Sender)
    {
     form->Close(); //form ist das dynamische Formular
    }
    

    form wurde ja im Konstruktor erstellt:

    TCreateForm::TCreateForm(TForm* parent, int i_left, int i_top, int i_width, int i_height, TColor clBack, String cap)
    {
     form = new TForm(parent); 
     ...
    }
    

    und ist in TCreateForm als private so deklariert:

    class TCreateForm
    {
     private:
      TForm *form; //die zu erstellende Form
    

    Hier die Zuweisung:

    void __fastcall TForm1::L_KB_hlpClick(TObject *Sender)
    {
     TCreateForm f (0,Form1->Width/2,Form1->Height/2,400, 350, clHighlight, "Hello");
     TButton *button = f.CreateKomp<TButton>(10,210,f.width-40,60,"Ok");
     button->OnClick = f.CloseMe;
    }
    

    Wenn ich jedoch auf den Button klicke, kommt eine Zugriffsverletzung. Das verstehe ich nicht, da es eigentlich mit der Zuweisung "form->Close()" keine Schwierigkeiten geben sollte

    Weißt Du da auch bescheid 🤡 ?
    lg, freakC++



  • f wird zerstört, wenn die Funktion und somit der Scope verlassen wird. Danach hast Du ein undefiniertes Verhalten...



  • Ach *gegendieWandlauf*....warum seh ich das nicht? Vielen Dank für den Hinweis. Jetzt läuft es natürlich! Super 👍

    Machts gut und liebe Grüße
    freakC++


Anmelden zum Antworten