Event Handler for Dynamically Created CheckBox

1.5k Views Asked by At

I wrote a C++ program using Borland C++ Builder 5. The program dynamically creates an array of TCheckBox objects. I have tried to write an OnClick event handler that would identify which checkbox is being clicked and execute some instructions based on that. My event handler is based on similar posts to this website, but I cannot seem to make it work.

Here is the (abbreviated) code

// Header declaration
void __fastcall CBoxClick(TObject *Sender); 
// End Header

// CBoxClick function (the event handler)

void __fastcall CBoxClick(TObject *Sender){
    if (dynamic_cast<TCheckBox*>(Sender)!=NULL){
        //Do stuff
    }
    else{
        Form1 -> Debug -> Text = "Object is not a TCheckBox";         
    }
}

void ChkBoxInit(void){
    int i;                                            //Loop counter index
    TCheckBox* ChkBx[NCARDS];                         //Define array of type checkboxes
    for(i = 0; i < NCARDS; i++){                      //Initalize each checkbox
        ChkBx[i] = new TCheckBox(Form1);              //Create a new checkbox
        ChkBx[i] -> Parent = Form1;                   //Define parent of checkbox
        ChkBx[i] -> Tag = i;                          //Set value of Tag to index
        //  Other CheckBox parameters here such as Height, Width, Top, Left, Name are here
        //  Next, call event handler. I've tried the following 2 statements with the comment results
        ChkBx[i] -> OnClick = CBoxClick(ChkBx[i]);    //  Results in E2109: Not an allowed type
        ChkBx[i] -> OnClick = CBoxClick;              /* Alternate try - Results in E2034: Cannot convert
                                                        'void (_fastcall *)(TObject *)' to 
                                                        'void (_fastcall * (_closure )(TObject *))(TObject *)'  */
    }                                                 //End of for loop
}                                                     //End of function
2

There are 2 best solutions below

7
On

CBoxClick() is not a member of your Form class.

Within your cpp file, the compiler sees it as a standalone function. That is what the error message is complaining about when the assignment of the OnClick event fails (non-static class methods have the __closure attribute, non-members do not).

Make sure CBoxClick() is declared inside your Form class in the header file:

class TForm1 : public TForm
{
    ...
public:
    ...
    void __fastcall CBoxClick(TObject *Sender); // <-- add this
    ...
};

And then change this line in your cpp file:

void __fastcall CBoxClick(TObject *Sender){

To this instead:

void __fastcall TForm1::CBoxClick(TObject *Sender){

And then change your assignment of the OnClick event from this:

ChkBx[i]->OnClick = CBoxClick;

To this instead (since ChkBoxInit() itself appears to not be a member of the Form class, either):

ChkBx[i]->OnClick = Form1->CBoxClick;

The first syntax you tried (OnClick = CBoxClick(ChkBx[i]);) is just plain wrong, as you are actually calling CBoxClick() and then trying to assign its void return value to OnClick, which obviously will not work. You need to assign the address of CBoxClick() to OnClick, and that will work only for non-static class methods, not for standalone functions (well, it CAN be done, but it requires different code involving a type-cast hack utilizing the TMethod struct).

Also, you should not be using dynamic_cast. Since you know the Sender will always be a TCheckBox, use static_cast instead:

void __fastcall TForm1::CBoxClick(TObject *Sender){
    TCheckBox *cb = static_cast<TCheckBox*>(Sender);
    //Do stuff with cb...
}

UPDATE: Now, with that said, a better option is to get rid of ChkBoxInit() altogether and do the array initialization inside the Form's own constructor instead:

class TForm1 : public TForm
{
    ...
private:
    ...
    void __fastcall CBoxClick(TObject *Sender); // <-- moved here
    ...
public:
    __fastcall TForm1(TComponent *Owner); // <-- constructor
    ...
};

__fastcall TForm1::TForm1(TComponent *Owner)
    : TForm(Owner)
{
    TCheckBox* ChkBx[NCARDS];                         //Define array of type checkboxes
    for(int i = 0; i < NCARDS; i++){                  //Initalize each checkbox
        ChkBx[i] = new TCheckBox(this);               //Create a new checkbox
        ChkBx[i] -> Parent = this;                    //Define parent of checkbox
        ChkBx[i] -> Tag = i;                          //Set value of Tag to index
        //  Other CheckBox parameters here such as Height, Width, Top, Left, Name are here
        //  Next, setup event handler
        ChkBx[i]->OnClick = CBoxClick;
    }                                                 //End of for loop
}        

void __fastcall TForm1::CBoxClick(TObject *Sender)
{
    TCheckBox *cb = static_cast<TCheckBox*>(Sender);
    // Do stuff with cb...
}
0
On

Remy - I'm still having trouble using the editor. Here is the working code which is based on your first response with one small change. Thank you for your help.

`// .h File----------------

class TForm1 : public TForm
{
__published:    // IDE-managed Components
        TButton *Deal;
        TButton *Reset;
        TButton *Help;
        TButton *Quit;
        TEdit *Debug;
        void __fastcall QuitClick(TObject *Sender);
        void __fastcall ResetClick(TObject *Sender);
private:    // User declarations
public:     // User declarations
  __fastcall TForm1(TComponent* Owner);
  void __fastcall CBoxClick(TObject *Sender);  //CheckBox Event Handler prototype
};

// .cpp File ----------------

  void ChkBoxInit(void){
    int i;                                         //Loop counter index
    TCheckBox* ChkBx[NCARDS];                      //Define array of type checkbox
    for(i = 0; i < NCARDS; i++){                   //Initalize each checkbox
      ChkBx[i] = new TCheckBox(Form1);             //Create a new checkbox
      ChkBx[i] -> Parent = Form1;                  //Define parent of checkbox

// ..... Various parameters of ChkBx[i] go here (height, Width, Top, Left, etc.)

      ChkBx[i] -> Tag = i;                         //Use index value as tag
`     ChkBx[i] -> OnClick = Form1 -> CBoxClick;    //Assign OnClick event to CBoxClick
    }                                              //End of for loop
  }                                                //End of function


//  Event Handler ------------------

 void __fastcall TForm1::CBoxClick(TObject *Sender){
   if (static_cast<TCheckBox*>(Sender)!=NULL){
     Form1 -> Debug -> Text =  (static_cast<TCheckBox*>(Sender) -> Name);
   }
   else{
     Form1 -> Debug -> Text = "Object is not a TCheckBox";
   }
 }