Solved

make a form work in a thread

Posted on 2006-07-19
5
541 Views
Last Modified: 2010-04-05
Hi, everyone

I need some Delphi codes to make a form work in a thread and the form also can communicate with main form.

Thanks
0
Comment
Question by:wangzheng
5 Comments
 
LVL 26

Accepted Solution

by:
Russell Libby earned 84 total points
Comment Utility

Delphi forms are not thread safe and should not be created by secondary threads. That is not to say they can't perform a body of work using a secondary thread. You should re-target your question in regards to the work that is to be done by the secondary form, and how threads can be used to perform that work (while keeping the Form UI responsive).

Regards,
Russell
0
 
LVL 17

Assisted Solution

by:TheRealLoki
TheRealLoki earned 83 total points
Comment Utility
If you are just using this form so you can put components on it, then try using a Datamodule instead of a form.
You can use that inside a thread
Although it's still a bit of an overkill, it's a quick solution if you do not want to change your code much
If you want buttons, labels, etc. then a thread is not the way to go

If you just wanted to show 2 forms at the same time, then do a Show and not a ShowModal
you can set the FormStyle to fsStayOnTop if you want it to appear over the other form
0
 

Assisted Solution

by:RikBarker
RikBarker earned 83 total points
Comment Utility
I think you're not asking the right question.  I'll try to explain why and then I'll try to answer it anyway.

A thread goes off and does stuff for you in the background.  Like ordering a meal in a restaurant, you don't want to sit completely still until the food arrives back, you want to chat and drink wine and maybe even scratch your nose.   You also don't want the waiter to run up to the table every 2 minutes and say "your meal is nearly ready!"

A thread works in the background, a form is foreground so making a form work in a thread is the wrong way round.  make a thread work for a form is better.

So, that aside, the main part of your problem is probably to do with showing progress for something I'm guessing - you're not very clear.  There are several ways you can get a thread to let a form know what it's doing.

Method 1.
  Set properties of your thread so you can reference them in a timer on the form.  This is nice and simple, but does have the problem that your form will only look every now and again, not necessarily at the moment something happens in your thread.

type
  TYourThread=class(TThread)
  private
    FCurrentAction: string;
  public
    property CurrentAction:string read FCurrentAction;  
  end;

Now in your thread, at regular points you can set FCurrentAction to state what's happening and your form can read it.

in the thread:  FCurrentAction:='Doing cool stuff';
in the main form, on a timer:
  Label1.Caption:=MyThread.CurrentAction;

That allows you to update the form and the thread never has to lock the main thread to do it.  The downside, as I mentioned, is that your form may not read the action when you want it to...

which leads to events and synchronisation.

Synchronisation is where you force your thread to act in the context of the main thread.
You do it by calling a method of your thread in the synchronize() function.

Because you're in the context of the main thread, it's ok to update the onscreen variables.  Because it's a method, it can't have any variables, so if you want to pass
any info back to the main form, you're doing it through properties again.

TYourThread=class(TThread)
private
  procedure DoUpdateProgress;
public
  procedure Execute; override;
  property CurrentAction:....
end;

procedure TYourThread.Execute;
begin
  FCurrentAction:='Starting to do something thready';
   Synchronise(DoUpdateProgress);
   ... Do some stuff here
   FCurrentAction:='Done doing funky stuff';
   Synchronize(DoUpdateProgress);
end;

procedure TYourThread.DoUpdateProgress;
begin
  YourForm.Label1.Caption:=FCurrentAction.
end;

That's how to do it, now here's why you shouldn't:  It's an awful solution to the problem.  
1) You're taking over the main thread to do it - might make your app unresponsive.
2) Your thread now references your main form - hardly Object oriented coding, definitely not portable.
3) there are better ways.

2 better ways (IMO) exist.  They both have their upsides and downsides.  The first is events.  Make an event in your thread that will fire in your form.  If you don't need to update the User interface, this can be great... be aware though that your code will be called in the context of the thread, so you must leave the UI alone.

TYourThread=class(TThread)
private
  FOnProgress: TNotifyEvent;
  procedure DoUpdateProgress;
public
  procedure Execute; override;
  property CurrentAction:....
  property OnProgress: TNotifyEvent read FOnProgress write FOnProgress.
end;

procedure TYourThread.Execute;
begin
  FCurrentAction:='Starting to do something thready';
  if Assigned(FOnProgress) then
    FOnProgress(Self);
   ... Do some stuff here
end;

Attach it in your form when you create the thread...
FMyThread:=TYourThread.Create(True);
FMyThread.OnProgress:=OnThreadProgressEvent;

and add a method to your form to handle it...
YourForm=class(TForm)
private
   FMyThreadHistory: TStringList;
   FMyThread: TYourThread;
   procedure OnThreadProgressEvent(Sender: TObject);
...

procedure TYourForm.OnThreadProgressEvent(Sender:TObject);
begin
  //Note to self:  I'm still in the context of the thread - don't touch the UI!
  MyThreadHistory.Add(DateTimeToStr(Now)+' '+FMyThread.CurrentAction);
end;

That's events.  Very useful if you don't need to update the UI, great for responding to things the moment they happen in your thread, but not great if you want onscreen notification when something happens.

For that, you need the final type of communication, windows messages.  The nice thing about a message is that it sends a message to the main form, so the form doesn't have to check regularly with a timer.  It happens in the message queue of the form's thread, so you can update any UI you want, and it's not blocking like Synchronize() is.

If your form passes its window handle to the thread when it creates it, you can use SendMessage and PostMessage to inform the form that something has just happened.

const
  WM_MYPROGRESS_MESSAGE=WM_USER +1;

TYourThread=class(TThread)
private
  FNotifyHandle: Hwnd;
  FOnProgress: TNotifyEvent;
  procedure DoUpdateProgress(const Msg: string);
public
  procedure Execute; override;
  property CurrentAction:....
end;

procedure TYourThread.Execute;
begin
   DoUpdateProgress('Beginning');
   ... Do some stuff here
  DoUpdateProgress('Something else');
  ... More stuff
  DoUpdateProgress('Done!');
end;

procedure TYourThread.DoUpdateProgress(const Msg: string);
begin
  FCurrentAction:=Msg;
  if FNotifyHandle <> 0 then
  begin  
    PostMessage(FNotifyHandle,WM_MYPROGRESS_MESSAGE,0,0);
    //You can also use SendMessage here - have a read and work out which is best for
    //you.
  end;
end;

in your form, you would usually catch this in your form's WndProc, or if the message is declared such that both the form and the thread can access it, you can do it like this;

TYourForm=class(TForm)
private
  procedure MyThreadProgressEvent(var Msg: TMessage); message WM_MYPROGRESS_MESSAGE;
end;

procedure TYourForm.MyThreadProgressEvent(var Msg: TMessage);
begin
  Label1.Caption:=FMyThread.CurrentAction;
  Showmessage('Hooray - I'm in the main thread from a form!');
end;

Hope that helps.
0

Featured Post

How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
This video discusses moving either the default database or any database to a new volume.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now