views:

347

answers:

1

Base question: http://stackoverflow.com/questions/1141544/tstatusbar-flickers-when-calling-update-procedure-ways-to-painlessly-fix-this

The executed code is in the questions first posts first part ( you can see light grey separating line ) ...

But - problem is that while this code is executed, form does not automatically activate and focus on the top of all other applications.

I have read these articles:

http://www.installationexcellence.com/articles/VistaWithDelphi/Original/Index.html
http://delphi.about.com/od/formsdialogs/l/aa073101b.htm

but according to them it should be working no matter what. I tried all the TApplicationEvents and TForm events with Show; Visible: Repaint; Refresh; BringToFront; ... nothing works.

So - I think I have two options - multithreading or trapping WM_SYSCOMMAND message and in the SC_ACTIVE event simply repaint form. Could this scenario become successful?

+4  A: 

None of your linked articles deal with the problem you are having. What you see is the behaviour of a program that does not process Windows messages, so consequently it will not redraw parts that become invalid, and it will not react to keyboard or mouse input (for example moving or resizing with the mouse, or app activation using the taskbar button).

In your code you call StatusBar1.Update, so at least the status bar text is redrawn, but apart from coming to the foreground your application is probably also ignoring move or resize requests.

You need to process Windows messages in a timely manner, so any execution path that takes more than say 200 or 300 milliseconds needs to make sure that messages are handled, otherwise the application will appear unresponsive or hung.

You have basically three options:

  • Keep the long running code, and insert calls to Application.ProcessMessages - this will allow Windows messages to be processed. Make sure that you keep the code from being entered again, for instance by disabling all the controls that are used to start the operation.

  • Rework your code in a way that it appears as a sequence of steps, each taking no more than a few 10 milliseconds. Put calls to the code in a timer event handler, or call it from the Application.OnIdle handler.

  • Call your code in a worker thread, and post messages to the main GUI thread to update your UI.

All these options have their own pros and cons, and for multithreading especially there is a lot of questions and answers already here on SO. It is the most difficult but best option overall when you are working on anything more than a toy program.

mghie
Posting messages from a background thread has its own pitfall: You must be sure that the window handle you are posting to has already been created before using it in the background thread, otherwise the thread will create it and messages posted to it will never reacht the main thread. So always call <wincontrol>HandleNeeded in the foreground thread before using that handle in the background thread.
dummzeuch
And yet another possible pitfall: Do not post to many messages, otherwise you will have a very busy event handler but your program will hardly get any work done. I've been there and done that myself and wondered why simple calculations took forever.
dummzeuch
@dummzeuch: Both are very important comments. Re the first: this can never happen if one obeys by the rule that VCL methods must not be called from worker threads - accessing the Handle property implies a method call. Let the thread have a HWND instead. Re the second: I usually handle this by calling calling LockedInc() on an integer field, and posting a message only if the field had the value 0. The receiver calls LockedExchange() to reset the field to zero when it starts handling the posted message. Works fine for me.
mghie