简体   繁体   English

Delphi:从框架中引用线程控制

[英]Delphi: refer to control from thread in frame

There is a FRAME (not a form) and a thread. FRAME (不是表格)和线程。 How to refer to Frame's control from the thread? 如何从线程中引用Frame的控件? 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. 事实上,您根本不应该调用任何方法或修改VCL控件的任何属性,或者用户可见的任何内容(应用程序的用户界面,这意味着VCL通常在Delphi中控制,无论是否在框架中)直接从后台线程。

You can however send an event or notification to the main thread, either using PostMessage , or TThread.Synchronize or TThread.Queue . 但是,您可以使用PostMessageTThread.SynchronizeTThread.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. 最好不要将包含框架或其他控件的窗体的句柄传递给线程,并使用用户消息(WM_USER + 10001),而不是在线程对象中引用框架或控件。 )像这样。

I prefer PostMessage to TTHread.Synchronize or Queue, because it's really simple and it works great. 我更喜欢PostMessage到TTHread.Synchronize或Queue,因为它非常简单并且效果很好。 It's not exactly a cross-platform-friendly technique since it's tied to the Win32 API. 它不是一个跨平台友好的技术,因为它与Win32 API相关联。

You should call synchronize like this: 你应该像这样调用synchronize:

  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.... 然后,当您使用tthread.synchronize封装的过程中,您可以调用该事件,而不是忘记测试它是否已分配....

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 :- 当你创建你设计的线程对象时,你必须将ondisablebutton分配给包含按钮的表单的过程,如下所示:

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. 像这样,显然你必须访问form1(或包含按钮的表单),你在那里定义和创建你的线程,这不一定是好的设计,但它的工作原理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM