Delphi: refer to control from thread in frame
There is a FRAME (not a form) and a thread. How 开发者_StackOverflow社区to refer to Frame's control from the thread? For example I want to disable a button from a thread. But I don't have a pointer to the button, no global variables in the frame.
Thanks!
You should not in fact, call any method or modify any property of an VCL control at all, or anything visible to the user (the User interface of your application, which means VCL controls normally in Delphi, whether in a frame or not) directly from a background thread.
You can however send an event or notification to the main thread, either using PostMessage
, or TThread.Synchronize
or TThread.Queue
.
Rather than having a reference to the frame or the control in your thread object, it might be better to just pass the handle of the form that contains your frame or other controls, to the thread, and use a user-message (WM_USER+10001) like this.
I prefer PostMessage to TTHread.Synchronize or Queue, because it's really simple and it works great. It's not exactly a cross-platform-friendly technique since it's tied to the Win32 API.
You should call synchronize like this:
TMyThread = class(TThread)
private
FFrame: TFrame;
...
public
constructor Create(AFrame: TFrame);
...
end;
constructor TMyThread.Create(AFrame: TFrame);
begin
FFrame := AFrame;
inherited Create;
end;
// do not call directly, only using Synchronize
procedure TMyThread.AMethodWithNoParameters;
begin
FFrame.Button1.Enabled := not FBusy;
end;
procedure TMyThread.DoWork; // called from Execute.
begin
FBusy := true;
Synchronize(AMethodWithNoParameters);
Sleep(100); //dummy;
FBusy := false;
Synchronize(AMethodWithNoParameters);
end;
As quite rightly pointed out, you cannot call any members of any visual component in a background thread.
To disable the button from inside the thread code you have to have a reference to the button OR a reference to an event which you can assign the thread object - you can then fire the thread inside the queued or synchronized procedure, like so :-
type
test=class(tthread)
ondisablebutton:tnotifyevent;
{...}
then, when in the procedure which you encapsulate with tthread.synchronize you can call the event, not forgetting to test if it is assigned....
procedure test.synchronisedprocedure;
begin
if assigned(ondisablebutton) then
ondisablebuttone(self);
end;
When you create the thread object you have designed, you then have to assign the ondisablebutton to a procedure of the form containing the button which looks like thus :-
procedure form1.threadwantstodisablebutton(sender:tobject);
begin
button1.enabled:=false;
end;
your thread creation then needs an extra line :-
mythread:=test.create;
test.ondisablebutton:=form1.threadwantstodisablebutton;
like so, obviously you have to have access to form1 (or the form containing the button) where you are defining and creating your thread, which is not necessarily good design but it works.
精彩评论