[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: 546
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: 1488
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: 546
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: 546
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: 1488
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: 546
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: 546
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: 1488
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: 546
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: 1488
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TFrame create and delete

Postby Lena » Sun Feb 18, 2018 5:40 am

Thank You veru much! Now everything works well.
Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
    Frame1->Release();
    Frame1 = nullptr;
}


I want to replace all ShowMessage to show Frame because ShowMessage it does not work in FMX for FullScreen = true.
https://forums.embarcadero.com/thread.j ... 0&tstart=0

I use Indy. Can I creation TFrame in an event ThreadTerminated?
Code: Select all
void __fastcall TFormMenu::ThreadTerminated(TObject *Sender)
{

    Button2->Enabled = true;
   AniIndicator1->Visible = false;
   Close();

   TMyConnect *Thread = static_cast<TMyConnect*>(Sender);
   if(Thread->FatalException)//if setver OFF = Connect timed out.
    {
     chek = 1;

        Frame1 = new TFrame1(this);
        Frame1->Parent = this;
        Frame1->Label1->Text = L"Sorry, there is no connection to the server.";
        Frame1->OnButtonClick = &(Form1->FrameButtonClicked); //&FrameButtonClicked;
        //ShowMessage(L"Sorry, there is no connection to the server.");
    }

}
Lena
BCBJ Master
BCBJ Master
 
Posts: 546
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby rlebeau » Tue Feb 20, 2018 11:57 am

Lena wrote:I use Indy. Can I creation TFrame in an event ThreadTerminated?


The TThread::OnTerminate event is synchronized with the main UI thread by default. So yes, you can do whatever you want with your UI (you are already doing it with Button2 and AniIndicator1). That has nothing to do with Indy.

On the other hand, your OnTerminate handler is Close()'ing the same Form that you are setting as the Parent for your TFrame, so the user won't get a chance to see your error message. Maybe you meant to use Form1 as the Parent instead?
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1488
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [Android]TFrame create and delete

Postby Lena » Wed Feb 21, 2018 1:19 am

Maybe you meant to use Form1 as the Parent instead?


Yes you are right. It works now. Thank you very much.

Code: Select all
void __fastcall TFrame2::Button1Click(TObject *Sender)
{
 //Frame2->Release();
 //Frame2 = nullptr;
  if (OnButtonClick)
   OnButtonClick(this);
}
//---------------------------------------------------------------------------
void __fastcall TFrame2::ThreadTerminated(TObject *Sender)
{

    Button2->Enabled = true;
   AniIndicator1->Visible = false;

   //close Frame2 with Memo1
   Frame2->Release();
   Frame2 = nullptr;

   TMyConnect *Thread = static_cast<TMyConnect*>(Sender);
   if(Thread->FatalException)//if setver OFF=Connect timed out.
    {
     //show Frame1 with message
        Frame1 = new TFrame1(Form1);
        Frame1->Parent = Form1;
        Frame1->Label1->Text = L"Sorry, there is no connection to the server.";
        Frame1->OnButtonClick = &(Form1->FrameButtonClicked);
        //ShowMessage(L"Sorry, there is no connection to the server.");
    }
    else
      {
      Frame1 = new TFrame1(Form1);
      Frame1->Parent = Form1;
      Frame1->Label1->Text = L"Thank you.";
      Frame1->OnButtonClick = &(Form1->FrameButtonClicked);
        Form1->Button2Click(0);
      }


}
Lena
BCBJ Master
BCBJ Master
 
Posts: 546
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]TFrame create and delete

Postby ingalime » Thu Feb 22, 2018 4:02 am

I read this topic. Why can not you just delete TFrame?
It will be a critical mistake for android?
Code: Select all
void __fastcall TFrame1::Button1Click(TObject *Sender)
{
   Frame1->Release();
   Frame1 = nullptr;
}

Or should I must use always TNotifyEvent OnButtonClick for delete TFrame in FMX?
Code: Select all
void __fastcall TFrame1::Button1Click(TObject *Sender)
{
 if (OnButtonClick)
   OnButtonClick(this);
}


Another question. Do I need to do extra checks when delete TFrame?
Code: Select all
void __fastcall TForm1::FrameButtonClicked(TObject *Sender)
{
 if(Frame1 != NULL)//<--- ?
  {
   Frame1->Parent = NULL; //<--- ?
        Frame1->Release();
   Frame1 = nullptr;
  }
}


С++ Builder Berlin.
Thank you for your advice.
ingalime
Active Poster
Active Poster
 
Posts: 19
Joined: Wed Apr 12, 2017 3:22 am

Re: [Android]TFrame create and delete

Postby rlebeau » Thu Feb 22, 2018 1:57 pm

ingalime wrote:I read this topic. Why can not you just delete TFrame?


Because it is not safe to immediately 'delete' an object while you are inside an event handler that is owned by that same object. The RTL still needs access to the object after the event handler exits. So the 'delete' needs to be deferred to a later time when the object is not being accessed anymore.

ingalime wrote:
Code: Select all
void __fastcall TFrame1::Button1Click(TObject *Sender)
{
   Frame1->Release();
   Frame1 = nullptr;
}



If you read the discussion more carefully, you will see that 1) calling Release() is one of the proposed solutions, since Release() is a deferred 'delete', and 2) Release() is deprecated in 10.2 Tokyo.

ingalime wrote:Or should I must use always TNotifyEvent OnButtonClick for delete TFrame in FMX?


If you read the discussion more carefully, you will see that the OnButtonClick event was being used because the Frame1 object pointer is owned by another object (the MainForm), so while the TFrame1 object *could* Release() itself, the reference to the released TFrame1 object still needs to be nulled out. Letting the MainForm decide how to manage its TFrame1 object pointer is a better choice from a design perspective.

ingalime wrote:Another question. Do I need to do extra checks when delete TFrame?


Not in the example in this discussion, since OnButtonClick is fired by the TFrame1 object, so the MainForm's Frame1 object pointer should never be null when the OnButtonClick event is fired. And you don't need to set the Parent to null before calling Release().
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1488
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA


Return to Technical

Who is online

Users browsing this forum: No registered users and 16 guests

cron