I am a novice programmer, so apologies if this sounds very basic to you all. I have a procedure which looks (essentially) like this:
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;
The problem is that the caption on panel1
doesn't change until the mediaplayer has played the sound file; I need the caption to change, and the player to start playing, simultaneously. How can I ensure this?
I thought that the procedure would execute each line of code sequentially, meaning that the caption of panel1
changes, followed by the mediaplayer springing into action. Where have I gone wrong?
VCL components (like TPanel
) usually have an internal method called Invalidate()
that is called when a property (like Caption
) changes and that change requires repainting part of the control (eg. to draw the new caption text).
This method only sets a flag inside the window control, but does not invoke the repaint method itself. The reason for that is to avoid calling the Repaint()
method multiple times, if many properties are changed at once (sequentially, in short time).
The Repaint()
method is actually called when the component receives a message to repaint via the main message loop (processed from the main thread of the application - the GUI thread).
The way you start playing the media player is blocking, because you set the Wait
property to True
, which makes the player block the calling thread (again the main thread) until the file has been played.
This does not give a chance to the main thread to process it's message queue and initiate the repaint.
A quick fix to the problem is either the one suggested by becsystems, or this one:
panel1.Caption := 'This is a sentence';
Application.ProcessMessages();
Calling ProcessMessages()
will give the main thread the opportunity to process the message queue and perform the update, just before starting to play the file.
This is a quick fix, as the main thread will still be blocked after starting to play, which will prevent other portions of the window to repaint (eg. Try moving the window around or minimizing and maximizing it while playing).
The code suggested by becsystems is similar, but instead of processing the message queue, just forces the control to repaint.
To properly fix the problem you should not use the Wait
property and instead handle the OnNotify
event of the media player.
Here is an example, adapted from Swiss Delphi Center (not tested as I do not have Delphi installed at the moment):
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;
There is a short explanation of the VCL message loop (part of Delphi Developer's Guide) published here:
Anatomy of a Message System: VCL
Also, not related to the problem, but take a look at Delphi Coding Style Guide. It's just nice when code posted is formatted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With