我有一个程序,当单击一个按钮时,它会同步播放三个.wav文件。问题是即使我在点击处理程序的第一条指令上禁用了按钮,其他鼠标点击事件也会排队,然后在播放完成时执行,如果用户在播放声音时点击。我怎么能避免这个?
这是我的点击处理程序,playSnippet_P1,2,3以不同的顺序播放三个音频文件:
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);
}
答案 0 :(得分:1)
这是Windows的一个特意功能。允许您输入繁忙的窗口也是一样的 - 一旦应用程序恢复,您的文本就会出现。
为什么不禁用按钮并异步播放声音?
答案 1 :(得分:1)
我假设你的描述中playnippet()阻塞直到片段完成播放,所以这应该是一个单独的线程(你可以使用BackgroundWorker类)。这样可以避免GUI被卡住,也可以解决按钮点击问题。
当BackgroundWorker完成后,您可以重新启用该按钮,但请确保在GUI线程上执行此操作(使用Control.Invoke或Control.BeginInvoke)
祝你好运。答案 2 :(得分:1)
这种行为更简单:
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
System.Threading.Thread.Sleep(1000);
button1.Enabled = true;
}
问题是在UI线程忙时记录的鼠标单击进入消息队列并停留在那里直到事件处理程序完成。发生这种情况时,该按钮已再次启用,允许Click事件再次运行。
解决这个问题非常困难,包括支付在工作线程上运行代码的代价。一个可能的好解决方法是在重新启用按钮之前清除消息队列并删除所有鼠标消息,但这在Winforms中并不容易。代码实际上在那里,但它是内部的。一个真正务实的解决方案可能会让我遇到麻烦,但是安全有效:
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
System.Threading.Thread.Sleep(1000);
Application.DoEvents();
if (!button1.IsDisposed) button1.Enabled = true;
}
答案 3 :(得分:0)
我想出的解决方案如下:
在鼠标单击处理程序中,禁用该按钮,但在播放完成时不重新启用它。而是以足够的间隔启动Forms.Timer,以允许清除在禁用按钮上到达的消息。在第一个刻度线上,重新启用按钮并停止计时器。
这会阻止其他点击事件执行任何操作,因为该按钮仍会被禁用。当然,这个解决方案对我的情况非常具体,我理解通常解决方案是在单独的线程上播放音频。
感谢您的所有建议!!
编辑:Hans Passant发布了一个更明智的解决方案 - 请参阅他在启用控件之前清除消息队列的建议。