我是一名新手程序员,如果这对你们所有人来说听起来非常基本,那么道歉。 我有一个看起来(基本上)像这样的程序:
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
的标题会发生变化,然后媒体播放器就会生效。我哪里出错?
答案 0 :(得分:10)
VCL组件(如TPanel
)通常有一个名为Invalidate()
的内部方法,当属性(如Caption
)发生变化时调用该方法,并且该更改需要重新绘制部分控件(例如。绘制新标题文本)。
此方法仅在窗口控件内设置一个标志,但不调用重绘方法本身。这样做的原因是,如果一次更改许多属性(在短时间内顺序更改),则应避免多次调用Repaint()
方法。
当组件通过主消息循环(从应用程序的主线程 - GUI线程处理)接收要重新绘制的消息时,实际调用Repaint()
方法。
您开始播放媒体播放器的方式是阻止,因为您将Wait
属性设置为True
,这会使播放器阻止调用线程(再次是主线程),直到文件被删除为止播放。
这不会让主线程处理它的消息队列并启动重绘。
问题的快速解决方案是becsystems建议的那个,或者这个:
panel1.Caption := 'This is a sentence';
Application.ProcessMessages();
在开始播放文件之前,调用ProcessMessages()
将使主线程有机会处理消息队列并执行更新。
这是一个快速修复,因为主线程在开始播放后仍然会被阻止,这将阻止窗口的其他部分重新绘制(例如,尝试移动窗口或在播放时最小化并最大化它)。< / p>
becsystems建议的代码类似,但不是处理消息队列,只是强制控件重绘。
要正确解决问题,您不应使用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 Developer&Guide?的一部分)有一个简短的解释:
此外,与问题无关,但请查看Delphi Coding Style Guide。发布代码格式化后,这很不错。
答案 1 :(得分:2)
设置标题后添加Refresh
来电,即:
panel1.caption:='This is a sentence';
Refresh;