Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update GUI from Thread using Delphi

I am using Delphi anonymous thread to execute code. In the middle of the thread, a couple of GUI updates have to take place, a couple of labels changing etc.

If I do this from inside the thread, the changes take place, but as soon as the thread stops. they disappear, and then the application gives me the old window handler error...(Which is to be expected)

System Error. Code:1400. Invalid window handle

I tried using the Syncronize(updateui); method to execute the changes(moved them to a separate function), but I get an error on the syncronize E2066 Missing operator or semicolon which does not make sense to me at all...

I have searched through page after page, and they all seem to call it this way, but when I do, I get the above error...

Am I calling it wrong?

Code:

TThread.CreateAnonymousThread(
procedure
 begin
 main.Enabled:=false;
 Loading.show;
 label52.caption:=getfieldvalue(datalive.users,'users','credit_amount','user_id',user_id );
 CoInitialize(nil);
   if (length(maskedit1.Text)=maskedit1.MaxLength) and (pingip(serverip)=true) then
    begin
       if (strtofloat(label52.caption)>0) then
        begin
           ....do some work....

           Synchronize(updateui);
        end
       else Showmessage('Insufficient Funds. Please add funds to continue.');
    end
   else if (length(maskedit1.Text)<>maskedit1.MaxLength) then
    begin
     Showmessage('ID Number not long enough.');
    end
   else
    begin
     Showmessage('Could not connect to the server. Please check your internet connection and try again.');
    end;
 CoUnInitialize;
 loading.close;
 main.Enabled:=true;
end).start;

UpdateUI:

procedure TMain.updateui;
var
birthdate,deathdate:TDate;
begin
Panel3.Show;

Label57.Caption := 'Change 1';
Label59.Caption := 'Change 2';
Label58.Caption := 'Change 3';
Label60.Caption := 'Change 4';
Label62.Caption := 'Change 5';
Label70.Caption := 'Change 6';


ScrollBox1.Color := clwhite;
end;
like image 678
Marcel Avatar asked Jul 15 '15 14:07

Marcel


1 Answers

Use TThread.Synchronize and pass another anonymous function to it. Then you can call updateui in the anonymous function:

TThread.CreateAnonymousThread(
  procedure
  begin
    // do whatever you want

    TThread.Synchronize(nil,
      procedure
      begin
        updateui();
      end);

   // do something more if you want
  end
).Start();

Synchronizations are generally expensive (regarding performance). Only do them when they are really neccessary. You can increase the performance if you extend the updateui-method to reduce paint-operations.

This is possible to a call to SendMessage with WM_SETREDRAW:

procedure StopDrawing(const Handle: HWND);
const
  cnStopDrawing = 0;
begin
  SendMessage(Handle, WM_SETREDRAW, cnStopDrawing, 0);
end;

procedure ContinueDrawing(const Handle: HWND);
const
  cnStartDrawing = 1;
begin
  SendMessage(Handle, WM_SETREDRAW, cnStartDrawing, 0);

  // manually trigger the first draw of the window
  RedrawWindow(Handle, nil, 0,
    RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_ALLCHILDREN);
end;

Add a call to StopDrawing() at the top of updateui() and a call to ContinueDrawing() at the end of updateui(). The call to ContinueDrawing() should be in a finally-block. This will ensure that the window will be painted even after an exception has occured during the execution of updateui.

Example:

procedure TMain.updateui;
begin
  try
    StopDrawing(Handle);

    Panel3.Show;

    Label57.Caption := 'Change 1';
    Label59.Caption := 'Change 2';

    // ...
  finally
    // Code under finally gets executed even if there was an error
    ContinueDrawing(Handle);
  end;
end;
like image 94
Stefan Wanitzek Avatar answered Oct 09 '22 07:10

Stefan Wanitzek