I have a program which plays three .wav files synchronously when a button is clicked. The problem is that even if I disable the button on the first instruction of the click handler, additional mouse click events are queued and then executed when playback completes if the use clicks while sound is playing. How can I avoid this?
Here’s my click handler, playSnippet_P1,2,3 play the three audio files in different orders:
void btnPlay_Click(object sender, EventArgs e)
{
btnPlay.Enabled = false;
this.Refresh();
//play a snippet for the current passage
switch (myProgram.condition)
{
case 1:
playSnippet_P1();
break;
case 2:
playSnippet_P2();
break;
case 3:
playSnippet_P3();
break;
default:
if (myProgram.debug)
{
MessageBox.Show("Error(frmPassage): Invalid condition set: " + myProgram.condition);
}
break;
}
//leave this phase once final passage is finished
if (snipsPlayed >= (myProgram.snipCount_1
+ myProgram.snipCount_2
+ myProgram.snipCount_3))
{
myProgram.phaseController.runNextPhase();
}
//reset the form to show text for next passage
if (snipsPlayed >= (getSnipCount(1) + getSnipCount(2)))
currentPassage = 2;
else if (snipsPlayed >= getSnipCount(1))
currentPassage = 1;
else
currentPassage = 0;
lblTitle.Text = "Passage " + randPOrder[currentPassage].ToString();
btnPlay.Enabled = true;
}
private void playSnippet_P1()
{
snipsPlayed++;
int cSnipCount = getCSnipCount();
int snipNum = (snipsPlayed % cSnipCount);
int subNum = getSubNum(snipsPlayed);
if (snipNum == 0)
snipNum = cSnipCount;
int rPassage = randPOrder[currentPassage];
//play "Next question is for..."
JE_SP.playSound(Application.StartupPath
+ "\\res\\audio\\next\\Next" + subNum.ToString()
+ ".wav", true);
//play snippet
JE_SP.playSound(Application.StartupPath
+ "\\res\\audio\\passages\\Snip" + rPassage.ToString()
+ "-" + snipNum.ToString()
+ ".wav", true);
//play question
JE_SP.playSound(Application.StartupPath
+ "\\res\\audio\\passages\\Q" + rPassage.ToString()
+ "-" + snipNum.ToString()
+ ".wav", true);
string[] writeMe =
{
rPassage.ToString(),
"\\res\\audio\\passages\\Snip" + rPassage.ToString()
+ "-" + snipNum.ToString(),
"\\res\\audio\\passages\\Q" + rPassage.ToString()
+ "-" + snipNum.ToString(),
subNum.ToString(),
myProgram.condition.ToString()
};
JE_Log.logData(writeMe, "\t", myProgram.groupFile);
}
A simpler repro of this behavior:
The problem is that the mouse clicks that are recorded while the UI thread is busy go into the message queue and are stuck there until the event handler completes. When that happens, the button is already enabled again, allowed the Click event to run again.
Fixing this is fugly and includes paying the price of running the code on a worker thread. A possible good fix is to purge the message queue and remove all the mouse messages before re-enabling the button but that’s not easy in Winforms. The code is actually there but it is internal. A really pragmatic fix is one that might get me in trouble but is safe and effective :