Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

make a form work in a thread

Posted on 2006-07-19
5
Medium Priority
?
555 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
5 Comments
 
LVL 26

Accepted Solution

by:
Russell Libby earned 336 total points
ID: 17141924

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 332 total points
ID: 17142170
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 332 total points
ID: 17142517
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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

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…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In this video, Percona Solutions Engineer Barrett Chambers discusses some of the basic syntax differences between MySQL and MongoDB. To learn more check out our webinar on MongoDB administration for MySQL DBA: https://www.percona.com/resources/we…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Suggested Courses

722 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