【问题标题】:TThread Creation of TButton Parented By TPanel由 TPanel 为父的 TButton 的 TThread 创建
【发布时间】:2011-07-05 15:06:38
【问题描述】:

我有一个关于在主线程 TPanel 上单独创建主线程 TButton 的问题。 TPanel 必须设置为 TButton 的父级。

ButtonVariableName := TButton.Create (
    (Form1.FindComponent('PanelNameString') as TComponent)
   );

ButtonVariableName.Parent := (
    (Form1.FindComponent('PanelNameString') as TWinControl)
  );

不工作...

ButtonVariableName 在主线程上。 TButton.Create() 在单独的 TThread 上被调用。 ButtonVariableName.Parent 也从单独的TThread 调用。

FindComponent 似乎正在崩溃。当我删除它并在那里放置其他东西时它可以工作。从单独的TThread 调用时,FindComponent 可能不起作用,但我不确定。

任何指针^? 哈哈。

-i2programmer

【问题讨论】:

  • Windows 与创建它们的线程有关联。在实践中,这使得构建具有多个线程的 UI 几乎是不可能的。这也是没有意义的。将 UI 放在主线程中,并在线程外运行长时间运行的非可视任务。
  • 放弃 - 甚至尝试将线程与 UI 控件一起使用是绝对没有意义的。

标签: multithreading delphi button panel lazarus


【解决方案1】:

您不能从辅助线程使用 VCL。在辅助线程中使用 Synchronize 或 Queue 可以在主线程的上下文中执行 VCL 相关代码。

【讨论】:

  • 这就是我所说的:它确实包括 Synchronize(); procedure TMyThread.forButton; begin Buttons[ItemCounter] := TButton.Create((Form1.FindComponent(tsl[0 + 3]) as TComponent)); Synchronize(@forButton); 但它仍然不起作用。
  • @i2programmer:在 StackOverflow 上,您可以编辑您的问题。将评论中的代码包含在问题中并正确格式化会更有帮助(使用 {} 按钮,或者简单地将自己缩进四个空格)。
【解决方案2】:

这应该是一个注释,但我想包含一些代码。首先,您不应该从辅助线程调用任何 VCL,因此不能保证调用 FindComponent 可以正常工作。尽管如此,我还是怀疑这是你的问题,因为除非你特别幸运,否则你不会得到竞争条件,所以你不会得到错误。

你应该做两件事:

  • 将您的代码放在表单上的一个简单按钮下,对其进行测试,当您知道代码良好时,将其移至您的后台线程。
  • 稍微分解一下您的代码,这样您就可以看到哪里出了问题。无需猜测 FindComponent 是否是失败的那个,因为它很容易测试和确定。

像这样分解你的代码:

var ParentPanel: TWinControl;
    anControl: TControl;
begin
  Assert(Assigned(Form1)); // Assertions are free, you might as well test everything
  anControl := Form1.FindComponent('YourNameHere'); // casting straight to TWinControl raises an error if the returned control is nil
  Assert(Assigned(anControl)); // Make sure we got something
  ParentPanel := anControl as TWinControl; // raises error if the control is not TWinControl
  ButtonVariableName := TButton.Create(ParentPanel);
  ButtonVariableName.Parent := ParentPanel;
end;

【讨论】:

    【解决方案3】:
    type
      TMyThread = class( TThread )
      private
        FOwner : TComponent;
        procedure DoCreateButton;
      public
        constructor Create(AOwner: TComponent);
        procedure Execute; override;
      end;
    
    .....
    
    { TMyThread }
    
    constructor TMyThread.Create(AOwner: TComponent);
    begin
      inherited Create(True);
      FreeOnTerminate := True;
    
      FOwner := AOwner;
      Resume;
    end;
    
    procedure TMyThread.DoCreateButton;
    begin
      with TButton.Create(FOwner) do
      begin
        //Set the button Position 
        Left := 5;
        Top := 5;
    
        Parent := FOwner as TWinControl;
      end;
    end;
    
    procedure TMyThread.Execute;
    begin
      Synchronize(DoCreateButton);
    end;
    
    
    { Form1 }
    
    procedure TForm1.btnExecClick(Sender: TObject);
    begin
      TMyThread.Create(Panel1);
    end;
    

    【讨论】:

    • +1 用于看起来可以满足 OP 要求的代码。但问题仍然存在:为什么? IMO,请遵循给出的建议,并避免在线程中这样做。
    • -1 这毫无意义。创建一个线程,以便您可以序列化回主线程。为什么?!
    • 也许是因为 TMyThread.Execute 会做其他事情而不仅仅是创建一个按钮......话虽如此,我认为 OP 试图实现的是根据内部发生的情况更新 UI线程(可能在线程运行超过 X 秒后添加一个取消按钮......)。这样做的正确方法可能是向主线程添加一个回调并让它处理按钮创建。但这只是猜测。
    • 代码只是一个使用TThread.Synchronize的演示,当然如果你从实际的角度来看它是没有意义的。但是就像 Ken 所说的,这并不意味着线程只执行按钮创建,它可以做其他事情而不仅仅是创建按钮。我只是在回答 OP 的问题,仅此而已。
    • 顺便说一句,我同意使用方法回调到主线程是比使用同步更直接的解决方案:)
    猜你喜欢
    • 2012-01-15
    • 2011-05-09
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 2012-05-15
    • 2014-03-25
    • 2014-11-22
    相关资源
    最近更新 更多