Eigene Radio Button Komponente



  • Hallo

    ich arbeite gerade an einem Radio Button, der einige Features mehr hat als der Standard Radio Button.

    Wenn ich die Radio Buttons dynamisch erzeuge oder auf eine Form lege, klappt auch alles zufriedenstellend. Wenn ein Button gedrückt wird, bekommt er die Markierung und bei dem, der die Markierung vorher gehabt hat, wird die Markierung gelöscht.

    Das mache ich mit folgendem Code:

    void __fastcall TCLMenueButton::SetSelected(bool Status)
    {
      int i;
      TComponent *Parent = this->GetParentComponent();
    
      if ( Parent != NULL )
        for ( i=0 ; i<Parent->ComponentCount ; i++ )
          if (Parent->Components[i]->ClassNameIs("TCLMenueButton") &&
              Parent->Components[i] != this )
            dynamic_cast<TCLMenueButton*>(Parent->Components[i])->UnSelected();
    
      FSelected = true;
      Repaint();
    }
    

    Wobei die UnSelect Funktion FSelected bei den anderen Button auf false setzt.

    Jetzt zu dem Problem: Wenn ich die Buttons in eine GroupBox stelle funktioniert es nicht mehr !?!?! Die Button auf dem Formular beeinflussen die Buttons in der GroupBox mit, und die in der Groupbox machen gar nix mehr.

    Wo hab ich da einen Fehler gemacht ? (Ist mein erster Radio Button 🙂 )

    Pronto451

    [ Dieser Beitrag wurde am 12.05.2003 um 22:19 Uhr von Pronto451 editiert. ]



  • Hallo

    Dank dem letzten Beitrag von Master P. in dem Tread
    "Rausfinden welche CheckBox außer der gerade gedrückten in der GroupBox sind!"
    habe ich mein Problem selbst lösen können. Für die, die es interessiert, hier der geänderte Code.

    TWinControl *Parent = this->Parent;
    
      if ( Parent != NULL )
        for ( i=0 ; i<Parent->ControlCount; i++ )
          if (Parent->Controls[i]->ClassNameIs("TCLMenueButton") &&
              Parent->Controls[i] != this )
            dynamic_cast<TCLMenueButton*>(Parent->Controls[i])->UnSelected();
    

    Danke Master P.



  • Hi,

    ehrlich gesagt finde ich die Lösung nicht so toll. Das ganze gecaste ist recht unsicher. Zugriffsverletztungen sind prinzipiell nicht vollkommen ausgeschlossen. Zudem ist es stilistisch nicht unbedingt klug, andere Objekte dermaßen zu beeinflussen. Sicherlich müssen diese Objekte darauf hingewiesen werden, dass sich der Status geändert hat. Allerdings ist das auch das einige was muss.

    Lösungsvorschlag:

    Windows hat bereits ein funktionierendes Messages- System, welches man auch mit ruhe nutzen darf. Dafür definiert man erst einmal eine Nachricht:

    #define MY_CHANGESTATE (WM_APP + 500)
    

    Diese Nachricht bedeute nun:

    *Hallo, ich bin ein CheckControl, gehöre zu Group 1 und ein User hat auch mich rumgetreten.
    *
    das bedeutet:

    TMessage Msg;
    Msg.Msg = MY_CHANGESTATE;
    Msg.WParam = 1;// Gruppenzuordung
    Msg.LParam = (int)(this);
    Msg.Result = 0;
    

    so, diese Nachricht ist alles was man braucht. Dem Parent wird diese Nachricht übergeben, der die Nachricht an alle untergeordneten Komponetnen weitergibt.
    Dafür verwendet man Brodcast:

    Parent->Broadcast(Msg);
    

    In deiner Komponente musst du also eine solche Nachricht abschicken und auch solche Nachrichten abfangen. So kannst du dieses Problem etwas sauberer lösen..



  • Hallo AndreasW,

    Prima, funktioniert prächtig, jetzt weiß ich auch wie ich eine eigene Message sende.

    Danke !!!

    Hab aber noch ne Frage: Woher weiß der Button, das die Message für ihn ist,
    An dem Message #define ?
    Muß ich bei einem anderen Button dann die Message ändern ?
    (z.B. #define My_CHANGESTATE**_B2** (WM_APP + 501))

    Hier mein jetziges Ergebnis:

    ---------  *.h  ----------
    
    #define MY_CHANGESTATE (WM_APP + 500)
    
      void __fastcall MYChecked(TMessage &Message);
      BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(MY_CHANGESTATE,TMessage, MYChecked)
      END_MESSAGE_MAP(TGraphicControl)
    
    --------- *.cpp ----------
    
    void __fastcall TCLRadioButton::MYChecked(TMessage &Message)
    {
      if (FChecked)
      {
        FChecked = false;
        Repaint();
      }
    }
    
    void __fastcall TCLRadioButton::SetChecked(bool Status)
    {
      TMessage Msg;
    
      Msg.Msg = MY_CHANGESTATE;
      Msg.WParam = 1;
      Msg.LParam = (int)(this);
      Msg.Result = 0;
    
      this->Parent->Broadcast(&Msg);
    
      FChecked = true;
      Repaint();
    }
    

    Pronto451

    [ Dieser Beitrag wurde am 14.05.2003 um 08:56 Uhr von Pronto451 editiert. ]



  • Original erstellt von Pronto451:
    Hab aber noch ne Frage: Woher weiß der Button, das die Message für ihn ist,
    An dem Message #define ?
    Muß ich bei einem anderen Button dann die Message ändern ?
    (z.B. #define My_CHANGESTATE
    _B2** (WM_APP + 501))

    **

    Ansich würde in dein Beispiel bereits die Gruppenzugehörigkeit über den LPARAM- Parameter in der Nachricht abgedeckt. Allerdings war mein BEispiel nicht ganz run. Du must da noch schleifen.
    na, und dann abfragen:

    void __fastcall TCLRadioButton::MYChecked(TMessage &Message)
    {
      // dei Variable FGroupIndex musst du durch deine Variable ersetzuen, 
      //die deine zugehörigkeit der Komonente in der Gruppe identifiziert. Siehe CheckBox.
      if (Message.Msg==MY_CHANGESTATE 
          && (HWND) Message.Message.WParam !=Handle  // setzt woraus, dass du den WParam- Parameter das Fensterhandle mitgegeben hast.
          && Message.Message.LParam==FGroupIndex)
      {
        FChecked ^=1;
        Repaint();
      }
    }
    

    naja, zumindest so ähnlich.Ein wenig musst du noch dran rumfeilen..

    du kannst aber auch structs in die Nachricht einbauen:

    struct msgstruct
    {
    void* Control;
    int GroupIndex;
    String Name;
    }  // nur ein Beispiel
    
    void __fastcall TCLRadioButton::SetChecked(bool Status)
    {
      msgstruct m;
      m.GroupIndex=10;
      m.Name="GroupBox2";
      m.Contol=(void*)this;
      TMessage Msg;
      Msg.Msg = MY_CHANGESTATE;
      Msg.WParam = (HWND)this->Handle;  // SOmit können andere Steuerelemente über dieses Handle das abschickende Element indentifizieren  
      Msg.LParam = &m; // hier die struct als Nachricht mitschicken
      Msg.Result = 0;
    
      this->Parent->Broadcast(&Msg);
    
      FChecked = true;
      Repaint();
    }
    

    [ Dieser Beitrag wurde am 14.05.2003 um 09:11 Uhr von AndreasW editiert. ]



  • Hallo AndreasW

    da es ein RadioButton werden soll, brauche ich da einen GroupIndex ?
    Die RadioButtons bilden doch in GroupBoxen, Forms oder Panels bereits eine Gruppen.

    Zu : if ( Message.Msg==MY_CHANGESTATE ...
    Die Message ruft ja meine Funktion auf, warum muß ich da noch auf die "MY_CHANGESTATE" Message prüfen ?

    Das mit dem "#define MY_CHANGESTATE_B2 (WM_APP + 501)" ist gedacht für mehrere anderen RadioButtons (einem MenueButton), der in der selben Gruppe liegen könnte. Sich aber so ähnlich wie der RadioButton verhält, das eben nur einer selektiert ist. Können die beiden die selbe "MY_CHANGESTATE" Message verwenden ?
    oder ist es doch besser eben die "MY_CHANGESTATE_B2" zu benutzen.

    Ich glaub wir haben uns da aber missverstanden, du meintest sicher die Gruppen per GroupIndex auseinander zu halten. Tschuldige, hab mich da nicht klar ausgedrückt. Das mit dem übergeben ganzer Strukturen find ich jedenfalls interessant.

    Aber nochmal zum außeinanderhalten von ButtonTypen, ich definiere für jeden Button eine eigene Message, oder halten sich die Buttons mit dem Msg.LParam auseinander ?

    Pronto451

    PS. Wenn ich Parent->Broadcast(Msg); schreibe bekomme ich ne Fehlermeldung (E2034 Konvertierung von 'TMessage' nach 'void *' nicht möglich)
    mit Parent->Broadcast(&Msg); funktionierts.

    Ich werd mal den Abschnitt über Messages in der Borlandhilfe durch lesen, dann wird mir das ganze klarer. Da steht aber auch Parent->Broadcast(Msg); !?!?!
    Mach ich da was falsch !?!?! nur mit Parent->Broadcast(&Msg); funktionierts.

    [ Dieser Beitrag wurde am 14.05.2003 um 10:13 Uhr von Pronto451 editiert. ]



  • Original erstellt von Pronto451:
    **da es ein RadioButton werden soll, brauche ich da einen GroupIndex ?
    **

    nicht zwingend. Wenn du aber auf ein Formular 8 Radiobuttons hast aber nur jeweils 4 interagieren sollen brauchst du den GroupIndex.

    Original erstellt von Pronto451:
    **
    Die RadioButtons bilden doch in GroupBoxen, Forms oder Panels bereits eine Gruppen.
    **

    ja, aber man kann dann auf den Parent nicht kombinieren.

    Mehr zum Thema Groupindex findet man in der Hilfe zur CheckBox.

    Original erstellt von Pronto451:
    **

    Das mit dem "#define MY_CHANGESTATE_B2 (WM_APP + 501)" ist gedacht für mehrere anderen RadioButtons (einem MenueButton), der in der selben Gruppe liegen könnte. Sich aber so ähnlich wie der RadioButton verhält, das eben nur einer selektiert ist. Können die beiden die selbe "MY_CHANGESTATE" Message verwenden ?
    **

    generell schon. Du musst nur halt die Parameter oder die Parameterstruktur so geschickt wählen, das du möglichst viel abdeckst ohne unhandlich zu werden.

    Original erstellt von Pronto451:
    **
    oder ist es doch besser eben die "MY_CHANGESTATE_B2" zu benutzen.
    **

    wenn es das selbe Auslöseereignis ist ist es unklug einen weiteren Nachrichtstyp einzuführen. Aussserdem würde nur sowas zur Verwirrung führen. Für dich vielleicht nicht. Aber wenn irgendjemand auf die Idee kommt und eine Komponete von deiner ableitet schon.

    Schau dich mal ein wenig in der MSDN und im API- Forum um. Dort findest du bestimmt ein paar Anregungen zur Handhabung von Nachrichten.

    Original erstellt von Pronto451:
    **
    Aber nochmal zum außeinanderhalten von ButtonTypen, ich definiere für jeden Button eine eigene Message, oder halten sich die Buttons mit dem Msg.LParam auseinander ?
    **

    Falscher Ansatz. Eine Nachricht hat nicht über die Herkunft zu differenzieren sonder eine Botschaft eindeutig beschreiben und notwendige Parameter mitliefern.
    Von wen die Nachricht kommt ist irrelevant. Die Nachricht wird an alle Komponenten des Parent geschickt. Zum Beispiel auch an ein Edit- Feld. Allerdings reagiert ein Edit- Feld auf die Nachricht nicht. Könnte es aber.

    Original erstellt von Pronto451:
    **
    Mach ich da was falsch !?!?! nur mit Parent->Broadcast(&Msg); funktionierts.
    **

    nein. Es können nur Integer- Werte übergeben werden. Mit &Msg übergibt man die Speicheradresse der Nachricht. Also ein Pointer. Wenn du die Nachricht abfängst kannst du diesen void* - Zeiger in die struct casten.



  • Hallo AndreasW

    vielen Dank für die ausführliche Antwort, ich hab viel gelernt. Danke

    Pronto 451



  • kein Problem, ich helf ja gerne 🙂


Anmelden zum Antworten