Komponentenentwickulung Eigenschaft mit Untereigenschaft erstellen



  • Bigwill schrieb:

    nByteSize  __fastcall GetByteSize(){return F_ByteSize;}
            void  __fastcall SetByteSize(nByteSize br){F_ByteSize = br;}
    

    [C++ Warnung] Comm.h(45): W8006 nByteSize wird mit unsigned char initialisiert

    Hi,
    welche der beiden ist Zeile 45? Ich habe die vermutung das diese Warnung von der 1. Zeile produziert wird!

    MFG

    Alexander Sulfrian



  • Die kommt bei allen beiden, war nur zu Faul die 2 mal reinzukopieren.
    Und mir ist auch klar warum die kommt, ich dachte nur man kann die irh´gendwie wegbekommen, denn gehen tut es.



  • hi,

    Du hast den Wert BaudRate (im Post hier drüber) doch im Konstruktor der Form gestezt. Das kann aber doch so nicht richtig sein. Ich wollte doch den Wert, der in der Ide angezeigt werden soll initialisieren, ich denke dann muss ich den in den KOnstruktor der Klasse schreiben, oder?

    das sollte nur ein Beispiel sein, wie man eine Instanz erzeugt und Werte setzt. Das hat nicht mit der Klasse zu tun. Ich habs nur im Konstruktor vom Formular geschrieben, da dieser bei BCB- Start automatisch erzeugt wird. Faulheit siegt 😉 Hätte auch eine beliebig andere Funktion sein können...

    Warum das mit dem setzen der Werte aus meinem vorigen Beispiel nicht geht habe ich auch rausgefunden, da DCB ja trotz allem ein Strukt war und das damit nicht geht. Daraufhin habe ich alle Eigenschaften einzeln in der Klasse aufgeführt, seitdem geht es.

    das geht perfekt, siech Beispiel von mir...

    und während der Laufzeit sollte man dann auch DWORD werte setzen können, weil man auch beliebige non Standartwerte verwenden kann. Das geht natürlich aber mit der _property nicht, geht das überhaupt irgendwie?

    wer sagt, dass das nicht geht.

    wenn ich nochmal auf mein Beispiel zurückkommen darf:

    __fastcall TForm1::TForm1(TComponent* Owner) 
       : TForm(Owner) 
    { 
     TComm* Comm=new TComm(this); 
    // Comm->DCB->BaudRate= BR_200;  // hier setzt du die Bautrate 
    Comm->DCB->BaudRate=(TListe)200; // nun setzt du das Property über ein DWORD
    
    // oder
    DWORD t=54;
    Comm->DCB->BaudRate=(TListe)t;
     delete Comm; 
    } 
    //---------------------------------------------------------------------------
    
    enum nByteSize{FourBits=4, FifeBits=5, SixBits=6, SevenBits=7, EightBits=8}; 
     
    //--------------------------------------------------------------------------- 
    class PACKAGE myDCB : public TPersistent 
    { 
    __published: 
            __property nByteSize ByteSize     = {read=GetByteSize, write=SetByteSize}; 
     
    private: 
            BYTE  F_ByteSize; 
     
    public: 
     
    protected: 
            nByteSize  __fastcall GetByteSize(){return F_ByteSize;} 
            void  __fastcall SetByteSize(nByteSize br){F_ByteSize = br;} 
     
    }; 
    //---------------------------------------------------------------------------
    

    ähm, du MUSST bei ein Datentyp bei der Implementierung bleiben. Wie der Anwender später castet ist seine Sache. Man kann ohne PRobleme ein DWORD oder ein int nach enum oder umgekehr casten.
    mach dich also um sowas kein Kopf...



  • Oh shit man, jetzt seh ich erst, das ich gepennt habe. Hatte dein Beispiel vorhins vor lauter Code net gesehen, dachte nämlich du hättest ein Stück aus meinem letzen Code zitiert, weil ich nicht gleich ne Änderung gesehen hatte.



  • 🙂



  • So fast geschafft es geht alles bis auf eine kleine Sache. Hier erst mal der Code, die Fehlerbeschreibung untendrunter.

    enum nByteSize   {Bits4=4, Bits5=5, Bits6=6, Bits7=7, Bits8=8};
    enum nStopBits   {OneStopBit, OneAndHalfStopBits, TwoStopBit};
    enum nParity     {NoParity, OddParity, EvenParity, MarkParity, SpaceParity};
    enum nRtsControl {RtsDisable, RtsEnable, RtsHandshake, RtsToggle};
    enum nDtrControl {DtrDisable, DtrEnable, DtrHandshake};
    
    //---------------------------------------------------------------------------
    class PACKAGE CommSet : public TPersistent
    {
    __published:
            __property DWORD       BaudRate     = {read=GetBaudRate, write=SetBaudRate};
            __property nDtrControl fDtrControl  = {read=GetfDtrControl, write=SetfDtrControl};
            __property nRtsControl fRtsControl  = {read=GetfRtsControl, write=SetfRtsControl};
            __property nParity     Parity       = {read=GetParity, write=SetParity};
            __property nStopBits   StopBits     = {read=GetStopBits, write=SetStopBits};
            __property nByteSize   ByteSize     = {read=GetByteSize, write=SetByteSize};
    
    private:
            DWORD       F_BaudRate;       //Current Baud Rate
            nDtrControl F_fDtrControl;    //Data-Terminal-Ready Flow Control
            nRtsControl F_fRtsControl;    //Request-To-Send Flow Control
            nParity     F_Parity;         //Specifies the Parity Scheme
            nStopBits   F_StopBits;       //Number of Stop Bits
            nByteSize   F_ByteSize;       //Number of Bits in Bytes transmitted and received
    
    public:
            __fastcall CommSet()
            {
              BaudRate = 9600;
              fDtrControl = DtrHandshake;
              fRtsControl = RtsHandshake;
              Parity = EvenParity;
              StopBits = OneStopBit;
              ByteSize = Bits4;
            }
    
            __fastcall ~CommSet(){}
    
    protected:
            DWORD       __fastcall GetBaudRate()                 {return F_BaudRate;}
            void        __fastcall SetBaudRate(DWORD br)         {F_BaudRate = br;}
            nDtrControl __fastcall GetfDtrControl()              {return F_fDtrControl;}
            void        __fastcall SetfDtrControl(nDtrControl br){F_fDtrControl = br;}
            nRtsControl __fastcall GetfRtsControl()              {return F_fRtsControl;}
            void        __fastcall SetfRtsControl(nRtsControl br){F_fRtsControl = br;}
            nParity     __fastcall GetParity()                   {return F_Parity;}
            void        __fastcall SetParity(nParity br)         {F_Parity = br;}
            nStopBits   __fastcall GetStopBits()                 {return F_StopBits;}
            void        __fastcall SetStopBits(nStopBits br)     {F_StopBits = br;}
            nByteSize  __fastcall GetByteSize()                  {return F_ByteSize;}
            void       __fastcall SetByteSize(nByteSize br)      {F_ByteSize = br;}
    
    };
    //---------------------------------------------------------------------------
    
    class PACKAGE TComm : public TComponent
    {
    private:
            CommSet *FDCB;
    
    protected:
    public:
            __fastcall TComm(TComponent* Owner);
            __fastcall ~TComm();
    
    __published:
            __property CommSet *CommDCB = {read=FDCB, write=FDCB};
    
    };
    //---------------------------------------------------------------------------
    #endif
    

    Das Problem ist das erste enum ganz oben, wo ich ab 4 Initialisiere.
    Bei allen anderen geht alles aber bei diesem enum habe ich in der Ide als Standard Bit8 gesetzt obwohl ich doch im Construktor Bit4 angebe und in der Auswahlliste steht haufenweise wirres Zeug aber nicht das aus dem enum. Ich schätze mal das liegt daran, das ich nicht mit 0... Initialisiere wie bei den anderen, wenn ich das nähmlich mache, geht es da auch. Nur dummerweise kann ich es nicht machen, da ich nunmal als Wert 4-8 brauche und nicht 0-3.



  • Hi,
    das Prob. hatte ich auch mal!

    Aber du kannst doch in den Read/Write Methoden immer +-4 rechnen!

    MFG

    Alexander Sulfrian



  • Theoretisch ja, in diesem bsp. ginge das.

    Aber ich wollte das ja noch für weitere Eigenschaften nutzen, wie zum Bsp. die BaudRate und da gibts werde zw. 10 - 256000, da kann ich eben nicht einfach nehn festen Wert drauf rechnen.
    Ich habs übrigens gerade noch mal mit anderen Werten probiert, ist tatsächlich so, das das nur geht, wenn man von 0...x an aufwärts geht.

    Das der Computer aber auch immer was anderes machen muss, wie ich gern will. 😃
    Hoffentlich findet sich noch eine Lösung. Ich hatte noch an eine switch in den read/write Methoden gedacht, allerdings wäre das dann nicht ganz sauber, denn wenn man versuchen würde eine 0...x zu setzen würde er den entsprechenden Wert aus dem enum setzen und erst wenn man dort einen wert größer als die Anzahl eingibt, dann einen höheren. Also könnte ich zum Bsp bei BaudRate nie 10 setzen, weil das im switch einer BaudRate von 38400 entsprechen würde.



  • die enum-aufzählung mit einem wert != 0 und fortlaufende werte die um != 1 erhöht werden, funktionieren im objektinspektor nicht!

    du könntest ein array angelegen, wo du dir die zugehörigen werte vermerkst und dann mit dem gewählten elemente aus dem enum abfragen. mit switch und case kann man das auch machen, musste halt nur ein break nach jedem case schreiben.

    enum nByteSize
    {
      Bits4, Bits5, Bits6, Bits7, Bits8
    }; 
    
    int ByteValue[5] = { 4, 5, 6, 7, 8 };
    
    // im code
    int Bytes = ByteValue[ByteSize];
    


  • Ist nur leider das gleiche Problem wie oben beschreben. Da man ja zur Laufzeit auch non enum WErte eingeben können soll.

    Bsp. BaudRate = BR_600; bzw. BaudRate = 600;

    Das ist zwar in dem Fall ziemlich sinnlos aber zum Beispiel wäre eine BaudRate von 12300 kein Standardwert, demzufolge nicht in der enum aufgeführt. das heist aber nicht das es keine Geräte geben kann die diesen benutzen, deshalb sollte ertrotzdem setzbar sein.

    Dies wäre nun auch realisierbar, indem ich einfach alle Werte die nicht im switch vorkommen per default: einfach weitergebe. Da aber zum Bsp BR-600 im enum einer 3 entspricht könnt ich nie zur Laufzeit BaudRate = 3 setzen, da diese dann immer auf 600 gesetzt werden würde.



  • Naja, dann kommst du wohl um die switch Construktion nicht herum!

    MFG

    Alexander Sulfrian



  • Doch, ich amch BaudRate jetzt als reines DWORD, hat der USer halt keine Liste mit ´Standards zur Auswahl. Nur isses halt jetzt nicht genauso wie ichs eigentlich gern hätte.
    Aber egal, ich verlagere jetzt meine Bemühungen mal aufs eigentliche proggen der Funktionalität.



  • Ich hoffe AndreasW schaut mal wieder rein.

    Ichhabe nämlich ein weiteres Problem bei der Komponentenentwicklung aufgetan, diesmal mit den Ereignisbehandlungsmethoden.

    Im Code unten ist mal das meiste unwichtige ausgeblendet. Man sieht eine Ereignisbehandlungsmethode OnBeforeConnect() die ausgelösst werden soll befor hatl die Connection zum Comport wirklich aufgebaut wird.

    Meine Frage ist die Implementierung dieser. Ich habe sie einfach in die Methode connect() (mit der stellt man die Verbindung her) als erstes reingeschrieben.
    In deinem Builder Tutorial arbeitest du aber mit Messages. Soll ich nun für alle meine EreignisbehandlungsMethoden ne eigene Message erstellen und Abfragen?
    Insbesondere bei dieser stell ich mir das nämlich schwierig vor, den wenn dann statt der Methode direkt erst der Aufruf der Mesage verschickt wird abeitet der doch danach weiter ab und wartet nicht, bis die Message erfolgreich verarbeitet wurde. Oder täusche ich mich da?

    typedef void __fastcall (__closure *MyDataRead)  (String Daten, bool &doDelete);
    typedef void __fastcall (__closure *MyBeforeOpen)(DCB &dcb);
    
    //---------------------------------------------------------------------------
    
    class PACKAGE TComm : public TComponent
    {
    __published:
            __property MyBeforeOpen OnBeforeOpen = {read=F_MyBeforeOpen, write=F_MyBeforeOpen};
    
    private:
            MyBeforeOpen F_MyBeforeOpen;
    
    public:
            __fastcall TComm(TComponent* Owner);
            __fastcall ~TComm();
            bool  __fastcall Connect();
    
    protected:
            bool  __fastcall OpenCommP();
            bool  __fastcall CloseCommP();
            void  __fastcall DataInBuffer(DWORD *InQueue, DWORD *OutQueue);
    };
    //---------------------------------------------------------------------------
    #endif
    
    bool __fastcall TComm::Connect()
    {
      //Ereignisbehandlung OnBeforeOpen(...) aufrufen
      if(F_MyBeforeOpen)F_MyBeforeOpen(FDCB->FDCB);
    
      //wenn Verbindung zum Comport bereits besteht, dann trennen
      //und eine neue Verbindung mit den angegebenen Daten erstelln
      if (F_Connected)
        CloseCommP();
      DatenBuffer = "";
      if (!OpenCommP())
        return false;
    
      //Je nach Modus die entsprechene Bearbeitung zum Daten lesen starten
      switch(F_Modus)
      {
        case PollingMode:
            PollingModeTimer->Interval = F_PollingTimeout;
            PollingModeTimer->Enabled  = true;
            break;
    
        case EventMode:
            //Code folgt
            break;
    
        default:
            return false;
      };
    
      return true;
    }
    //---------------------------------------------------------------------------
    

    -------------------------------------------------------

    Ich habe gerade noch das hier in deinem Tutorial gelesen:

    if (!ComponentState.Contains(csDesigning))TLabel::Paint();
    

    Hier wird die virtuelle Methode Paint redefiniert. Wenn dies leer ist, wird auch nichts gezeichnet. in der if-Anweisung wird die alte Methode nur aufgerufen, wenn die Komponente sich zur Laufzeit erstellt wird. Zur Entwurfzeit wird nichts gezeichnet.

    Habs aber nicht ganz verstanden. unterscheidet der hier richtig zwischen Laufzeit und Entwurfszeit oder liegt die Betonung auf zur Laufzeit erstellt. Also muss das Teil mit new erzeugen?



  • Hi,
    ich bin zwar nicht AndreasW aber ich sag trotzdem mal was!

    zu 1) Ich mache das immer so wie du! Messages sind wahrscheinlich nur wichtig wenn man sie auch außerhalb der Komponente abfrage können soll, oder?

    Bigwill schrieb:

    Habs aber nicht ganz verstanden. unterscheidet der hier richtig zwischen Laufzeit und Entwurfszeit oder liegt die Betonung auf zur Laufzeit erstellt. Also muss das Teil mit new erzeugen?

    Hierbei unterscheidet er richtig zuwischen Entwurf- und Laufzeit! Beim TImage wird der gestrichelte Rand im Entwurf so gezeichnet:

    if (ComponentState.Contains(csDesigning))
    Zeichne_gestrichelten_Rand();

    Dadurch wird der Rand nur im Formulardesigner gezeichnet!

    MFG

    Alexander Sulfrian



  • Nun, das sind ja dann gleich 2 positive Antworten auf einmal, das mag ich.



  • Biwill schrieb:

    Ich hoffe AndreasW schaut mal wieder rein.

    Da bin ich wieder 🙂 War auf Wochenend- Tour...

    Biwill schrieb:

    In deinem Builder Tutorial arbeitest du aber mit Messages.

    ähm, keine Ahnung was du meinst. Gib mir doch noch ein Hinweis bezüglich des Themas und Abschnittes. Vielleicht muss ich da ja noch was verbessern, wenn das Thema vomn mir nicht klar genug oder misverständlich ausgeführt wurde 😉

    Deine Implementation des Ereignisses sieht aber richtig aus. So muss es sein..

    Biwill schrieb:

    Insbesondere bei dieser stell ich mir das nämlich schwierig vor, den wenn dann statt der Methode direkt erst der Aufruf der Mesage verschickt wird abeitet der doch danach weiter ab und wartet nicht, bis die Message erfolgreich verarbeitet wurde. Oder täusche ich mich da?

    wo liegt der Unterschied zwischen SendMessage (Perform/Broadcast) und PostMessage ? 😉

    Alexander Sulfrian schrieb:

    Ich mache das immer so wie du! Messages sind wahrscheinlich nur wichtig wenn man sie auch außerhalb der Komponente abfrage können soll, oder?

    naja, vom Prinzip her schon.
    Etwas weiter gedacht, macht es aber schon Sinn. Nimm mal an, jemand möchte deine Komponente als SubKomponente verwenden. Er möchte aber auch auf Ereignisse der SubKomponente reagieren. Wenn er nun aber die Ereignisse (Methodenpointer) der SubKomponente benutzt kann der Anwendungsentwickler dieses aber nicht mehr )*. Somit sich Messages, die zum Parent und zum Owner geschickt werden durchaus sinnvoll. Vielleicht hab ich diese Aspekte im Tutorial noch nicht ganz rund angesprochen...
    Ist ja auch erst mal eine Grundlage, ich werd da noch dran arbeiten und ausbauen.

    )* Das weiterreichen von Methodenpointern innerhalb eine Komponente zu SubComponenten erscheint nur auf den ersten Bilck sicher. Wenn Komponeten zur Laufzeit dynamisch erzeugt und wieder zerstört werden, kann es durchaus passieren, dass Methodenpointer ins "Nichts" führen und Zugriffsverletztungen verursachen.
    Man sollte einfach hinnehmen, das ein Pointer halt nur eine Adresse aufnehmen kann. Jede weitere hin und her zwitscherrei kostet Sicherheit und ist nicht Vertretbar.

    Simples (ähnliches) Beispiel:

    erzeuge eine Kompo. die auf das Ereignis Rezize des ParentForm reagiert.

    macht dabei eine zwitscherrei wie (nur Pseudocode):

    im Konstruktor:
    OldFormResize= GetParentForm()->OnResize;
    GetParentForm()->OnResize=FromResize;

    in der Methdoe FormResize sowas wie:

    ShowMessage("FormResize");
    if(OldFormResize)OldFormResize(GetParentForm());

    im Destruktor
    GetParentForm()->OnResize=OldFormResize;

    soweit die Testkomponente.

    nun zieh 4 dieser Komponeten in dein Formular.
    Du wirst feststellen, dass es funktioniert.
    nun lösche die zweite Komponente zur Laufzeit ....
    Joh, und das Übel nimmt seine Lauf...


Anmelden zum Antworten