Mega Code Archive

 
Categories / Delphi / Examples
 

Using Application.ProcessMessages

Title: Using Application.ProcessMessages Question: If you are new to Delphi you may be surprised to learn that under certain circumstances none of the event handlers you've coded into your programs will execute when the events occur. Preposterous? Consider that no more than one section of your program's code can execute at the same time. How, then, can your event handler code execute while your program is performing a lengthy task in a for loop for example? Even if you've never given this any thought, you may nonetheless be familiar with the problem: Your program's user interface is unresponsive during long executions. It appears that the application has died. Your program's main window turns white. The application is stuck in never-never land until lengthy tasks finish. What can you do to make your application function normally as you execute a lengthy task? Answer: A simple solution is Application.ProcessMessages. Application.ProcessMessages is a command that interrupts the execution of an application so that it can respond to events. Specifically, Application.ProcessMessages reads through the messages that Windows has accumulated for your program and selectively executes those parts of your program designed to respond to each message. Short of you calling them directly, this is the only way your event handlers will ever execute. Using Application.ProcessMessages Using Application.ProcessMessages is easy, but you'll need to know a little about the Application variable to use it. The Application variable is declared in Delphi's Forms unit. Here is the declaration: var Application: TApplication; When a Delphi program starts, an instance of TApplication is created. The public variable Application points to this instance. You can therefore make a call to Application.ProcessMessages in any unit that includes Forms in one of its uses clauses. uses Forms Whenever you code a loop (while, for, repeat/until) into your program, place a call to Application.ProcessMessages somewhere inside the loop. This will allow your application to repaint, minimize, maximize, resize, or respond to messages in some other way while your loop executes. The following lines of code illustrate the idea: for I := 1 to 1000 do begin .... .... .... Application.ProcessMessages; end; An Example Let's look at a simple example. This example loops through over 900 records with a progress bar and a label to display progress. To create the example yourself, start a new project and drop the following components onto the form: TTable TButton TProgressBar TLabel Click on the TTable component and set the DatabaseName property in the object inspector to DBDEMOS and the TableName property to ITEMS.DB. Then double click on the TButton component and code the OnClick event as follows: procedure TForm1.Button1Click(Sender: TObject); begin ProgressBar1.Max := Table1.RecordCount; ProgressBar1.Position := 0; Table1.First; while not Table1.EOF do begin Sleep(50); // Just to slow this down. ProgressBar1.Position := ProgressBar1.Position + 1; Label1.Caption := 'Record ' + IntToStr(ProgressBar1.Position); Table1.Next; end; end; Run the application and click the button. While the button's event handler is executing, notice that you cannot move the window. Placing another window on top causes your program's window to turn white. The caption of LabeL1 does not change. Now add a call to Application.ProcessMessages to the button's OnClick event handler. The event handler should now look like this: procedure TForm1.Button1Click(Sender: TObject); begin ProgressBar1.Max := Table1.RecordCount; ProgressBar1.Position := 0; Table1.First; while not Table1.EOF do begin Sleep(50); // Just to slow this down. ProgressBar1.Position := ProgressBar1.Position + 1; Label1.Caption := 'Record ' + IntToStr(ProgressBar1.Position); Application.ProcessMessages; Table1.Next; end; end; Run the application again and click the button. This time notice that you can move the window. Repaint works, and the label is changing! Everything is working great. Or is it? Where ProcessMessages is called, be aware that all events will be processed, even events that you may not want to have processed. It's up to you to protect your application from the user. Run the application again. Click the button. Wait a few seconds. Click the button again. Notice what has happened. The function started over again. To prevent this unwanted effect, disable the controls that may cause problems. Let's add a few lines of code to disable and enable the button: procedure TForm1.Button1Click(Sender: TObject); begin ProgressBar1.Max := Table1.RecordCount; ProgressBar1.Position := 0; Button1.Enabled := False; try Table1.First; while not Table1.EOF do begin Sleep(50); // Just to slow this down. ProgressBar1.Position := ProgressBar1.Position + 1; Label1.Caption := 'Record ' + IntToStr(ProgressBar1.Position); Application.ProcessMessages; Table1.Next; end; finally Button1.Enabled := True; end; end; You should now know enough about Application.ProcessMessages to use it in any Delphi program you write. Download sample project Read the artile also at the JEDI pages...