Formular zur laufzeit erstellen mit vielen fragen



  • hey du... 😉

    ich schreibe gerade an einem msn messenger clone.
    das klappt soweit auch gut, jedoch habe ich mit dem erstellen einer form zur laufzeit so meine probleme. viele aspekte sind ja zu beachten. ich versuche meine probleme so gut wie möglich in kategorien zu unterteilen und beschreiben.
    let's rock.....

    Das erstellen einer Form zur laufzeit.
    Da fangen meine probleme eigentlich schon an, da ich noch nie sowas von gebrauch machte. ich fange das ganze wie folgt an:

    TForm *Msg = new TForm(this);
        Msg->Caption = "Unterhaltung mit " + Online->Items->Strings[Online->ItemIndex];
        Msg->Height = 400;
        Msg->Width = 400;
        Msg->Show();
    

    wäre das soweit richtig? so als basis?
    Jedenfalls wird bei klicken in der listbox, ein formular erstellt mit den gesetzten eigenschaften. ich bitte euch in diesem kapitel um hilfe 🙂

    Das einmalige erstellen wenn bereits erstellt wurde.
    Jetzt stellt sich mir die frage, wie erstelle ich pro user jeweils nur ein fenster? wenn ihr einen messenger habt (MSN, icq, etc.) dann wisst ihr ja das diese jeweils pro benutzer nur einmal erstellt werden, also wenn das fenster bereits offen ist. wie regle ich sowas?

    Die events
    Gang speziell hier kratz ich mir am kopf.
    Wie greiffe ich an die events des erstellten formular zu?
    zur design zeit wäre das ja pipsi einfach über den objektinspektor.
    stellt euch vor, ich habe an den zur laufzeit erstellten formularen ja z.B. ein memo, ein edit und einen button. ich muss ja dann z.B. beim button auf OnClick zugreifen, damit ich auch an den user eine message schicken kann 😃
    ausserdem müsste ich hier auch das schlüsselwort delete mal einsetzen, damits keine schmutzverletzung gibt.

    Weitere fragen
    Fallen mir sicher noch ein... 🕶



  • Hallo

    wäre das soweit richtig? so als basis?

    Ja, genau so ist richtig. Natürlich sollte dann statt TForm eine von dir erstellte, von TForm abgeleitete Klasse aufgerufen werden, damit du bereits alle Steuerelemente integriert hast.
    Erstell also in deinem Projekt ein Formular, das du so aufbaust, wie du es dir als Chat-Fenster vorstellst. Das nennst du dan z.B. TChatForm. Dann includest du die Header des Forms in deinem Hauptprogramm, wo du die Chatfenster erstellen willst. Dann ist der Aufruf

    TCharForm *Msg = new TChatForm(this);
    Msg->Caption = "Unterhaltung mit " + Online->Items->Strings[Online->ItemIndex];
    Msg->Height = 400;
    Msg->Width = 400;
    Msg->Show();
    

    Natprlich sollte man nicht vergessen, jede dynamische Fenster-Instanz wieder per delete zu löschen.

    Jetzt stellt sich mir die frage, wie erstelle ich pro user jeweils nur ein fenster? wenn ihr einen messenger habt (MSN, icq, etc.) dann wisst ihr ja das diese jeweils pro benutzer nur einmal erstellt werden, also wenn das fenster bereits offen ist. wie regle ich sowas?

    Du hast ja sicher für jeden User ein Objekt/Struct, wo du Daten über den User speicherst (zB. Name oder IP-Adresse). Die erweiterst du einfach um eine Referenz auf ein TChatForm. Standardwert ist NULL. Wenn du für diesen User einen Chatfenster erstellt, speicherst du die Referenz der Instanz dadrin:

    // Datenstruktur-Klasse eines Users
    class TUser 
    {
      ...
      private :
      TChatForm *iChatForm; // Referenz des Chatfensters zu diesem User
    
      public :
      __property TChatForm *ChatForm = {read = iChatForm}; // Property auf ChatForm, um es von außen lesen zu können
      TUser() {... ChatForm = NULL}; // Konstruktor : Referenz auf NULL setzen
      int OpenWindow(); // Memberfunktion zum Öffnen des Fensters
      int CloseWindow(); // ... zum Schließen des Fensters
      }
    
    int TUser::OpenWindow()
    {
      if (iChatForm != NULL) return(-1); // Wenn Fenster vorhanden, abbrechen
      iChatForm = new TChatForm(NULL); // Für diesen User ein Chatfenster erstellen
      iChatForm->Tag = (int) this; // Adresse der Userdaten in Form speichern, um Usaerdaten vom Form aus zu finden
      ... // Weitere Werte des Fensters setzen
      }
    
    int TUser::CloseWindow()
    {
      iChatForm.Close(); // Fenster schließen
      delete iChatForm; // Fenster löschen
      iChatForm = NULL; // Referemz zurücksetzen
      }
    ...
    // Dann kannst du für einen User ein Fenster öffnen und schließen
    TUser *User;
    ...
    User->OpenWindow();
    User->CloseWindow();
    

    Gang speziell hier kratz ich mir am kopf.
    Wie greiffe ich an die events des erstellten formular zu?

    Zuerst einmal kannst du durch dein bereits vorgefertigtes ChatForm bereits viele Events innerhalb des Forms bereits zur Entwurfszeit erstellen.
    Zur Laufzeit machst du das so:

    // irgendwo im Hauptformular eine Eventfunktion vom Typ TNotifyEvent,
    // die das schließen eines ChatForms regelt
    void __fastcall TMainForm::OnChatFormClose(TObject *Sender)
    {
      TButton *CloseButton = dynamic_cast<TButton *> (Sender); // Sender als TButton
      TChatForm *ChatFormToClose; // Das Fenster, dessen Close-Button den Event ausgelöst hat
      TUser *User; // Der user, dessen CHatfenster geschlossen werden soll
    
      if (CloseButton == NULL) return; // Wenn kein Button, abbrechen
      ChatFormToClose = dynamic_cast<TChatForm *> (CloseButton->Owner); // Chatfenster von Button bestimmen
      if (ChatFormToClose == NULL) return; // Wenn kein Chatfenster, abbrechen
      User = (TUser *) ChatFormToClose->Tag; // User aus Tag bestimmen
      User->CloseWindow(); // User-Objekt schließt das Fenster
      }
    
    // dann mußt du jedem neu erschaffenen ChatForm nur den Event setzen
    TUser *UserToChat;
    ...
    UserToChat->OpenWindow;
    UserToChat->ChatForm->ButtonCloseClick = OnChatFormClose;
    

    ich hoffe du kannst das nachvollziehen. ansonsten schreib.

    bis bald
    akari



  • Hallo

    wen ich's mir von mal überlege, gehört die Eventfunktion OnChatFormClose (und jede andere Userspezifische Eventfunktion) natürlich nicht zum TMainForm, sondern lieber direkt zu TUser. Dann kann man sich es sparen, den User zu bestimmen.
    Und in TUser->CloseWindow() sollte man zuerst über prüfen, ob ein ChatForm vorhanden ist.
    Und natürlich Rückgabewerte...

    bis bald
    akari



  • hey vielen dank für deine ausführliche hilfe! 👍
    da hast du mich doch gleich auf eine gute lösung gebracht. ich habe nähmlich wie du geschrieben hast ein formular erstellt, memo, edit, button drauf getan und dann im messenger projekt abgespeichert.

    nun habe ich dort ja drei dateien. die *.cpp, *.dfm (oder so:)), *.h
    das macht das ganze nähmlich nun leicht für mich da ich alle benötigten kompenenten ebreits auf dem formular habe und sie nicht extra mit dem new operator anfordern muss. lediglich die form erstelle ich zur laufzeit neu.
    also eben, habe nun diese drei dateien und in meinem messenger projekt inkludiert "chatwindow.h". dann im messenger code:

    TChat *ChatWindow = new TChat(this);
        ChatWindow->Show();
    

    und fertig 😉
    ist das ganze gut und ok so wie ich das nun mache?
    das erspaart mir einiges. auch muss ich die events nicht extra schreiben. ich kann nun ja einfach das formular entsprechend bearbeiten (events etc.) und dann wieder abspeichern.



  • Hallo

    ist das ganze gut und ok so wie ich das nun mache?

    naja, funktioniert es denn? Mein Ansatz finde ich natürlich gut 😃

    bis bald
    akari



  • yepp es funktioniert 😉
    aber das mit dem nur einmal pro user erstellen leuchtet mir noch nciht ganz ein.

    verstehst du wie ich das meinte?
    es gibt zwei möglichkeiten wie man das fenster neu erstellt:

    1. der benutzer macht es selber, indem er auf einen namen in der listbox doppelt klickt.
    2. der benutzer kriegt eine nachricht geschickt, es popt sich also ein neues fenster auf.

    diese zwei möglichkeiten gibt es.
    jetzt muss ich natürlich verhinern das mehrere fenster erstellt werden, wenn bereits eins des betroffenen user erstellt wurde.



  • Hallo

    aber das mit dem nur einmal pro user erstellen leuchtet mir noch nciht ganz ein.
    verstehst du wie ich das meinte?
    es gibt zwei möglichkeiten wie man das fenster neu erstellt:

    Du verwechselst den User an sich und die Daten, die ein Client über einen User sammelt.
    Der Client braucht über jeden User, mit dem er in Kontakt steht, Daten wie Name und IP-Adresse.
    Dazu habe ich eine Klasse TUser (ab sofort TUserData) angedacht. Als Member bekommt die zB.

    AnsiString iName; 
    AnsiString iIP;
    // und eben
    TChatForm *iChatForm;
    // und oben beschriebene Zugriffsfunktion
    

    dann erstellst du ein Array oder besser noch ein std::vector über TUserData, um alle erreichbaren User zu erfassen. Dann Kannst du zu jedem User über die zugehörigen TUserDaten aus dem Array ein eigenes Fenster öffen :

    include <vector>
    using namespace std;
    ...
    vector<TUserData> UserList;
    TUserData *UserData;
    int Pos = 1;
    
    ...
    UserData = *(UserList.at(Pos)); // Zugriff auf zweiten User, wenn vorhanden
    UserData->OpenWindow();
    

    alle Klarheiten beseitigt?



  • danke für deine hilfe aber ich krieg das nicht hin..
    habe keine ahnung von vector, und so 🙂 kann dein beispiel nicht nachvollziehen 😞



  • Hallo

    danke für deine hilfe aber ich krieg das nicht hin..
    habe keine ahnung von vector, und so 🙂 kann dein beispiel nicht nachvollziehen 😞

    Vielleicht hilfts es ja ein bißchen, wenn du statt vector erstmal DynamicArray nimmst.

    class TMainForm : public TForm
    {
      ...
      DynamicArray<TUserData> UserList; // Liste aller User, die von diesem Client aus zu erreichen sind
      int AddUser(AnsiString Name, AnsiString IDAddress); // Beispielfunktion zum Hinzufügen eines neuen Users
      int OpenChatWindow(int UserNumber); // Bepsielfunktion zum Öffnen eines CHatfensters zu einem bestimmten Users
    
      }
    ...
    
    int TMainForm::AddUser(AnsiString UserName, AnsiString UserIDAdress)
    {
      int Pos = -1; // Position der Userdaten
      UserList.Length++; // neuen Eintrag in der Liste machen
      Pos = UserList.Length -1; // Position bestimmen
      UserList[Pos] = TUserData(); // Eintrag initialisieren
      UserList[Pos].Name = UserName; // Namen zuweisen
      UserList[Pos].IPAdress = UserIPAddress; // Adresse zuweisen
      ...
      return(Pos);
      }
    
    int TMainForm::OpenChatWindow(unsinged int UserNumber)
    {
    
      // UserNumber überprüfen, ob in Bereich des Arrays
      if (UserNumber >= UserList.Length) 
      {
        ShowMessage("User List - Out of Range");
        return;
        }
    
       // Wenn Fenster für diesen User noch nicht vorhanden, Fenster öffen
       if (UserList[UserNumber].ChatForm == NULL)
         UserList[UserNumber].OpenWindow();
    
       // Wenn Fenster für diesen User vorhanden, diesen in Vordergrund holen
       else
         UserList[UserNumber].ChatForm->SetFocus();
    
      return(0);
      }
    

    Noch weiter vereinfachen kann ich es nicht. Wenn du damit nicht klar kommst, solltest du dich erst mal an einfacheren Sachen probieren (Ist nicht böse oder überheblich, sondern ernst gemeint)

    bis bald
    akari


Log in to reply