暂时禁用按钮

4
应该很简单,但是... :)
我的表格上只有一个按钮。这个按钮调用一个函数,需要执行10秒(例如)。我想在这段时间内暂时禁用按钮,这样如果用户在函数执行时按下Enter键,就不会发生任何事情。
但现在它的工作方式是:用户按下Enter键,按钮变为禁用状态。在这10秒内,用户再次按下Enter键,当第一个函数完成时,它被再次调用。我想防止这种情况发生,所以只有当按钮重新启用时,Enter键才能起作用。所有内容都在同一个线程中。
我的代码:
private void button1_Click(object sender, EventArgs e)
{
    Debug.WriteLine("Click");
    button1.Enabled = false;
    Thread.Sleep(3000); // simulate something...
    button1.Enabled = true;
    button1.Focus();
}

编辑:该函数将内容打印到打印机上。通过打印机的API函数,我可以确定何时完成打印,只有在此之后按钮才能重新启用。


你尝试过使用 this.AcceptButton=null,然后在完成后设置 this.AcceptButton=button1,除了切换 Enabled 之外吗? - V4Vendetta
@jfs:请查看我在问题中的编辑。 - sventevit
3个回答

4

我最近也遇到了同样的问题,我决定发表我的解释:

在执行漫长的任务时,用户多次点击按钮。Windows 将这些消息发送给应用程序,但是由于此时应用程序正在忙碌,它无法立即处理它们。所以这些消息在队列中等待应用程序变得可用。当应用程序完成任务后,按钮的 Enabled 属性会立即设置为 True,接下来发生的事情就是它接收到队列中的消息。此时按钮已启用,因此应用程序会像按钮从未被禁用一样处理新的点击。

修复建议:

您可以在 "button1.Enabled = true;" 行之前插入 "Application.DoEvents()" 一行。这样,在按钮仍处于禁用状态时,队列中的消息将到达按钮,而按钮现在会将它们丢弃。

免责声明:使用 Application.DoEvents() 看来并不是一个好习惯。然而,它很好地展示了这种特定情况下的情况。

我认为 Rhapsody 提出的解决方案比使用 Application.DoEvents() 更好。通过 BackgroundWorker,我们创建了一个第二个线程,因此不会阻塞 GUI 线程。由于 GUI 保持响应,禁用按钮将按预期工作。


这正是我遇到的问题,DoEvents确实解决了这个问题。我不擅长winforms,所以我不明白为什么Rhapsody建议的解决方法会起作用,因为问题不在于禁用和启用按钮,而在于消息被排队并在按钮启用后立即执行? - Shaikh Owais
@Shaikh Owais:实际上,我会说Application.DoEvents()是这里的解决方法。请看我的修改。 - Escape Velocity

1

如果您知道要执行的操作需要较长时间(例如> 5秒),最好异步执行该操作。

因此,在这种情况下,使用BackgroundWorker。在启动BackgroundWorker之前,您可以轻松禁用按钮,并在其完成事件中再次启用它。


非常好,谢谢。我想知道这是否可以在同一个线程中完成。 - sventevit

0

尝试这段代码。

AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

private button1_click(object o, EventArgs e)
{
    button1.Enabled = false;
    DoPrinterJob();
    button1.Enabled = _autoResetEvent.WaitOne();
}

private void DoPrinterJob()
{
    //Do something.
    _autoResetEvent.Set();
}

希望能有所帮助。


1
这样做是行不通的。GUI 仍然卡在 WaitOne() 调用中,无法处理事件。点击将积累,下次处理它们时,按钮的 enabled 属性将为 true,并且处理程序将执行。 - Denis Troller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接