[Android]TFrame create and delete

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

[Android]TFrame create and delete

Postby Lena » Mon Feb 12, 2018 6:18 am

Hi.
In main form I want press button and create my TFrame. In this TFrame I want press button and remove this TFrame.
How to dynamically create and then delete TFrame in FMX C++?

P.S.
I found In Delphi how delete TFrame:
Code: Select all
Frame1.Parent := nil;
Frame1.DisposeOf;
Frame1 := nil; //it is necessary or not???
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby rlebeau » Mon Feb 12, 2018 2:43 pm

Lena wrote:In main form I want press button and create my TFrame. In this TFrame I want press button and remove this TFrame.


I would probably do something like this:

Code: Select all
void __fastcall TMyFrame::ButtonClick(TObject *Sender)
{
    TThread::ForceQueue(nullptr, [this](){ this->DisposeOf(); });
}


Lena wrote:How to dynamically create and then delete TFrame in FMX C++?

P.S.
I found In Delphi how delete TFrame:
Code: Select all
Frame1.Parent := nil;
Frame1.DisposeOf;
Frame1 := nil; //it is necessary or not???



Yes, you should null out any references you have to the dead Frame object (this is especially imporrtant if you target ARC platforms). In which case, I would probably do something more like this instead:

Code: Select all
class TMyFrame : public TFrame
{
public:
    ...
    TNotifyEvent OnButtonClick;
    ...
};

void __fastcall TMyFrame::ButtonClick(TObject *Sender)
{
    if (OnButtonClick)
        OnButtonClick(this);
}

...

void __fastcall TMainForm::CreateFrame()
{
    Frame = new TMyFrame(this);
    Frame->Parent = this;
    Frame->OnButtonClick = &FrameButtonClicked;
}

void __fastcall TMainForm::FrameButtonClicked(TObject *Sender)
{
    TThread::ForceQueue(nullptr, [Frame](){ Frame->DisposeOf(); });
    Frame = nullptr;
}
[code]

Or:

[code]
class TMyFrame : public TFrame
{
public:
    ...
    TNotifyEvent OnButtonClick;
    ...
};

void __fastcall TMyFrame::ButtonClick(TObject *Sender)
{
    if (OnButtonClick)
        OnButtonClick(this);
    TThread::ForceQueue(nullptr, [this](){ this->DisposeOf(); });
}

...

void __fastcall TMainForm::CreateFrame()
{
    Frame = new TMyFrame(this);
    Frame->Parent = this;
    Frame->OnButtonClick = &FrameButtonClicked;
}

void __fastcall TMainForm::FrameButtonClicked(TObject *Sender)
{
    Frame = nullptr;
}
Last edited by rlebeau on Tue Feb 13, 2018 12:19 pm, edited 1 time in total.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1484
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TFrame create and delete

Postby Lena » Tue Feb 13, 2018 4:22 am

Thank You very much! I will try Your code.
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby Lena » Tue Feb 13, 2018 6:05 am

Sorry I can not apply your code. Where is my mistake?
I try in main Form:
Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
  TThread::ForceQueue(nullptr, [Frame1](){ Frame1->DisposeOf(); });
  Frame1 = nullptr;
}


[bccaarm Error] UnitDibocca.cpp(38): no member named 'ForceQueue' in 'System::Classes::TThread'
[bccaarm Error] UnitDibocca.cpp(38): 'Frame1' cannot be captured because it does not have automatic storage duration
UnitFrameMessage.h(31): 'Frame1' declared here
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby rlebeau » Tue Feb 13, 2018 12:34 pm

Lena wrote:[bccaarm Error] UnitDibocca.cpp(38): no member named 'ForceQueue' in 'System::Classes::TThread'


TThread::ForceQueue() was added in 10.2 Tokyo. If you are using an older version, you will have to use a worker thread to call TThread::Queue() instead:

Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    //TThread::ForceQueue(nullptr, [Frame1](){ Frame1->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [Frame1](){
            TThread::Queue(nullptr, [Frame1](){ Frame1->DisposeOf(); });
        }
    )->Start();
    Frame1 = nullptr;
}


Lena wrote:[bccaarm Error] UnitDibocca.cpp(38): 'Frame1' cannot be captured because it does not have automatic storage duration


Hmm... I'm not sure what to make about that one. My lambda kung-fu is not so good.

Frame1 is an object pointer, so a lambda should be able to capture it by value. On the other hand, object pointers in Android are ARC-enabled, so that is probably interfering with the capture. Or, it could just be the fact that Frame1 is not a local variable to FrameButtonClicked() to begin with, so it doesn't need to be captured at all.

Try this:

Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    TFrame *frm = Frame1;
    //TThread::ForceQueue(nullptr, [frm](){ frm->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [frm](){
            TThread::Queue(nullptr, [frm](){ frm->DisposeOf(); });
        }
    )->Start();
    Frame1 = nullptr;
}


Or this:

Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    //TThread::ForceQueue(nullptr, [](){ Frame1->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [](){
            TThread::Queue(nullptr, [](){ Frame1->DisposeOf(); });
        }
    )->Start();
    Frame1 = nullptr;
}
Last edited by rlebeau on Thu Feb 15, 2018 8:11 pm, edited 2 times in total.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1484
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TFrame create and delete

Postby Lena » Wed Feb 14, 2018 2:54 am

Thank Yoy rlebeau for your help!
Both versions compile successfully:
Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    TFrame *frm = Frame1;
    //TThread::ForceQueue(nullptr, [frm](){ frm->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [frm](){
            TThread::Queue(nullptr, [frm](){ frm->DisposeOf(); });
        }
    );
    Frame1 = nullptr;
}

And this:
Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    //TThread::ForceQueue(nullptr, [](){ Frame1->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [](){
            TThread::Queue(nullptr, [](){ Frame1->DisposeOf(); });
        }
    );
    Frame1 = nullptr;
}


I'm trying to move on. :)
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby Lena » Wed Feb 14, 2018 8:37 am

Does not work for me. :(
Code: Select all
//h frame
class TFrame1 : public TFrame
{
__published:   // IDE-managed Components
   TRectangle *Rectangle1;
   TLayout *Layout1;
   TRectangle *Rectangle2;
   TRectangle *Rectangle3;
   TLabel *Label1;
   TButton *Button1;
   void __fastcall Button1Click(TObject *Sender);
private:   // User declarations
public:      // User declarations
   __fastcall TFrame1(TComponent* Owner);
    TNotifyEvent OnButtonClick;
};
//---------------------------------------------------------------------------
extern PACKAGE TFrame1 *Frame1;


Code: Select all
//cpp frame
#include <fmx.h>
#pragma hdrstop

#include "UnitFrameMessage.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TFrame1 *Frame1;
//---------------------------------------------------------------------------
__fastcall TFrame1::TFrame1(TComponent* Owner)
   : TFrame(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFrame1::Button1Click(TObject *Sender)
{
 if (OnButtonClick)
   OnButtonClick(this);
}


Main.
Code: Select all
//h main
class TForm1 : public TForm
{
//***
public:      // User declarations
   __fastcall TForm1(TComponent* Owner);
   void __fastcall FrameButtonClicked(TObject *Sender);

};


Code: Select all
//cpp main
void __fastcall TForm1::Button2Click(TObject *Sender)
{

   Frame1 = new TFrame1(this);
   Frame1->Parent = this;
   Frame1->Label1->Text = L"My first frame";
   Frame1->OnButtonClick = &FrameButtonClicked;
}

void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
   //TThread::ForceQueue(nullptr, [](){ Frame1->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [](){
            TThread::Queue(nullptr, [](){ Frame1->DisposeOf(); });
        }
    );
   Frame1 = nullptr;
}



I see my Frame. When I press Button1Click on my Frame nothing happens. The frame does not disappear. :(
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby rlebeau » Thu Feb 15, 2018 8:13 pm

Lena wrote:I see my Frame. When I press Button1Click on my Frame nothing happens. The frame does not disappear. :(


Make sure to call Start() on the TThread object that TThread::CreateAnonymousThread() returns. The thread is suspended by default. I forgot that in my earlier examples, but I have fixed it now.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1484
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TFrame create and delete

Postby Lena » Fri Feb 16, 2018 2:56 am

Make sure to call Start() on the TThread object that TThread::CreateAnonymousThread() returns.


Thank You rlebeau!
Problem still exists: I see my Frame. When I press Button1Click on my Frame nothing happens. The frame does not disappear. Were my mistake?
h frame:
Code: Select all
class TFrame1 : public TFrame
{
__published:   // IDE-managed Components
//***
   void __fastcall Button1Click(TObject *Sender);
private:   // User declarations
public:      // User declarations
   __fastcall TFrame1(TComponent* Owner);
    TNotifyEvent OnButtonClick;
};


cpp frame:
Code: Select all
TFrame1 *Frame1;
//---------------------------------------------------------------------------
__fastcall TFrame1::TFrame1(TComponent* Owner)
   : TFrame(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFrame1::Button1Click(TObject *Sender)
{
 if (OnButtonClick)
   OnButtonClick(this);
}


h main:
Code: Select all
class TForm1 : public TForm
{
//***
private:   // User declarations
public:      // User declarations
   __fastcall TForm1(TComponent* Owner);
   void __fastcall FrameButtonClicked(TObject *Sender);
};


cpp main:
Code: Select all
void __fastcall TForm1::Button2Click(TObject *Sender)
{

   Frame1 = new TFrame1(this);
   Frame1->Parent = this;
   Frame1->Label1->Text = L"My first frame";
    Frame1->OnButtonClick = &FrameButtonClicked;
}

void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
   //TThread::ForceQueue(nullptr, [](){ Frame1->DisposeOf(); });
    TThread::CreateAnonymousThread(
        [](){
            TThread::Queue(nullptr, [](){ Frame1->DisposeOf(); });
        }
    )->Start();
    Frame1 = nullptr;
}


More questions :)
1. Why we use TThread for delete Frame?
2. Do we need FreeOnTerminate = true?
3. Frame1->DisposeOf() I read what could be better Frame1->Release()???
Lena
BCBJ Master
BCBJ Master
 
Posts: 544
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby rlebeau » Fri Feb 16, 2018 3:12 pm

Lena wrote:Problem still exists: I see my Frame. When I press Button1Click on my Frame nothing happens. The frame does not disappear. Were my mistake?


Well, did you verify that the code is even TRYING to do something? Did you verify that Button1Click() is being called, and thus FrameButtonClicked() is being called? That TThread::Queue() is calling DisposeOf()? This is basic debugging stuff.

Lena wrote:1. Why we use TThread for delete Frame?


You need to free the TFrame object in a delayed manner after its Button1Click() handler has exited first. To ensure that no code is still trying to access the TFrame object when/after it is destroyed. Since you are using a version of C++Builder that doesn't support TThread::ForceQueue(), we have a TThread::Queue() alternative, except that TThread::Queue() is not delayed when called in the main UI thread, so we call it in a worker thread instead. TThread::ForceQueue() would have avoided the need for that worker thread.

Lena wrote:2. Do we need FreeOnTerminate = true?


TThread::CreateAnonymousThread() creates a TThread object that has FreeOnTerminate=true set.

Lena wrote:3. Frame1->DisposeOf() I read what could be better Frame1->Release()???


Oh yeah, I keep forgetting that all TFmxObject-derived objects (like TFrame) have a Release() method in FMX (whereas only TForm has it in VCL). Well, in that case:

Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    Frame1->Release();
    Frame1 = nullptr;
}


On the other hand, TFmxObject::Release() has been officially deprecated in 10.2 Tokyo, with no official word on what it should be replaced with. So, best to just design your code to account for that now, in case you decide to ever upgrade to Tokyo+ later. Of course, if you did upgrade, then you would have access to TThread::ForceQueue() and wouldn't have to worry about this threading issue anymore.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1484
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA


Return to Technical

Who is online

Users browsing this forum: Baidu [Spider] and 16 guests

cron