Search code examples
c++firemonkeyc++builder

What to pass as a Sender in a button OnClick method?


I have a function that creates a button dynamically

void createBtn(News obj,TForm *Form1){
    TButton *spam = new TButton(Form1);
    spam->Parent = newsCard;
    spam->Position->X = 280;
    spam->Position->Y = 256;
    spam->Text = "Spam";
}

I need to assign an OnClick event to it, so I added the following line to the function above:

spam->OnClick = spamClick;

The code for spamClick is:

void __fastcall TForm1::spamClick(TObject *Sender, News obj)
 {
    allUsers[userIndex].spamNews(obj);
    ShowMessage("Done!");
 }

The problem is, I need to pass the obj in the line, but the function requires 2 arguments, which are the Sender and the obj.

spam->OnClick = spamClick(obj); // error here

But I do not know what to pass. I've tried Form1, spam, and newsCard. But nothing works.

What do I pass as a Sender? Or, is there a way to assign an OnClick event to the button inside createBtn()?

Edit: class News has the following definition

class News
{
public:
    News(string, string, string, Dates);
    string title;
    string description;
    Dates date;
    int rate;
    string category;
    vector<Comment> comments;
    int spamCount;
    static int newsCount;
    int newsID;
    int numOfRatedUsers;
};

and spamNews is a function in the user class that pushes the obj.newsID into a vector in the user then increases the spamCount.

void user::spamNews(News& obj) {
//check that the news in not already spammed 
    if(!findNews(spammedNews,obj)){  
        spammedNews.push_back(obj.newsID);
        obj.spamCount++;
    }
}

Solution

  • Your second approach doesn't work, because you are trying to call spamClick() first and then assign its return value to the OnClick event.

    Your first approach is the correct way, however you can't add parameters to the OnClick event handler.

    TButton has Tag... properties for holding user-defined data. However, since the News object is not being passed around by pointer, the Tag... properties are not very helpful in this case (unless the News object is held in an array/list whose index can then be stored in the Tag).

    Otherwise, I would suggest deriving a new class from TButton to hold the News object, eg:

    class TMyButton : public TButton
    {
    public:
        News NewsObj;
    
        __fastcall TMyButton(TComponent *Owner, const News &obj)
            : TButton(Owner), NewsObj(obj) {}
    };
    
    void TForm1::createBtn(const News &obj)
    {
        TMyButton *spam = new TMyButton(this, obj);
        spam->Parent = newsCard;
        spam->Position->X = 280;
        spam->Position->Y = 256;
        spam->Text = _D("Spam");
        spam->OnClick = &spamClick;
    }
    
    void __fastcall TForm1::spamClick(TObject *Sender)
    {
        MyButton *btn = static_cast<TMyButton*>(Sender);
        allUsers[userIndex].spamNews(btn->NewsObj);
        ShowMessage(_D("Done!"));
    }
    

    UPDATE: Since your News objects are being stored in a vector that you are looping through, then a simpler solution would be to pass the News object to createBtn() by reference and then store a pointer to that object in the TButton::Tag property, eg:

    void TForm1::createBtn(News &obj)
    {
        TButton *spam = new TButton(this);
        spam->Parent = newsCard;
        spam->Position->X = 280;
        spam->Position->Y = 256;
        spam->Text = _D("Spam");
        spam->Tag = reinterpret_cast<NativeInt>(&obj);
        spam->OnClick = &spamClick;
    }
    
    void __fastcall TForm1::spamClick(TObject *Sender)
    {
        TButton *btn = static_cast<TButton*>(Sender);
        News *obj = reinterpret_cast<News*>(btn->Tag);
        allUsers[userIndex].spamNews(*obj);
        ShowMessage(_D("Done!"));
    }
    

    Or, using the TMyButton descendant:

    class TMyButton : public TButton
    {
    public:
        News *NewsObj;
    
        __fastcall TMyButton(TComponent *Owner)
            : TButton(Owner) {}
    };
    
    void TForm1::createBtn(News &obj)
    {
        TMyButton *spam = new TMyButton(this);
        spam->Parent = newsCard;
        spam->Position->X = 280;
        spam->Position->Y = 256;
        spam->Text = _D("Spam");
        spam->NewsObj = &obj;
        spam->OnClick = &spamClick;
    }
    
    void __fastcall TForm1::spamClick(TObject *Sender)
    {
        MyButton *btn = static_cast<TMyButton*>(Sender);
        allUsers[userIndex].spamNews(*(btn->NewsObj));
        ShowMessage(_D("Done!"));
    }