为什么 tmediaplayer 会延迟 tpanel 上的字幕更改?

why does tmediaplayer delay a caption changing on a tpanel?

我是一名新手程序员,如果这对你们来说听起来很基础,我深表歉意。 我有一个看起来(基本上)像这样的程序:

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  panel1.caption:='This is a sentence';
  with MediaPlayer1 do
  begin
    filename:='f:\untitled.wma';
    open;
    wait:=true;
    play;
    close;
  end;
end;

问题是 panel1 上的标题在媒体播放器播放完声音文件后才会改变;我需要更改标题,同时播放器开始播放。我怎样才能确保这一点?

我认为该程序会按顺序执行每一行代码,这意味着 panel1 的标题发生变化,然后媒体播放器开始运行。我哪里做错了?

设置标题后添加Refresh调用,即:

panel1.caption:='This is a sentence'; 
Refresh;

解释:

VCL 组件(如 TPanel)通常有一个称为 Invalidate() 的内部方法,当 属性(如 Caption)发生变化并且该变化需要重新绘制时调用该方法控件的一部分(例如绘制新的标题文本)。

此方法仅在 window 控件内设置一个标志,但不会调用重绘方法本身。这样做的原因是为了避免多次调用 Repaint() 方法,如果同时更改许多属性(顺序地,在短时间内)。

当组件通过主消息循环(从应用程序的主线程 - GUI 线程处理)接收到要重绘的消息时,实际上会调用 Repaint() 方法。

您开始播放媒体播放器的方式是阻塞的,因为您将 Wait 属性 设置为 True,这会使播放器阻塞调用线程(同样是主线程) 直到文件播放完毕。

这不会让主线程有机会处理它的消息 queue 并启动重绘。

快速修复:

问题的快速解决方法是 becsystems 建议的方法,或者这个方法:

panel1.Caption := 'This is a sentence';
Application.ProcessMessages();

调用 ProcessMessages() 将使主线程有机会处理消息 queue 并在开始播放文件之前执行更新。

这是一个快速修复,因为主线程在开始播放后仍然会被阻塞,这将阻止 window 的其他部分重新绘制(例如,尝试移动 window或在播放时将其最小化和最大化)。

becsystems 建议的代码类似,但不是处理消息 queue,而是强制控件重新绘制。

正确修复:

要正确解决此问题,您不应使用 Wait 属性 而是处理媒体播放器的 OnNotify 事件。

这是一个例子,改编自Swiss Delphi Center未测试,因为我目前没有安装Delphi):

procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  panel1.Caption := 'This is a sentence';
  with MediaPlayer1 do
  begin
    Notify := True;
    OnNotify := NotifyProc;
    Filename := 'f:\untitled.wma';
    Open;
    Play;
  end;
end;

procedure TForm1.NotifyProc(Sender: TObject);
begin
  with Sender as TMediaPlayer do 
  begin
    case Mode of
      mpStopped: {do something here};
    end;

    // Set to true to enable next-time notification
    Notify := True;
  end;
end;

旁注:

此处发布了 VCL 消息循环的简短说明(Delphi 开发人员指南的一部分):

Anatomy of a Message System: VCL

另外,与问题无关,但看一下Delphi Coding Style Guide。格式化发布的代码真是太好了。