Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get window to refresh (etc) without calling Application.ProcessMessages?

Tags:

delphi

I've got a legacy app here that has a few 'time-consuming' loops that get fired off as a result of various user interaction. The time-consuming code periodically updates something on the screen with progress information (typically a label) and then, seemingly to persuade the visual refresh to happen there-and-then, the code calls Application.ProcessMessages (argh!).

We all know now what kind of trouble this can introduce to a GUI app (being charitable, it was a more innocent time back then) and we're finding that sure as eggs, from time to time we get users achieving the impossible with the program because they're clicking on controls while the program is 'busy'.

What's the best way of periodically refreshing the visuals of the form without taking-on other events/messages etc?

My thoughts were to either;
- disable all of the controls before doing anything time-consuming, and leaving the '...ProcessMessages' calls in place to 'force' the refresh, or
- find another way to refresh a control periodically

I can do the former but it left me wondering - is there a better solution?

example code from legacy;

i:=0;
while FJobToBeDone do
begin
  DoStepOfLoop;
  inc(i);
  if i mod 100 = 0 then
  begin
    UpdateLabelsEtc;
    Application.ProcessMessages;
  end;
end;

I can already hear you all fainting, at the back. :-)

like image 483
robsoft Avatar asked Jul 17 '09 09:07

robsoft


3 Answers

If you call Update() on the controls after you have changed properties you will force them to redraw. Another way is to call Repaint() instead of Refresh(), which implies a call to Update().

You may need to call Update() on parent controls or frames as well, but this could allow you to eliminate the ProcessMessages() call completely.

like image 154
mghie Avatar answered Oct 06 '22 03:10

mghie


The solution I use for long updates is by doing the calculations in a separate thread. That way, the main thread stays very responsive. Once the thread is done, it sends a Windows message to the main thread, indicating the main thread can process the results.

This has some other, severe drawbacks though. First of all, while the other thread is active, you'll have to disable a few controls because they might restart the thread again. A second drawback is that your code needs to become thread-safe. This can be a real challenge sometimes. If you're working with legacy code, it's very likely that your code won't be thread-safe. Finally, multi-threaded code is harder to debug and should be done by experienced developers.

But the big advantage of multi-threading is that your application stays responsive and the user can just continue to do some other things until the thread is done. Basically, you're translating a synchronous method into an asynchronous function. And the thread can fire several messages indicating certain controls to refresh their own data, which would be updated immediately, on the fly. (And at the moment when you want them to be updated.)

I've used this technique myself quite a few times, because I think it's much better. (But also more complex.)

like image 38
Wim ten Brink Avatar answered Oct 06 '22 03:10

Wim ten Brink


Do not call Application.Processmessages, This is slow and might generate unlimited recursion. For example, to update everything in Panel1 without flick, we can use this method:

procedure TForm1.ForceRepaint;
var
  Cache: TBitmap;
  DC: HDC;
begin
  Cache := TBitmap.Create;
  Cache.SetSize(Panel1.Width, Panel1.Height);
  Cache.Canvas.Lock;
  DC := GetDC(Panel1.Handle);
  try
    Panel1.PaintTo(Cache.Canvas.Handle, 0, 0);
    BitBlt(DC, 0, 0, Panel1.Width, Panel1.Height, Cache.Canvas.Handle, 0, 0, SRCCOPY);
  finally
    ReleaseDC(Panel1.Handle, DC);
    Cache.Canvas.Unlock;
    Cache.Free;
  end;
end;

For better performance, the cache bitmap should be created at first and free when the process has finished

like image 27
Le Minh Hoang Avatar answered Oct 06 '22 03:10

Le Minh Hoang