2

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?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
livelaughlove
  • 376
  • 3
  • 9
  • 21

1 Answers1

5

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...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • thanks so much! i've been stuck on this for days and it's a major part of my program =] – livelaughlove Feb 07 '12 at 19:57
  • if you read this again i would like to just ask for ur suggestion about the positioning of these frames and the logic behind it. u brought up a really good point that i would have to shift all the frames up if a frame in the middle was to be deleted. however because of the way i positioned these frames and the way i named them, shifting them frames up would also require renaming the frames and recalculating the positions. i guess my questions is, is there a better way to do the positioning? i really appreciate ur help, i'm just a co-op student and i've never tackled any program like this befor – livelaughlove Feb 07 '12 at 20:29
  • 4
    Store your `TFrame` pointers in a `TList`, `std::vector`, `std::list`, etc, whatever you are comfortable with. When a given `TFrame` instance is being closed, you can locate that pointer in the list, remove it, and then loop through the remaining pointers decrementing their `Top` properties by the `Height` of the `TFrame` that is being closed. You do not have to rely on their `Name` values, in fact I always give my `TFrame` objects no `Name` values at all, just set the `Name` to a blank string. It works fine. – Remy Lebeau Feb 07 '12 at 21:47
  • ty ty! i will tried that out and it was a lot easier to program with on the logic side with this set up – livelaughlove Feb 08 '12 at 14:32