Solved

make a form work in a thread

Posted on 2006-07-19
5
553 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 84 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 83 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 83 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

On Demand Webinar - Networking for the Cloud Era

This webinar discusses:
-Common barriers companies experience when moving to the cloud
-How SD-WAN changes the way we look at networks
-Best practices customers should employ moving forward with cloud migration
-What happens behind the scenes of SteelConnect’s one-click button

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Magic Software info 18 154
Need Help Delphi 2010 CheckBox1 Stored value in memo 13 101
Microsoft Access 97 and Delphi XE2 9 85
Connect to SQL 2008 r2 server over the Internet 4 84
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…

738 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