【问题标题】:Button disable and enable按钮禁用和启用
【发布时间】:2012-02-17 00:50:10
【问题描述】:

我有一个基于 vb.net 的 Windows 应用程序,当单击“GO”按钮时,会将一堆数据加载到 DB 中。因此,在我的应用程序中,只要单击“GO”按钮,我就想禁用它,并希望在上传完成后重新启用它。 现在在我的 btnGo_Click() 的具体方法中,我有:

btnGo.Enabled = False

作为第一行和

btnGo.Enabled = True

在同一方法中作为最后一行。

但我不明白为什么“GO”虽然显示为被禁用,但在处理过程中仍然允许点击。此外,如果我删除最后一行,它会被永久禁用并且不允许点击事件。

请提出我做错了什么?

编辑(日期:2012 年 1 月 25 日):我按照同事的建议进行了更改,但我在这里遇到了一个新问题。我面临文本框更新但并非总是如此的问题。我在后台工作线程的“_ProgressChanged”事件中更新了我的文本框。就我而言,如果上传了 10 条记录。然后在 texbox 中预期有 10 行更新。但是文本框中只显示了几行。难道又是重绘问题?请建议...因为所有其他事情都按照您的建议完成

【问题讨论】:

    标签: vb.net winforms


    【解决方案1】:

    管理提交按钮的状态通常不是一个好主意。而是在提交时执行验证。

    【讨论】:

      【解决方案2】:

      UI 线程一有空闲时间就处理按钮单击事件。 禁用按钮后,UI 线程会被您的代码保持忙碌。在您的方法结束时,您重新启用该按钮,然后退出该方法并允许空闲时间。 因此,该按钮将在处理点击事件的时间点启用,因此您的点击被“识别”。

      正如其他人已经建议的那样,解决方案是使用 Backgroundworker。

      不要尝试使用 doEvents() 作为解决方案(永远不要这样做),因为这很容易引入其他微妙的问题。也就是说,您可以在代码中使用一些实验性的 doEvents 来证明上述解释。如果在重新启用按钮之前执行了 doEvents,您将看到单击被丢弃。另一方面,如果在单击之前执行 doEvents,直接在 button.disable 之后执行(以“更新 GUI”)将无济于事。

      【讨论】:

        【解决方案3】:

        我刚刚尝试禁用一个按钮,Updateing 表单,Sleeping,然后再次启用它。启用后,它仍然执行单击(在禁用按钮的情况下“休眠”时完成的单击)。

        我猜是“记住”点击的形式。

        (编辑:我是在 C# 中完成的。)

        【讨论】:

        • @downvoter 想解释一下为什么?我的回答是第一个真正回答这个问题的人。 (并且不提供错误信息,将其归咎于 UI 未更新。)
        • 这如何回答这个问题?我不明白您执行的“测试”是如何证明任何事情的,更不用说证明问题不是阻塞 UI 的结果。
        • 问题是他做错了什么按钮“虽然显示为被禁用仍然允许点击”。答案是(可能)——他没有做错任何事。这就是表格的工作方式。至于 UI,虽然这也是我的第一个想法——a) Update 未能纠正这种情况。 b) OP 写道:“显示为被禁用”。
        • 你认为Update 有什么作用?它需要消息泵,并且在事件处理程序方法中的代码运行时不会泵送消息。
        • Update 使按钮变为灰色。 (经过测试。)就是重点。 禁用按钮。 OP 对被阻止的 UI 没有任何问题。他的问题是按钮确实接收消息,而不是它不接收消息。
        【解决方案4】:

        如果您的 btnGo_Click() 在主线程中运行,则 UI 无法在耗时的任务中正确更新。
        您可以做您需要的最佳方式是在BackgroundWorker 中运行您的方法。

        【讨论】:

        • 我只是不明白为什么人们仍然建议Application.DoEvents。是的,它可能在这里工作。不,这不是一个好主意。不,对于不了解在其代码中使用线程、重入和 Windows 消息循环的人来说,这不是一件好事。它表面上的简单掩盖了它真正复杂和潜在困难的现实。在这里提出这个建议尤其有害,众所周知,人们倾向于将答案中的粘贴代码直接复制到他们的项目中,而不了解它的工作原理和原因,或者使用它的陷阱是什么。
        • @CodyGray:我很抱歉您的评论:我知道Application.DoEvents 不好,事实上我首先认为我建议使用Backgroundworker。我的例子只是告诉 OP 可以做到,即使它不好......我要编辑我的帖子......
        【解决方案5】:

        由于您尝试执行可能需要一些时间的函数,我建议您使用线程。在.NET 中有一个BackgroundWorker 组件,它非常适合异步执行任务。

        单击按钮时,像这样调用 BackgroundWorker:

        if not bgwWorker.IsBusy then
           btnGo.enabled = false
           bgwWorker.RunWorkerAsync()
        end if 
        

        并使用完成的事件再次启用按钮:

        Private Sub bgwWorker_DoWork(ByVal sender As System.Object, _
                         ByVal e As System.ComponentModel.DoWorkEventArgs) _
                         Handles bgwWorker.DoWork
        ' Do your things    
        End Sub
        
        Private Sub bgwWorker_RunWorkerCompleted(ByVal sender As System.Object, _
                                 ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
                                 Handles bgwWorker.RunWorkerCompleted
        ' Called when the BackgroundWorker is completed.
        btnGo.enabled = true
        End Sub
        

        在上面的示例中,我使用 bgwWorker 作为 BackgroundWorker 的实例。

        【讨论】:

        • 嗨狂想曲,好文章。我在这里有一个查询:我有一个正在更新的文本框,正在上传(数据库更新)。那么我应该将此特定代码移动到“backgroundWorker1_ProgressChanged”事件处理程序中吗?因为否则在“backgroundWorker1_DoWork”事件处理程序中它会抛出“跨线程使用...”的异常
        • 是的,将BackgroundWorker的WorkerReportsProgress属性设置为true,并结合ProgressChanged事件使用bgwWorker.ReportProgress来更新用户界面。
        • 我在这里遇到了一个问题,即文本框会更新但并非总是如此。就像我的情况一样,如果上传了 10 条记录。然后在 texbox 中有 10 行预期的更新。但文本框中只显示了几行。难道又是重绘问题?请建议... Bcos所有其他事情都按照您的建议完成
        • 我建议您在这里使用一些代码在 SO 创建一个新问题。像这样,“我们”更容易提供帮助:-)(或者你可以更新这个问题)
        • 我会再试一次,否则会发布一个单独的问题。无论如何感谢您的支持。这确实很有帮助。我也会在这里发布我的新链接! :)
        【解决方案6】:

        你没有做错任何事。问题在于,直到事件处理程序方法内部的代码完成执行后,UI 才会更新。然后,按钮被禁用并立即快速启动。

        这解释了为什么如果您忘记在事件处理程序方法结束时重新启用按钮控件,它仍然被禁用——因为您在方法的第一行告诉它禁用按钮.

        这是一个经典案例,说明您永远不应在事件处理程序方法中执行长时间运行的计算任务,因为它会阻止 UI 更新。计算实际上需要在单独的线程上进行。但是不要尝试手动创建线程,也绝对不要尝试从单独的线程更新您的 UI。相反,请使用BackgroundWorker component 自动为您处理所有这些。链接的 MSDN 文档有一个很好的示例来说明如何使用它。

        在启动BackgroundWorker 之前禁用该按钮,然后在其Completed 事件中重新启用它,表示您的数据库加载完成。

        【讨论】:

        • 我同意。使用 Backgroundworker 是这里的方法。它将保持 UI 响应。
        • 嗨,科迪,好文章。我在这里有一个查询:我有一个正在更新的文本框,正在上传(数据库更新)。那么我应该将此特定代码移动到“backgroundWorker1_ProgressChanged”事件处理程序中吗?因为否则在“backgroundWorker1_DoWork”事件处理程序中它会抛出“跨线程使用...”的异常
        • @Justin:是的,如果您想响应进度更新,则该代码需要进入ProgressChanged 事件处理程序。 DoWork 事件处理程序方法是您在后台线程上执行应该发生的事情的地方。您必须记住的是,您只能从单个线程(主要线程,指定 UI 线程)与您的 UI 交互。其他一切都不能碰它,否则你会得到一个关于跨线程的异常。 BackgroundWorker 使这种分离变得容易,因为您可以将代码放在事件处理程序中,让它担心在正确的线程中引发它。
        • @Cody - 我在这里遇到了一个问题,即文本框会更新但并非总是如此。就像我的情况一样,如果上传了 10 条记录。然后在 texbox 中预期有 10 行更新。但文本框中只显示了几行。难道又是重绘问题?请建议... Bcos 所有其他事情都按照您的建议和支持 URL 完成。
        • @Justin:不,这听起来不像是绘画问题。一切完成后,任何绘画问题都会得到纠正,您将能够看到所有 10 条记录。如果这没有发生,那么您用来将它们添加到文本框中的代码有问题。也许是循环中的一个错误?尝试使用纯数字而不是记录来调试它,这样您就可以看到将数字添加到文本框中的顺序。如果失败,请使用您正在使用的实际代码更新您的问题。
        猜你喜欢
        • 1970-01-01
        • 2015-04-19
        • 1970-01-01
        • 1970-01-01
        • 2022-12-18
        • 2013-04-29
        • 2012-09-20
        • 2015-08-11
        相关资源
        最近更新 更多