Page 1 of 1

[Android]TFrame create and delete

PostPosted: Mon Feb 12, 2018 6:18 am
by Lena
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???

Re: [Android]TFrame create and delete

PostPosted: Mon Feb 12, 2018 2:43 pm
by rlebeau
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;
}

Re: [Android]TFrame create and delete

PostPosted: Tue Feb 13, 2018 4:22 am
by Lena
Thank You very much! I will try Your code.

Re: [Android]TFrame create and delete

PostPosted: Tue Feb 13, 2018 6:05 am
by Lena
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

Re: [Android]TFrame create and delete

PostPosted: Tue Feb 13, 2018 12:34 pm
by rlebeau
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;
}

Re: [Android]TFrame create and delete

PostPosted: Wed Feb 14, 2018 2:54 am
by Lena
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. :)

Re: [Android]TFrame create and delete

PostPosted: Wed Feb 14, 2018 8:37 am
by Lena
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. :(

Re: [Android]TFrame create and delete

PostPosted: Thu Feb 15, 2018 8:13 pm
by rlebeau
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.

Re: [Android]TFrame create and delete

PostPosted: Fri Feb 16, 2018 2:56 am
by Lena
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()???

Re: [Android]TFrame create and delete

PostPosted: Fri Feb 16, 2018 3:12 pm
by rlebeau
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.

Re: [Android]TFrame create and delete

PostPosted: Sun Feb 18, 2018 5:40 am
by Lena
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.");
    }

}

Re: [Android]TFrame create and delete

PostPosted: Tue Feb 20, 2018 11:57 am
by rlebeau
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?