Delphi:将StatusBar.OnDrawPanel事件分配给线程

时间:2015-09-25 03:59:11

标签: multithreading delphi

我正在尝试使用线程创建备份文件,并且将使用TProgressBarTLabel来指示流程的进度,该TStatusBarbackup_thread:= Tbackup_thread.Create(True); backup_thread.status_bar:= status_bar; backup_thread.status_bar_OnDrawPanel:= status_bar.OnDrawPanel; //is it correct? backup_thread.dir:= backup_dir; backup_thread.OnTerminate:= backup_thread_OnTerminate; backup_thread.Start; 将动态创建到其中一个{{1}面板。

Tbackup_thread = class(TThread)
  private
    Fstatus_bar: TStatusBar;
    Fprogress_bar: TProgressBar;
    Flabel_status: TLabel;
    Fdir: String;
    Fprogress_bar_position: Word;
    Flabel_status_caption: String;
    procedure do_update_progress_bar_position;
    procedure do_update_label_status_caption;
    procedure set_object_progress_bar(const progress_bar: TProgressBar);
    procedure execute_backup;
    procedure Get_status_bar_OnDrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect); //is it correct?
  protected
    procedure Execute; override;
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
    property status_bar: TStatusBar write Fstatus_bar;
    property status_bar_OnDrawPanel: TDrawPanelEvent read Get_status_bar_OnDrawPanel; //I get the error here...
    property dir: String write Fdir;
  end;

线程看起来像这样:

Get_status_bar_OnDrawPanel

procedure Tbackup_thread.Get_status_bar_OnDrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect); begin if Panel = Fstatus_bar.Panels[1] then begin with Fprogress_bar do begin Top := Rect.Top; Left := Rect.Left; Width := 60; Height := Rect.Bottom - Rect.Top; end; with Flabel_status do begin Top := Rect.Top; Left := Rect.Left + 105; Width := 150; Height := Rect.Bottom - Rect.Top; end; end; end; 看起来像这样:

OnDrawPanel

我的问题是如何从线程内部分配事件progress_bar。 如果我从线程内部动态创建label_status<?php $literalObjectDeclared = (object) array( 'foo' => (object) array( 'bar' => 'baz', 'pax' => 'vax' ), 'moo' => 'ui' ); print $literalObjectDeclared->foo->bar; // outputs "baz"! ?> 并使其在status_bar上可见,这样可以吗?我相信它会奏效......

1 个答案:

答案 0 :(得分:3)

  

我的问题是如何从线程内部分配事件OnDrawPanel。

你没有从线程中指定它,如果已经分配,​​你只需调用它。在绘图事件的情况下,你没有手动调用开始,你让操作系统发出主要UI线程的信号调用事件处理程序何时需要。

  

如果我从线程内部动态创建progress_bar和label_status并在status_bar上显示它们,这样可以吗?

只有在执行此操作时才与UI线程同步。它们是UI控件,毕竟它们只需要在主UI线程的上下文中创建和更新。

  

我相信它会奏效......

不是你表现出来的方式,不。

我会建议一个不同的设计。该帖子应该具有状态栏的无知识标签。它应该只是公开对其进度数据的访问,然后让主UI线程决定如何根据需要显示该数据。您可以为线程类创建新事件,只需确保在调用其处理程序时与主UI线程同步。

TThread.OnTerminate事件就是一个很好的例子。

尝试这样的事情:

type
  Tbackup_ProgressStatus_event = procedure(progress: Word; const status: String) of object;

  Tbackup_thread = class(TThread)
  private
    ...
    Fposition: Word;
    Fstatus: String;
    FOnProgressStatus: Tbackup_ProgressStatus_event;
    procedure update_progress_status(new_position: Word; const new_status: String);
    procedure do_update_progress_status;
    ...
  protected
    procedure Execute; override;
  public
    ...
    property OnProgressStatus: Tbackup_ProgressStatus_event read FOnProgressStatus write FOnProgressStatus;
  end;

procedure Tbackup_thread.Execute;
begin
  ...
  update_progress_status(..., ...);
  ...
end;

procedure Tbackup_thread.update_progress_status(new_position: Word; const new_status: String);
begin
  Fposition := new_position;
  Fstatus := new_status;
  if Assigned(FOnProgressStatus) then
    Synchronize(do_update_progress_status);
end;

procedure Tbackup_thread.do_update_progress_status;
begin
  if Assigned(FOnProgressStatus) then
    FOnProgressStatus(Fposition, Fstatus);
end;

backup_thread := Tbackup_thread.Create(True);
backup_thread.dir := backup_dir;
backup_thread.OnStatus := backup_thread_OnProgressStatus;
backup_thread.OnTerminate := backup_thread_OnTerminate;
backup_thread.Start;

...

procedure TMyForm.backup_thread_OnProgressStatus(progress: Word; const status: String);
begin
  // use progress and status as needed...
  progress_bar.Position := progress;
  label.Caption := status;
end;

procedure TMyForm.status_bar_OnDrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect);
begin
  // DRAW the specified TStatusPanel content within the specified TRect as needed...
  // DO NOT do anything else here!  Resizing controls DOES NOT belong in a
  // DRAWING event!  Do it before you even start the thread...
  //
  // Personally, I would not bother putting TProgressBar/TLabel controls
  // inside a TStatusPanel to begin with.  I would instead use THIS event
  // to DRAW a progress bar and text directly onto the Sender.Canvas
  // within the TRect using things like Sender.Canvas.FillRect() and
  // Sender.Canvas.TextRect()...
end;