Problem mit dynamischen Fenstern



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



  • 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


Anmelden zum Antworten