c++runtimec++buildertframe

Deleting TFrame from form at run time


I am using C++ Builder to create a VCL form application. Right now I have a TFrame containing a bunch of components and it looks like this...

enter image description here

I also have a button call "Add". Basically every time I press that Add button on the form, a new TFrame is added to it and below the previous one making something that looks like a table. And in order to add duplicates I have to rename the TFrame each time before it is created.

    int __fastcall TForm1::AddMapCells(void)
    {
        Num++;
        TFrame1 * MyFrame = new TFrame1(Form1);
        MyFrame->Parent=Form1;
        MyFrame->Name = "TFrame" + IntToStr(Num);
        MyFrame->Top = 23*Num;
        return Num;
    }

So then the naming of TFrame would be TFrame1, TFrame2, TFrame3, etc.

The problem now is I want to make it so then each time I press the 'X' button of a TFrame, it deletes that TFrame and I'm not quite sure how to do it. I was thinking maybe each time I create a TFrame I can also rename the 'X' button so then it's like Button1, Button2, Button3, etc. And then to delete the program would just match ButtonX with TFrameX to identify which TFrame to delete. For example, if I press Button 4, it should match up with TFrame4 and delete TFrame4.

I don't know how to implement this idea. Or would there be an easier way of doing this?


Solution

  • A simple solution would be to let the TFrame instances free themselves for you. Assign an OnClick event handler to the X button and have it post a queued message to its parent TFrame window via PostMessage(), then give the TFrame class a message handler that frees the TFrame instance when that message is processed (this is how the TForm::Release() method works), eg:

    void __fastcall TFrame1::CloseButtonClick(TObject *Sender)
    {
        // CM_RELEASE is defined in Controls.hpp
        PostMessage(Handle, CM_RELEASE, 0, 0);
    } 
    
    void __fastcall TFrame1::WndProc(TMessage &Message)
    {
        if (Message.Msg == CM_RELEASE)
        {
            delete this;
            return;
        }
    
        TFrame::WndProc(Message);
    }
    

    If you need your parent TForm to be notified of the TFrame being closed (for instance, to reposition lower TFrame instances), you can expose a custom TNotifyEvent event in the TFrame class and have your TForm assign an event handler to it, eg:

    class TFrame1 : public TFrame
    {
    private:
        TNotifyEvent FOnClose;
        ...
    public:
        ...
        __property TNotifyEvent OnClose = {read=FOnClose, write=FOnClose};
    };
    
    void __fastcall TFrame1::CloseButtonClick(TObject *Sender)
    {
        if (FOnClose != NULL) FOnClose(this);
        PostMessage(Handle, CM_RELEASE, 0, 0);
    } 
    
    void __fastcall TFrame1::WndProc(TMessage &Message)
    {
        if (Message.Msg == CM_RELEASE)
        {
            delete this;
            return;
        }
    
        TFrame::WndProc(Message);
    }
    

    .

    int __fastcall TForm1::AddMapCells(void) 
    { 
        Num++; 
        TFrame1 * MyFrame = new TFrame1(this); 
        MyFrame->Parent = this; 
        MyFrame->Name = "TFrame" + IntToStr(Num); 
        MyFrame->Top = 23*Num; 
        MyFrame->OnClose = &FrameClosed;
        return Num; 
    } 
    
    void __fastcall TForm1::FrameClosed(TObject *Sender)
    {
        // Sender is the TFrame1 instance whose X button was clicked.
        // It will auto-free itself after this method exits...
    }