Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ModaResult not working in TForm.OnActivate event handler

132 views
Skip to first unread message

Enquiring Mind

unread,
Jun 6, 2008, 6:18:10 AM6/6/08
to
Hi,

I require a modal form to execute a lengthy procedure immediately after it
becomes visible, then close itself. Calling the lengthy procedure in the
form's OnShow event handler doesn't work, because it would get executed
before the form is shown. I therefore thought of calling the lengthy
procedure in the form's OnActivate event handler as shown in the following
code. However I have found that setting ModalResult within the handler
doesn't have the intended effect of closing the form. Neither does the call
to Close have any effect. I had a quick look at the ModalResult property in
the VCL code, and found that it doesn't have a setter procedure. I find this
slightly surprising as in the right circumstances setting its value does
cause the form to be closed. How can the desired behaviour be achieved?

procedure TForm1.FormActivate(Sender: TObject);
begin
if not FLengthyProcedureDone then
begin
ExecuteLengthyProcedure;
ModalResult:= mrOK;
Close;
end;
end;

TIA.

Regards,

EM


Enquiring Mind

unread,
Jun 6, 2008, 7:07:18 AM6/6/08
to

"Enquiring Mind" <Enquiri...@nospam.btopenworld.com> wrote in message
news:48490ed3$1...@newsgroups.borland.com...

I have since discovered that assigning the event handler I posted to the
form's OnPaint event does work correctly. However, I would still be
interested to know why it doesn't work when assigned to the OnActivate
event.


Enquiring Mind

unread,
Jun 6, 2008, 9:32:35 AM6/6/08
to

"Enquiring Mind" <Enquiri...@nospam.btopenworld.com> wrote in message
news:48491a59$1...@newsgroups.borland.com...

>
>
> I have since discovered that assigning the event handler I posted to the
> form's OnPaint event does work correctly.

Correct except for the fact that a TLabel component that's a member of the
form is *not* rendered ... The form contains just 2 components - the TLabel
just mentioned, and a TAnimate cpt. The latter is correctly rendered. To
investigate the problem I had a look at the virtual Paint method and found:

procedure TCustomForm.Paint;
begin
if Assigned(FOnPaint) then FOnPaint(Self);
end;

I would have expected this to call the inherited Paint method, which in turn
should render each form control somewhere along the call chain. But the
default rendering clearly does not get triggered here. So where and when is
the default painting of the form performed?

Any ideas why the TLabel component is not being rendered? (I have another
version of the same form in which the lengthy procedure is called within a
TTimer OnTimer event handler instead of in the OnPaint event handler, and in
that version the label *is* properly rendered.

EM


RandomAccess

unread,
Jun 6, 2008, 10:09:09 AM6/6/08
to
> I have since discovered that assigning the event handler I posted to the
> form's OnPaint event does work correctly.

You could put a disabled TTimer control on your form
and activate it from FormActivate - alternatively, you
can create a TTimer in Form.Create.

ie

procedure TMyFormActivate(Sender : TObject);
begin
OnActivate := nil; // don't need this event anymore
MyTimer.Enabled := true;
end;

Now, put your code in the timer's OnTimer event.
In that event handler, permanently disable the timer as you wont
need that anymore either.

ie
Procedure MyTimerTimer(Sender : TObject)
begin
MyTimer.Enabled := false; // don't need this anymore
// Call your work code here.
end;


best

regards

Enquiring Mind

unread,
Jun 6, 2008, 11:01:02 AM6/6/08
to

"RandomAccess" <Ran...@Access.com> wrote in message
news:48494512$1...@newsgroups.borland.com...

>
> You could put a disabled TTimer control on your form
> and activate it from FormActivate - alternatively, you
> can create a TTimer in Form.Create.
>
> ie
>
> procedure TMyFormActivate(Sender : TObject);
> begin
> OnActivate := nil; // don't need this event anymore
> MyTimer.Enabled := true;
> end;
>
> Now, put your code in the timer's OnTimer event.
> In that event handler, permanently disable the timer as you wont
> need that anymore either.
>
Thanks for that. In fact, that's the approach I first used - and it works
:) - but I was hoping that there might be a simpler and more efficient
approach that only needs to use the form's standard events. I figured out
that if there's an event that is fired just before the form is made visible
(i.e. OnShow), there should be another event that is fired just after the
form becomes visible, if only for reasons of symmetry! But that doesn't seem
to be the case.

Regards,

EM


RandomAccess

unread,
Jun 6, 2008, 10:38:43 AM6/6/08
to
Another approach is to call Application.ProcessMessages inside
your form's OnActivate handler and then post yourself a custom
message. Again, set OnActivate to nil before doing this.

The create a handler for your custom message.

best regards


Enquiring Mind

unread,
Jun 6, 2008, 11:05:12 AM6/6/08
to

"RandomAccess" <Ran...@Access.com> wrote in message
news:48494c00$1...@newsgroups.borland.com...
Thanks - sounds like a good approach. From where would one send the custom
message?

Best regards,

EM


Remy Lebeau (TeamB)

unread,
Jun 6, 2008, 1:00:05 PM6/6/08
to

"Enquiring Mind" <Enquiri...@nospam.btopenworld.com> wrote in message
news:48490ed3$1...@newsgroups.borland.com...

> However I have found that setting ModalResult within the
> handler doesn't have the intended effect of closing the form.

That is because the ModalResult is automatically reset back to 0 after the
form is activated before the modal message pump is started. The OnActivate
and OnShow events are too early in the creation process to do what you are
attempting.

> Neither does the call to Close have any effect.

For a modal form, Close() simply sets the ModalResult to mrCancel and does
nothing else. So you are still subject to the automatic reset.

> I had a quick look at the ModalResult property in the VCL
> code, and found that it doesn't have a setter procedure.

It doesn't need one.

> I find this slightly surprising as in the right circumstances setting
> its value does cause the form to be closed.

ShowModal() runs an internal message pump that periodically checks the
ModalResult value and then exits when the ModalResult is not zero anymore.

> How can the desired behaviour be achieved?

I suggest having the OnShow or OnActivate event handler post a custom
message to the form, or start a short timer. When the message is received,
or the timer elapses, you can then start your processing. For example:

const
WM_START_PROCESSING = WM_APP + 100;

private
procedure WMStartProcessing(var Message: TMessage); message
WM_START_PROCESSING;

procedure TForm1.FormShow(Sender: TObject);
begin
PostMessage(Handle, WM_START_PROCESSING, 0, 0);
end;

procedure TForm1.WMStartProcessing(var Message: TMessage);


begin
ExecuteLengthyProcedure;
ModalResult := mrOK;

end;


Gambit


Enquiring Mind

unread,
Jun 7, 2008, 4:50:40 AM6/7/08
to

"Remy Lebeau (TeamB)" <no....@no.spam.com> wrote in message
news:48496d4b$1...@newsgroups.borland.com...

>
>
> That is because the ModalResult is automatically reset back to 0 after the
> form is activated before the modal message pump is started. The
> OnActivate and OnShow events are too early in the creation process to do
> what you are attempting.

> ShowModal() runs an internal message pump that periodically checks the

> ModalResult value and then exits when the ModalResult is not zero anymore.
>

Thanks for these helpful clarifications.


>
> I suggest having the OnShow or OnActivate event handler post a custom
> message to the form, or start a short timer. When the message is
> received, or the timer elapses, you can then start your processing. For
> example:
>

<Code omitted>

Thanks for the code illustrating how to use messages to achieve the required
behaviour. I have now implemented this approach and it works fine. On the
face of it the message approach is to my mind more elegant than the timer
approach, because it's simpler and not does not involve the arbitrary delay
of a timer tick interval. On the other hand, the timer approach has the
advantage that the timer could be used to implement a time-out cut-off.

Another issue is that if the lengthy procedure goes on for too long, the
operating system may think that the application has hung. See

> news:xn0feo5j...@newsgroups.borland.com...

I presume that if the lengthy procedure doesn't contain calls to
ProcessMessages or PeekMessage then timer events will not be processed
whilst the lengthy procedure is executing. Since the lengthy procedure is
passed to the form as an event handler, there is no certainty that it will
contain a call to ProcessMessages or to PeekMessage.

To get around this problem, should one run the lengthy procedure in a
secondary thread, and leave the main thread to process Timer and Paint
messages?

EM


RandomAccess

unread,
Jun 7, 2008, 5:13:14 AM6/7/08
to

> To get around this problem, should one run the lengthy procedure in a
> secondary thread, and leave the main thread to process Timer and Paint
> messages?
>

Ideally, I would say yes.
You need to be sure the functionality of that lengthy procedure is thread
safe.


best regards


Enquiring Mind

unread,
Jun 9, 2008, 6:27:55 AM6/9/08
to

"RandomAccess" <Ran...@Access.com> wrote in message
news:484a512a$1...@newsgroups.borland.com...
Thanks for that. The question of ensuring that the lengthy procedure is
thread safe is quite tricky if the code is not placed directly in the
Execute method of the TThread object, but in an event handler passed to the
thread object by the calling thread, because the event handler could contain
absolutely any code! However by executing the separate thread concurrently
with a modal form having very little user input functionality ensures that
shared variables other than VCL components definitely cannot be accessed by
the calling thread whilst the secondary thread is running.

I have now run and compared the performance of separate versions of the
modal form that accompanies the execution of a lengthy procedure,. The first
version executes the lengthy procedure in the main thread, while the second
version executes it in a secondary thread. Needless to say, the version that
executes the procedure in the main thread is not satisfactory when the
lengthy procedure takes more than about 1 second to complete, because the
modal form cannot be dragged, and if it gets covered by the form of another
application, it is not repainted when the other form is removed. Last, but
not least, the application shows up as 'not responding' in the Task Manager
while the lengthy procedure is executing. In conclusion, therefore, the
answer seems to be that to execute the lengthy procedure in a separate
thread is more than just ideal - it's necessary!

Regards,

EM


Remy Lebeau (TeamB)

unread,
Jun 9, 2008, 1:58:30 PM6/9/08
to

"Enquiring Mind" <Enquiri...@nospam.btopenworld.com> wrote in message
news:484a4c3b$1...@newsgroups.borland.com...

> Another issue is that if the lengthy procedure goes on for too
> long, the operating system may think that the application has
> hung.

Lengthy procedures should not be performed in the main thread to begin with.
That is what threads are for instead.

> I presume that if the lengthy procedure doesn't contain calls
> to ProcessMessages or PeekMessage then timer events will
> not be processed whilst the lengthy procedure is executing.

For message-based timers, like TTimer, that run in the same thread as the
lengthy procdure, yes. Threaded timers, like multimedia timers, are not
effected by that.

> To get around this problem, should one run the lengthy procedure in a
> secondary thread, and leave the main thread to process Timer and Paint
> messages?

Yes.


Gambit


Enquiring Mind

unread,
Jun 10, 2008, 11:34:43 AM6/10/08
to

"Remy Lebeau (TeamB)" <no....@no.spam.com> wrote in message
news:484d...@newsgroups.borland.com...

>
>
>> I presume that if the lengthy procedure doesn't contain calls
>> to ProcessMessages or PeekMessage then timer events will
>> not be processed whilst the lengthy procedure is executing.
>
> For message-based timers, like TTimer, that run in the same thread as the
> lengthy procdure, yes. Threaded timers, like multimedia timers, are not
> effected by that.
>
I am not familiar with threaded timers. Is a threaded timer a custom thread
object in whose thread method there's a loop that calls Sleep then
PostMessage or a timer action to be performed? Or is a VCL component?

Remy Lebeau (TeamB)

unread,
Jun 10, 2008, 1:58:38 PM6/10/08
to

"Enquiring Mind" <Enquiri...@nospam.btopenworld.com> wrote in message
news:484e...@newsgroups.borland.com...

> I am not familiar with threaded timers. Is a threaded timer a custom
> thread object in whose thread method there's a loop that calls Sleep
> then PostMessage or a timer action to be performed? Or is a
> VCL component?

No. I am referring to the Win32 API timeSetEvent() function (TTimer uses
the Win32 API SetTimer() function instead).


Gambit


0 new messages