notificationsc++builderdelegation

Notifying a control that another control has a state change


I am using C++Builder Enterprise and need some ideas.

I have a Form with a bunch of TButton and TSpeedButton controls on it. What I want to have happen is that, when a given button is pressed, I want to disable it and several others, while enabling several other buttons, i.e. state changes.

The issue is that I am duplicating the enable/disable code in a bunch of places. I've thought about somehow using the TNotifyEvent delegation to alert the buttons of a state change, but I don't think that delegation technique will work in this situation. I want to avoid creating a bunch of sub-classes of TButton/TSpeedButton.

I also would like to try to use techniques that are available from the VCL, as in each component carries an observer list and wonder if I could leverage that somehow.


Solution

  • My 2 cents worth..

    I have done a proof of concept for your problem, would need a lot more work but I think it is doable and should do what you want.

    I will do more work on it and post if there is interest.

    To find all controls of a class type on a form

    --- TControlAgent->FindTControl(this, "TButton");

    I added 4 buttons on a form, added code bellow to button1 click event,

    TControlAgent->SetControlProperty("Enabled", "TButton", false, true, "Button3");

    in this case, want controls with Enabled property whose class name is TButton, state = true or false, execute the action but but exclude control named TButton3

    There result was all TButtons on the form were set to disable except for button 3

    (note: the exclude option would be a lookup list of controls to be excluded from any action or be able to set a state that is not the state eg Enabled = !true or !false )

    In the click event of button3 I placed the following code;

    TControlAgent->SetControlProperty("Enabled", "TButton", true, true, "");

    The result of this is to re-enable all the controls, this is just an idea but with with the extra work it could be possible to execute any action on any control from a single parent form for any form in a collection of forms.

    I also think it could be possible to fire events, just a crazy idea...

    .h file using berlin 10.1

    
        //---------------------------------------------------------------------------
    
    #ifndef TControlAgentH
    #define TControlAgentH
    //---------------------------------------------------------------------------
    #include <System.SysUtils.hpp>
    #include <System.Classes.hpp>
    #include <Vcl.Controls.hpp>
    #include <System.TypInfo.hpp>
    #include <vector>
    #include <algorithm>
    
    //---------------------------------------------------------------------------
    class PACKAGE TControlAgent : public TComponent
    {
    private:
        std::vector<TControl*> FControls;
    protected:
    public:
      std::vector<UnicodeString> excludeControlNames;
        __fastcall TControlAgent(TComponent* Owner);
        TControl * __fastcall GetControl(TControl* ctrl, UnicodeString property);
        void __fastcall FindTControl(TForm *f, UnicodeString className);
      TControl* __fastcall SetControlProperty(UnicodeString property, UnicodeString className, bool state, bool exec, UnicodeString excludeControlName   );
    __published:
    };
    //---------------------------------------------------------------------------
    #endif
    
    
    

    .cpp file

    
        //---------------------------------------------------------------------------
    
    #include <vcl.h>
    
    #pragma hdrstop
    
    #include "TControlAgent.h"
    #pragma package(smart_init)
    //---------------------------------------------------------------------------
    // ValidCtrCheck is used to assure that the components created do not have
    // any pure virtual functions.
    //
    //---------------------------------------------------------------------------
    // component to act as agent for other components derived from TControl
    //
    //  Components have properties and events, is it possible to get all TControls into a vector
    //  then be able to get a property of a component then fire an event or carry out an action
    //  on the component!!!!
    //---------------------------------------------------------------------------
    static inline void ValidCtrCheck(TControlAgent *)
    {
        new TControlAgent(NULL);
    }
    //---------------------------------------------------------------------------
    __fastcall TControlAgent::TControlAgent(TComponent* Owner)
        : TComponent(Owner)
    {
    }
    //---------------------------------------------------------------------------
    void __fastcall TControlAgent::FindTControl(TForm *f, UnicodeString className)
        {
        FControls.clear();
    
        for(int i = 0; i < f->ControlCount; i++)
            {
            if(f->Controls[i]->ClassName() == className)  //control classes that to set as a group
                FControls.push_back(f->Controls[i]);
            }
        //note:  could have a function that appends other class names
        }
    //---------------------------------------------------------------------------
    TControl* __fastcall TControlAgent::SetControlProperty(UnicodeString property, UnicodeString className, bool state, bool exec, UnicodeString excludeControlName  )
        {
        PPropInfo propinfo;
        for(int i = 0; i < FControls.size(); i++)
            {
            if(FControls[i]->ClassName() == className)
                {
                propinfo = GetPropInfo(FControls[i], property);
                if (!propinfo)
                    continue;
                if(exec && FControls[i]->Name != excludeControlName )
                    {
                    if(property == "Enabled")
                        FControls[i]->Enabled = state;
                    }
                }
    
            }
    
        }
    //---------------------------------------------------------------------------
    namespace Tcontrolagent
    {
        void __fastcall PACKAGE Register()
        {
            TComponentClass classes[1] = {__classid(TControlAgent)};
            RegisterComponents(L"SQLite", classes, 0);
        }
    }
    //---------------------------------------------------------------------------