Synchronize not updating UI elements in a child form - multithreading

I created a thread class in Form1 Unit1.pas. Then in that thread class I also declare a method to update a shape color in that same Unit1.pas's Form1 class. This works fine.
But when I try to do the same in a child form, the shape color is not changing. Anybody know why?
Here is my code in my test application:
Parent unit1.pas is listed below. Parent Form1 only has 2 buttons and 1 shape.
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, unit2;
type
{ TMainPortThread }
TMainPortThread = class(TThread)
private
procedure Synchronous;
protected
procedure Execute; override;
public
// constructor Create(CreateSuspended: boolean);
end;
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Label1: TLabel;
Shape1: TShape;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
what:boolean;
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
procedure Delay(AMiliSeconds: DWORD);
var
DW: DWORD;
begin
DW := GetTickCount64;
while (GetTickCount64 < DW + AMiliSeconds) and (not Application.Terminated) do
begin
// nothing
end;
end;
{ For Thread }
procedure TMainPortThread.Synchronous;
begin
writeln('Parents Synchronous Method');
if Form1.what then
Form1.Shape1.Brush.Color := clRed
else
Form1.Shape1.Brush.Color := clLime;
end;
procedure TMainPortThread.Execute;
var
i:integer;
begin
while true do begin
Delay(500);
Synchronize(#Synchronous);
Form1.what := not Form1.what;
end;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
settings : TForm2;
begin
settings := TForm2.Create(Form1);
settings.Show;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
TMainPortThread.Create(false);
end;
end.
The child Unit2.pas and Form2. Form2 only has 1 button and 1 shape.
unit Unit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls;
type
{ TMainPortThread }
TSecondPortThread = class(TThread)
private
procedure Synchronous;
protected
procedure Execute; override;
public
// constructor Create(CreateSuspended: boolean);
end;
{ TForm2 }
TForm2 = class(TForm)
Button1: TButton;
Label1: TLabel;
Shape1: TShape;
procedure Button1Click(Sender: TObject);
private
what:boolean;
public
end;
var
Form2: TForm2;
implementation
{$R *.lfm}
procedure Delay(AMiliSeconds: DWORD);
var
DW: DWORD;
begin
DW := GetTickCount64;
while (GetTickCount64 < DW + AMiliSeconds) and (not Application.Terminated) do
begin
// nothing
end;
end;
{ For Thread }
procedure TSecondPortThread.Synchronous;
begin
writeln('Child Synchronous Method');
if Form2.what then
Form2.Shape1.Brush.Color := clRed
else
Form2.Shape1.Brush.Color := clLime;
end;
procedure TSecondPortThread.Execute;
var
i:integer;
begin
while true do begin
Delay(500);
Synchronize(#Synchronous);
Form2.what := not Form2.what;
end;
end;
{ TForm2 }
procedure TForm2.Button1Click(Sender: TObject);
begin
TSecondPortThread.Create(false);
end;
end.
In Form1, the shape color alternates between red and green after click the button. Then open Form2 by clicking another button and click the button on Form2, the shape just stay the same color only. I don't get it.
If you want complete project, here it is:
https://mega.nz/file/y8oEkbTS#EpsxNL6WhZL5qWvBijqcMD4NsPPn2c70CIsM7jwcHXc
Edit: #Tom gave the correct answer.
Just to add, since I wanted to retain the new form instance creation method, so to do it just remove the autocreate code in project.lpr, then use the actual Form name when creating it at Button1Click. ie Form2.CreateForm(). Do not declare another variable instance of the Form2 in Form1.

In procedure TSecondPortThread.Synchronous; you are referring to the autocreated form Form2 and its shape. However, even though the Form2 is created, it is never shown.
Instead, in procedure TForm1.Button1Click(Sender: TObject); you are creating and showing an instance named settings of type TForm2. But the settings form is not known to the blink timer thread.
One correction would be, as the Form2 is autocreated, to simply replace all code in TForm1.Button1Click() with Form2.Show.

Related

TFloatAnimation within anonymous thread. Thread wont die and animation wont restart

I've been struggling with this for a day or two and cannot find an answer anywhere. I thought this answer might help but it didn't.
In my sample code below I have two Timage Components each containing a "Start Image". When clicking on the "Start Button" two anonymous threads are created, one animates Image1 between the Start Image and End Image, the other does the same for Image2.
My problem is that when the KillAnimation boolean is set to True both animations should stop (which they do) but only one of the threads exit, the other one stops animating but leaves the image mid-animation.
I have the same problem if I use a predefined thread as well.
The sample application has only two images, the real world app can have anywhere from 15 to 24. Anonymous threads seem to suit because I can create them and no have to worry about defining up to 24 TThreads. I hope that makes sense.
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Objects,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Ani, FMX.Effects,
FMX.Filter.Effects;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
BtnStop: TButton;
BtnStart: TButton;
BtnReset: TButton;
procedure BtnStopClick(Sender: TObject);
procedure BtnStartClick(Sender: TObject);
procedure BtnResetClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure createthread(TheImage: TImage; TargetBMP: String);
end;
var
Form1: TForm1;
KillAnimation: Boolean;
implementation
{$R *.fmx}
procedure TForm1.BtnStopClick(Sender: TObject);
begin
KillAnimation := true;
end;
procedure TForm1.createthread(TheImage: TImage; TargetBMP: String);
begin
{ The thread works right up until it is stopped. Even though two
threads are started only one finishes. In addition, the images
do not end up as the target image, and neither image can be
reset even if I reload them from files. }
TThread.CreateAnonymousThread(
procedure()
var
Wiggle: TWiggleTransitionEffect;
TheFloat: TFloatAnimation;
begin
TThread.NameThreadForDebugging('Animate ' + TheImage.Name);
Wiggle := TWiggleTransitionEffect.Create(Nil);
Wiggle.RandomSeed := 0.3;
Wiggle.Progress := 0;
Wiggle.Parent := TheImage;
TThread.Synchronize(TThread.CurrentThread,
procedure()
Begin
Wiggle.Target.LoadFromFile(TargetBMP)
end);
TheFloat := TFloatAnimation.Create(Nil);
TheFloat.Parent := Wiggle;
TheFloat.PropertyName := 'Progress';
TheFloat.Duration := 2;
TheFloat.AutoReverse := true;
TheFloat.Loop := true;
TheFloat.StartValue := 0;
TheFloat.StopValue := 100;
TheFloat.StartFromCurrent := false;
TheFloat.start;
while not KillAnimation do
application.handlemessage;
TheFloat.stop;
end).start;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');
end;
procedure TForm1.BtnStartClick(Sender: TObject);
begin
KillAnimation := false;
createthread(Image1, 'c:\sample\endimage.png');
createthread(Image2, 'c:\sample\endimage.png');
end;
procedure TForm1.BtnResetClick(Sender: TObject);
begin
KillAnimation := false;
Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');
end;
end.
I would have thought that creating the Transition and FloatAnimation within the thread meant they would be destroyed when done because FreeOnTerminate is True.
When I try to reset the Images using "image1.bitmap.loadfromfile" it doesn't change.
The images are 170 x 170 png files.
What have I done wrong here?
My goal is to pass TImage and an Image File to the thread, let it animate until told to stop. It is unlikely that I'll need all 24 images to animate, but you never know.
Apologies if I shouldn't have posted all the code from my sample. At least you can see everything going on.
You should NOT be using a thread for this at all. UI elements, including visual effects, should be used only in the main UI thread. And unless you are developing your app for mobile platforms, objects are not destroyed automatically when they go out of scope, you have to destroy them yourself when you are done using them, or else assign them an Owner that will destroy them for you.
Try this instead:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Objects, FMX.Controls.Presentation,
FMX.StdCtrls, FMX.Ani, FMX.Effects, FMX.Filter.Effects;
type
TForm1 = class(TForm)
Image1: TImage;
Image2: TImage;
BtnStop: TButton;
BtnStart: TButton;
BtnReset: TButton;
procedure BtnStopClick(Sender: TObject);
procedure BtnStartClick(Sender: TObject);
procedure BtnResetClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
Float1: TFloatAnimation;
Float2: TFloatAnimation;
function PrepareEffect(TheImage: TImage; const TargetBMP: String): TFloatAnimation;
procedure ResetImages;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.BtnResetClick(Sender: TObject);
begin
ResetImages;
end;
procedure TForm1.BtnStartClick(Sender: TObject);
begin
Float1.start;
Float2.start;
end;
procedure TForm1.BtnStopClick(Sender: TObject);
begin
Float1.stop;
Float2.stop;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ResetImages;
Float1 := PrepareEffect(Image1, 'c:\sample\endimage.png');
Float2 := PrepareEffect(Image2, 'c:\sample\endimage.png');
end;
function TForm1.PrepareEffect(TheImage: TImage; const TargetBMP: String): TFloatAnimation;
var
Wiggle: TWiggleTransitionEffect;
TheFloat: TFloatAnimation;
begin
Wiggle := TWiggleTransitionEffect.Create(Self);
Wiggle.RandomSeed := 0.3;
Wiggle.Progress := 0;
Wiggle.Parent := TheImage;
Wiggle.Target.LoadFromFile(TargetBMP);
TheFloat := TFloatAnimation.Create(Self);
TheFloat.Parent := Wiggle;
TheFloat.PropertyName := 'Progress';
TheFloat.Duration := 2;
TheFloat.AutoReverse := true;
TheFloat.Loop := true;
TheFloat.StartValue := 0;
TheFloat.StopValue := 100;
TheFloat.StartFromCurrent := false;
Result := TheFloat;
end;
procedure TForm1.ResetImages;
begin
Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');
end;
end.

delphi thread error in subform

I have problem when run a thread in subform.
main form
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Unit2;
procedure TForm1.Button1Click(Sender: TObject);
begin
TForm2.create(form1).ShowModal;
end;
SUBform
type
TMthread=class(Tthread)
protected
procedure execute; override;
end;
type
TForm2 = class(TForm)
Label1: TLabel;
procedure FormShow(Sender: TObject);
private
public
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
uses Unit1;
procedure TMthread.Execute;
begin
synchronize( procedure
begin
sleep(200);
freeonterminate:=true;
sleep(200);
form2.label1.Caption:='beep';
form1.button1.Caption:='beep';
end);
end;
procedure TForm2.FormShow(Sender: TObject);
var Loadcombo2: TMthread;
begin
Loadcombo2:=TMthread.Create(False);
end;
Program
program Project1;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
I got error in Execute Procedure when trying to access Form2.Label1.caption.
my test:
When I add the subform(Form2) in the Initialize section(last code) the the application runs without error, but doesn't change Label1.caption on the Form2.(Button1.caption on the main form is changed)
When I put exactly the same thread in main form it works without issues.
The variable Form2 is never assigned. Because it is a global variable, its value is nil. Thus you encounter an error when you attempt to reference members of Form2.
You create an instance of Form2 like this:
TForm2.Create(Form1).ShowModal;
I suspect that instead you mean to write something like this:
Form2 := TForm2.Create(Form1);
Try
Form2.ShowModal;
Finally
Form2.Free;
End;

Delphi - Updating status bar from thread

What is the best way to update a status bar in mainform from one tthread class object.
For Example, I have one TThread object that make a very big quanty of stuff and i want that the verbose message acording to what soft do you do appear on status bar.
Create the thread with a reference to a callback method, which can be called synchonized if assigned.
Example;
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TInfoMessageCall=Procedure (const info:String) of object;
TMyThread=Class(TThread)
Constructor Create(Susp:Boolean;CallBack:TInfoMessageCall);overload;
Procedure Execute;override;
private
FMessage:String;
FInfoProc:TInfoMessageCall;
Procedure CallCallBack;
End;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure ACallBack(const s: String);
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TMyThread }
Procedure TForm1.ACallBack(Const s:String);
begin
Caption := s;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
With TMyThread.Create(false,ACallBack) do
FreeOnTerminate := true;
end;
procedure TMyThread.CallCallBack;
begin
if Assigned(FInfoProc) then FInfoProc(FMessage);
end;
constructor TMyThread.Create(Susp: Boolean; CallBack: TInfoMessageCall);
begin
Inherited Create(Susp);
FInfoProc := CallBack;
end;
procedure TMyThread.Execute;
var
i:Integer;
begin
inherited;
for I := 1 to 10 do
begin
FMessage := Format('Info %d',[i]);
Synchronize(CallCallBack);
Sleep(200);
end;
end;
end.

Delphi: Indy TIdTCPClient Reading Data

I am using Delphi 2007 & Indy 10; I am a bit of a Delphi noob so apologies if I have missed something obvious...
Background: I have a simple server app which simply sends the word "PING" when you connect to its port. It will also respond if it receives the word "PONG". This is working fine, I have manually tested this using netcat/wireshark.
I am trying to code my client to connect to the port and automatically respond to the word PING whenever it receives it. I have created a simple form with a button to manually connect.
The client connects, but it does not respond to the word PING.
I think the problem lies with:
TLog.AddMsg(FConn.IOHandler.ReadLn);
My debug log reports only as far as "DEBUG: TReadingThread.Execute - FConn.Connected".
My client code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdCustomTransparentProxy, IdSocks, IdBaseComponent,
IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
IdTCPConnection, IdTCPClient, IdSync;
type
TReadingThread = class(TThread)
protected
FConn: TIdTCPConnection;
procedure Execute; override;
public
constructor Create(AConn: TIdTCPConnection); reintroduce;
end;
TLog = class(TIdSync)
protected
FMsg: String;
procedure DoSynchronize; override;
public
constructor Create(const AMsg: String);
class procedure AddMsg(const AMsg: String);
end;
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
IdIOHandlerStack1: TIdIOHandlerStack;
client: TIdTCPClient;
IdSocksInfo1: TIdSocksInfo;
procedure Button1Click(Sender: TObject);
procedure clientConnected(Sender: TObject);
procedure clientDisconnected(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
rt: TReadingThread = nil;
implementation
{$R *.dfm}
constructor TReadingThread.Create(AConn: TIdTCPConnection);
begin
Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Create'); // Debug
FConn := AConn;
inherited Create(False);
end;
procedure TReadingThread.Execute;
begin
Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Execute'); // Debug
while not Terminated and FConn.Connected do
begin
Form1.Memo1.Lines.Add('DEBUG: TReadingThread.Execute - FConn.Connected'); // Debug
TLog.AddMsg(FConn.IOHandler.ReadLn);
end;
end;
constructor TLog.Create(const AMsg: String);
begin
Form1.Memo1.Lines.Add('DEBUG: TLog.Create'); // Debug
FMsg := AMsg;
inherited Create;
end;
procedure TLog.DoSynchronize;
var
cmd : string;
begin
Form1.Memo1.Lines.Add('DEBUG: TLog.DoSynchronize'); // Debug
cmd := copy(FMsg, 1, 1);
if cmd='PING' then begin
Form1.client.Socket.WriteLn('PONG');
end
end;
class procedure TLog.AddMsg(const AMsg: String);
begin
Form1.Memo1.Lines.Add('DEBUG: TLog.AddMsg'); // Debug
with Create(AMsg) do try
Synchronize;
finally
Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Clear;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Host : String;
Port : Integer;
begin
Host := '127.0.0.1';
Port := StrToInt('1234');
client.Host := Host;
client.Port := Port;
with client do
begin
try
Connect;
except
on E: Exception do
Memo1.Lines.Add('Error: ' + E.Message);
end;
end;
end;
procedure TForm1.clientConnected(Sender: TObject);
begin
Form1.Memo1.Lines.Add('DEBUG: TForm1.clientConnected'); // Debug
rt := TReadingThread.Create(client);
end;
procedure TForm1.clientDisconnected(Sender: TObject);
begin
Form1.Memo1.Lines.Add('DEBUG: TForm1.clientDisconnected'); // Debug
if rt <> nil then
begin
rt.Terminate;
rt.WaitFor;
FreeAndNil(rt);
end;
end;
end.
Any help/advice would be appreciated.
Thanks
The reading thread is directly accessing Form1.Memo1, which is not thread safe and can cause deadlocks, crashes, corrupted memory, etc. So it is possible that the reading thread is not even reaching the ReadLn() call at all. You MUST synchronize ALL access to UI controls to the main thread, no matter how trivial the access actually is. Just don't risk it.
Also, you are doing your thread's ping/pong logic inside of TLog itself, where it does not belong. Not to mention that you are truncating the cmd to only its first character before checking its value, so it will NEVER detect a PING command. You need to move the logic back into the thread, where it really belongs, and remove the truncation.
Try this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdCustomTransparentProxy, IdSocks, IdBaseComponent,
IdComponent, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
IdTCPConnection, IdTCPClient, IdSync;
type
TReadingThread = class(TThread)
protected
FConn: TIdTCPConnection;
procedure Execute; override;
procedure DoTerminate; override;
public
constructor Create(AConn: TIdTCPConnection); reintroduce;
end;
TLog = class(TIdSync)
protected
FMsg: String;
procedure DoSynchronize; override;
public
constructor Create(const AMsg: String);
class procedure AddMsg(const AMsg: String);
end;
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
IdIOHandlerStack1: TIdIOHandlerStack;
client: TIdTCPClient;
IdSocksInfo1: TIdSocksInfo;
procedure Button1Click(Sender: TObject);
procedure clientConnected(Sender: TObject);
procedure clientDisconnected(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
rt: TReadingThread = nil;
implementation
{$R *.dfm}
constructor TReadingThread.Create(AConn: TIdTCPConnection);
begin
TLog.AddMsg('DEBUG: TReadingThread.Create');
FConn := AConn;
inherited Create(False);
end;
procedure TReadingThread.Execute;
var
cmd: string;
begin
TLog.AddMsg('DEBUG: TReadingThread.Execute');
while not Terminated do
begin
cmd := FConn.IOHandler.ReadLn;
TLog.AddMsg('DEBUG: TReadingThread.Execute. Cmd: ' + cmd);
if cmd = 'PING' then begin
FConn.IOHandler.WriteLn('PONG');
end
end;
end;
procedure TReadingThread.DoTerminate;
begin
TLog.AddMsg('DEBUG: TReadingThread.DoTerminate');
inherited;
end;
constructor TLog.Create(const AMsg: String);
begin
inherited Create;
FMsg := AMsg;
end;
procedure TLog.DoSynchronize;
begin
Form1.Memo1.Lines.Add(FMsg);
end;
class procedure TLog.AddMsg(const AMsg: String);
begin
with Create(AMsg) do
try
Synchronize;
finally
Free;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Clear;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Host : String;
Port : Integer;
begin
Host := '127.0.0.1';
Port := StrToInt('1234');
client.Host := Host;
client.Port := Port;
try
client.Connect;
except
on E: Exception do
TLog.AddMsg('Error: ' + E.Message);
end;
end;
end;
procedure TForm1.clientConnected(Sender: TObject);
begin
TLog.AddMsg('DEBUG: TForm1.clientConnected');
rt := TReadingThread.Create(client);
end;
procedure TForm1.clientDisconnected(Sender: TObject);
begin
TLog.AddMsg('DEBUG: TForm1.clientDisconnected');
if rt <> nil then
begin
rt.Terminate;
rt.WaitFor;
FreeAndNil(rt);
end;
end;
end.
If that still does not work, then make sure the server is actually delimiting the PING string with a CRLF sequence, or at least a LF character (which is the minimum that ReadLn() looks for by default).

Delphi - Synchronize Thread with Frame

Good afternoon :-),
I have one Frame. This Frame I dynamically created by Main Form.
Main form:
Interface := TInterface.Create(self);
with handlingInterface do begin
Parent := Form1;
Left := 0; Top := 35;
Width := 570; Height := 250;
end;
In Frame I have a Thread. I call this Thread from Frame. Why I can synchronize Thread with Frame? There isn't any:
var
Form1: TForm1;
I call Thread inside Frame and I want to change Position of ProgressBar in Frame. I don't know, why I can in Synchronize method of Thread access the ProgressBar.
If would be Thread and ProgressBar in Form - Synchronize access is Form1.ProgressBar ...
But I have Thread and ProgressBar in Frame.
If the only thing you're trying to do is update the progress bar from the thread, there is a lighter weight option. I would consider using PostMessage instead. You don't want your thread to know too much about the details of the frame anyway.
When you create the thread, give it the handle of your frame so it knows where to post the message. Have the frame listen for the Windows message, which includes the progress position, and update the progress bar.
Here is a very simple example that increments the progress bar from 0 to 100 with a short sleep between each increment:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls;
const
WM_PROGRESS_MESSAGE = WM_USER + 99;
type
TProgressThread = class(TThread)
private
FWindowHandle: HWND;
protected
procedure Execute; override;
public
property WindowHandle: HWND read FWindowHandle write FWindowHandle;
end;
TFrame2 = class(TFrame)
ProgressBar1: TProgressBar;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure OnProgressMessage(var Msg: TMessage); message WM_PROGRESS_MESSAGE;
public
end;
implementation
{$R *.dfm}
{ TFrame2 }
procedure TFrame2.Button1Click(Sender: TObject);
var
lThread: TProgressThread;
begin
lThread := TProgressThread.Create(True);
lThread.FreeOnTerminate := True;
lThread.WindowHandle := Self.Handle;
lThread.Start;
end;
procedure TFrame2.OnProgressMessage(var Msg: TMessage);
begin
ProgressBar1.Position := Msg.WParam;
end;
{ TProgressThread }
procedure TProgressThread.Execute;
var
lProgressCount: Integer;
begin
inherited;
for lProgressCount := 0 to 100 do
begin
PostMessage(FWindowHandle, WM_PROGRESS_MESSAGE, lProgressCount, 0);
Sleep(15);
end;
end;
end.
You can give a reference to your progress bar to the thread.
Sample thread class.
unit Unit6;
interface
uses
Classes, ComCtrls;
type
TProgressBarThread = class(TThread)
private
{ Private declarations }
FProgressBar: TProgressBar;
procedure MoveProgress;
protected
procedure Execute; override;
public
procedure SetProgressBar(ProgressBar: TProgressBar);
end;
implementation
{ ProgressBarThread }
procedure TProgressBarThread.Execute;
begin
{ Place thread code here }
Synchronize(MoveProgress);
end;
procedure TProgressBarThread.MoveProgress;
begin
FProgressBar.StepIt;
end;
procedure TProgressBarThread.SetProgressBar(ProgressBar: TProgressBar);
begin
FProgressBar := ProgressBar;
end;
end.
Use like this
var
PBT: TProgressBarThread;
begin
PBT := TProgressBarThread.Create(True);
PBT.FreeOnTerminate := True;
PBT.SetProgressBar(ProgressBar1);
PBT.Start;
// PBT.Resume;
end;

Resources