Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: I can't get FMX controls to repaint within an OnClick method.


This question is answered.


Permlink Replies: 11 - Last Post: Apr 25, 2016 10:32 AM Last Post By: Free Dorfman
Free Dorfman

Posts: 139
Registered: 2/4/12
I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 14, 2016 9:48 PM
DX Seattle. FMX.

Quick explanation: I'm developing a puzzle app and want a for-testing-purposes-only [ANSWERS] button on the form. When the user (me) clicks [ANSWERS], I want to reveal the answers for two seconds (in red) and then have the app revert to its normal look.

I thought this would be a ten minute effort. Uh... no...

I was a bit surprised (a while into this) that even in VCL, this doesn't work without the Repaint call.

procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Font.Color := clRed;
Label1.Repaint;
 
System.Classes.TThread.Sleep(2000);
 
Label1.Font.Color := clBlack;
end;


Without Repaint in the above code, I see NOTHING.

If I comment out the final line (setting the font back to black), the text does "go red" - but not until AFTER the two second delay.

So -- I'm assuming Windows never has the "opportunity" to step in and redraw during the OnClick event.

However, the Repaint call gets it done.

Now I need it to work for FMX. I have the following:

procedure TForm1.FormCreate(Sender: TObject);
begin
Label1.StyledSettings := Label1.StyledSettings - [TStyledSetting.FontColor]; //here so you know this isn't the issue
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.TextSettings.FontColor := TAlphaColorRec.Red;
Label1.Repaint; //here for FMX, but doesn't help, as it does in VCL
 
System.Classes.TThread.Sleep(2000);
 
Label1.TextSettings.FontColor := TAlphaColorRec.Black;
end;


The above code NEVER shows me red text. (Unless I comment out the line that resets the font to black, in which case it behaves exactly as the VCL version.)

* I've tried this with a TTimer created and enabled in the OnClick event - that didn't work.
* In place of Label1.Repaint, I've tried Invalidate (for the form itself) - no help.

So:

(1) Is there some blatant (simple?) thing that I'm doing wrong here?
(2) Is there a Repaint alternative for FMX?
(3) Should I be (somehow) using an animation to do this "timed thing"?
(4) Whatever "fix" you might have for this I'd like to work for Android and iOS as well.

Thanks In Advance.
John Kouraklis

Posts: 209
Registered: 3/10/01
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 7:18 AM   in response to: Free Dorfman in response to: Free Dorfman

Now I need it to work for FMX. I have the following:

procedure TForm1.FormCreate(Sender: TObject);
begin
Label1.StyledSettings := Label1.StyledSettings - [TStyledSetting.FontColor]; //here so you know this isn't the issue
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.TextSettings.FontColor := TAlphaColorRec.Red;
Label1.Repaint; //here for FMX, but doesn't help, as it does in VCL
 
System.Classes.TThread.Sleep(2000);
 
Label1.TextSettings.FontColor := TAlphaColorRec.Black;
end;


The above code NEVER shows me red text. (Unless I comment out the line that resets the font to black, in which case it behaves exactly as the VCL version.)

* I've tried this with a TTimer created and enabled in the OnClick event - that didn't work.
* In place of Label1.Repaint, I've tried Invalidate (for the form itself) - no help.

So:

(1) Is there some blatant (simple?) thing that I'm doing wrong here?
(2) Is there a Repaint alternative for FMX?
(3) Should I be (somehow) using an animation to do this "timed thing"?
(4) Whatever "fix" you might have for this I'd like to work for Android and iOS as well.

Hi,

I struggled with this in the past and it looks like there is a stupid bug with TLabel. If you set the color to Black (as it is by default when you drop a label in a form), changing the color of the label programmatically doesn't have any effect on it.

But, and here is the catch, if you change the color of the label in design time to something else than black then your code will change the color.

So, drop a label in a form and change its color to (say) blue. Then, in the OnCreate event of the form, set the color to black. After that, this:

   Label1.TextSettings.FontColor:=TAlphaColorRec.Red;


should work.

Then, a timer, as you describe, would do the job you need.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 10:29 AM   in response to: Free Dorfman in response to: Free Dorfman
Free wrote:

Label1.Font.Color := clRed;
Label1.Repaint;
System.Classes.TThread.Sleep(2000);
Label1.Font.Color := clBlack;

Your call to Sleep() is blocking the main UI message loop from processing
new messages. You need to get rid of the Sleep() altogether and use a TTimer
instead, in both VCL and FMX:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Font.Color := clRed;
  Timer1.Interval := 2000;
  Timer1.Enabled := True;
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  Label1.Font.Color := clBlack;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.TextSettings.FontColor := TAlphaColorRec.Red;
  Timer1.Interval := 2000;
  Timer1.Enabled := True;
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  Label1.TextSettings.FontColor := TAlphaColorRec.Black;
end;


* I've tried this with a TTimer created and enabled in the OnClick
event - that didn't work. *

I assure you, it does. You must not have used it correctly.

--
Remy Lebeau (TeamB)
Free Dorfman

Posts: 139
Registered: 2/4/12
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 11:07 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...

* I've tried this with a TTimer created and enabled in the OnClick
event - that didn't work. *

I assure you, it does. You must not have used it correctly.

--
Remy Lebeau (TeamB)

Remy,

I don't disagree with you. I was using the TTime "incorrectly". And I understand that Sleep "freezes" things. That's what I want: Change to Red, Freeze for two seconds, Back to Black.

I have an issue with the TTimer approach (resulting in my incorrect usage). Specifically, I want the app to be frozen for those two seconds, with no ability to interact with the app.

The code here works exactly as I want in VCL (as long as I call *Label1.Repaint*).

If I do anything in FMX to "force" a "repaint" before calling Sleep, it works in FMX. My problem is that [so far] the only thing I've found to "force the repaint" is to send a message to my bug window. An inelegant approach to say the least.

What would fix this for me is something akin to Repaint in FMX. I have done a fair amount of re-search-ing this and there's a lot on the web of people looking for the same "fix". I found QC entries at EMBT, and a number of other posts/blogs/etc speaking to this.

See: https://delphihaven.wordpress.com/2013/09/28/fmx-issue-inability-to-safely-process-paint-messages-immediately/

In the end - and, in particular, after reading the above page - I am using Application.ProcessMessages in FMX (where the Label1.Repaint call is in VCL). This works. But I consider it to be - also - a particularly inelegant solution.

This is what I now have, working under FMX - for Windows and Android:

procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.TextSettings.FontColor := TAlphaColorRec.Red;
Application.ProcessMessages; {force "Repaint"}
 
System.Classes.TThread.Sleep(2000);
 
Label1.TextSettings.FontColor := TAlphaColorRec.Black;
end;


I'm ok with the Application.ProcessMessages approach for now, as this code is only compiled in for my test environment. But I could certainly see wanting to do the same type of thing in final, release code...

Any further thoughts appreciated.

Thanks,
-Free
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: I can't get FMX controls to repaint within an OnClick method.[Edit]
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 11:45 AM   in response to: Free Dorfman in response to: Free Dorfman
Free wrote:

I have an issue with the TTimer approach (resulting in my incorrect
usage). Specifically, I want the app to be frozen for those two
seconds, with no ability to interact with the app.

Just disable your UI controls while the timer is running, and then reenable
them when the timer is finished. DO NOT block the main UI thread for anything!
Just because the user is not interacting with the UI does not mean the OS
is not. The main UI thread needs to remain responsive to the OS at all times,
even when not interacting with the user.

I'm ok with the Application.ProcessMessages approach for now

Then you clearly do not understand what it actually does, the pitfalls of
using it, and why it should be avoided. Anytime you have to resort to using
it to solve a problem, you need to stop and rethink what you are doing.

--
Remy Lebeau (TeamB)
Free Dorfman

Posts: 139
Registered: 2/4/12
Re: I can't get FMX controls to repaint within an OnClick method.[Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 12:04 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy,

I get all that. This is the first time in at least 12 years that I've typed "Application.ProcessMessages".

What I am looking for (along with many other people, as made evident by my research) is a way to Repaint an FMX control "on demand".

-Free
#########
Markus Humm

Posts: 5,113
Registered: 11/9/03
Re: I can't get FMX controls to repaint within an OnClick method.[Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 16, 2016 7:29 AM   in response to: Free Dorfman in response to: Free Dorfman
Am 15.04.2016 um 21:04 schrieb Free Dorfman:
Remy,

I get all that. This is the first time in at least 12 years that I've typed "Application.ProcessMessages".

What I am looking for (along with many other people, as made evident by my research) is a way to Repaint an FMX control "on demand".

-Free
#########

Hello,

that way does most likely not exist. I had such a case lately as well
and needed to pack my lengthy operation in a thread which called the
output function via synchronize.

The reason why this most likely doesn't exist is, that screen output
usually is seen as a slow functionality and the OS will only do that for
you app it it doesn't have anything meaníngfull else to do, so e.g. on
Windows it's triggered via Windows messages and one could think that
these message is simply being processen when the system sees it I
nowadays doubt it. I doubt that wm_paint on Windows is processed when
other messages are in the queue. I guess they'r eprocessed when the
queue is empty again (except for those peinding wm_paint messages).

And I suspect it will be the same (only maybe with some other
technology) on any other platform.

Greetings

Markus
Free Dorfman

Posts: 139
Registered: 2/4/12
Re: I can't get FMX controls to repaint within an OnClick method.[Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 18, 2016 4:08 PM   in response to: Markus Humm in response to: Markus Humm
Markus Humm wrote:
Hello,

that way does most likely not exist. I had such a case lately as well
and needed to pack my lengthy operation in a thread which called the
output function via synchronize.

The reason why this most likely doesn't exist is, that screen output
usually is seen as a slow functionality and the OS will only do that for
you app it it doesn't have anything meaníngfull else to do, so e.g. on
Windows it's triggered via Windows messages and one could think that
these message is simply being processen when the system sees it I
nowadays doubt it. I doubt that wm_paint on Windows is processed when
other messages are in the queue. I guess they'r eprocessed when the
queue is empty again (except for those peinding wm_paint messages).

And I suspect it will be the same (only maybe with some other
technology) on any other platform.

Greetings

Markus

Markus,

I think you are 100% correct here as well. Thanks again!

Correct about the why of it.

Remy was right with the quick fix: TTimer. (Which I tried. Must've done something wrong. Tried it again, following Remy's thoughts. Now it's grand)

-Free
#########
Free Dorfman

Posts: 139
Registered: 2/4/12
Re: I can't get FMX controls to repaint within an OnClick method.[Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2016 5:21 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy,

I reconsidered what I was doing. And I'm now using a TTimer as you suggested.

Thanks.
Mike Dixon

Posts: 29
Registered: 3/2/07
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 25, 2016 9:19 AM   in response to: Free Dorfman in response to: Free Dorfman
Have you tried using a color animation? I did this for a Click-to-Copy feature where the user would click to copy to the clipboard and I ran a color animation on the label color.
Mike Dixon

Posts: 29
Registered: 3/2/07
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 25, 2016 9:51 AM   in response to: Mike Dixon in response to: Mike Dixon
Mike Dixon wrote:
Have you tried using a color animation? I did this for a Click-to-Copy feature where the user would click to copy to the clipboard and I ran a color animation on the label color.

Follow-up to my previous reply.

There also may be an animation associated with the button style for mousedown/up/focus etc.

I just had to try this out, so I coded up the sample below. To test it I set the animation for 12 seconds instead of the 2 seconds in code, I could still type in an edit while the animation was running.

procedure TForm1.ButtonClick(Sender: TObject);
begin
// make sure button StyledSettings is [Family,Size,Style] (no FontColor)
DoRevealButton(TButton(Sender));
end;

procedure TForm1.DoRevealButton(AButton: TButton);
Var
ColorAni : TColorAnimation;
begin
if AButton.TagObject <> nil then Exit;
ColorAni := TColorAnimation.Create(AButton);
ColorAni.Parent := AButton;
AButton.TagObject := ColorAni;
ColorAni.StartValue := TAlphaColorRec.Red;
ColorAni.StopValue := TAlphaColorRec.Red;
ColorAni.Duration := 2.0;
ColorAni.PropertyName := 'TextSettings.FontColor';
ColorAni.OnFinish := RevealButtonAnimationFinish;
ColorAni.Start;
end;

procedure TForm1.RevealButtonAnimationFinish(Sender: TObject);
Var
btn : Tbutton;
ColorAni : TColorAnimation;
begin
ColorAni := TColorAnimation(sender);
btn := TButton(ColorAni.Parent);
if ColorAni.Running then ColorAni.Stop;
btn.TextSettings.Font.Style := [];
btn.TextSettings.FontColor := TAlphaColorRec.Black;
ColorAni.Free;
btn.TagObject := nil;
end;

Free Dorfman

Posts: 139
Registered: 2/4/12
Re: I can't get FMX controls to repaint within an OnClick method.  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 25, 2016 10:30 AM   in response to: Mike Dixon in response to: Mike Dixon
Mike,

I appreciate your thoughts and help here. That snippet may well prove useful to me!

I settled on the Timer. Although, I think many people missed the point that the code I was looking for was only ever going to be executed by me. In testing / development mode. That it would never be deployed. If I could've REPAINTed "on demand" I would've been fine with a simple call to Sleep. And I expressly wanted nothing else to happen (or be able to happen) during the "wait interval".

Thanks.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02