2012-01-23 149 views
9

我有一个基于vb.net的Windows应用程序,当点击“GO”按钮时,一堆数据被加载到数据库中。所以在我的应用程序中,只要点击“GO”按钮,我想只是禁用它,并希望在上传完成后启用它。 现在,在我的btnGo_Click()具体方法我:按钮禁用和启用

btnGo.Enabled = False 

作为一线和

btnGo.Enabled = True 

在相同的方法,最后一行。

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

请提出我做错了什么?

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

回答

14

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

这就解释了为什么,如果你在事件处理程序方法的结尾忘记重新启用按钮控制,它仍然是禁用的,因为你告诉它在方法的第一行以禁用按钮。

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

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

+0

我同意。使用Backgroundworker是去这里的路。它会保持用户界面的响应。 – BenR

+0

嗨科迪,尼斯的文章。我在这里有一个查询:我有一个文本框得到更新,上传(数据库更新)正在发生。那么我应该将这个特定的代码移动到“backgroundWorker1_ProgressChanged”事件处理程序中吗?因为否则在“backgroundWorker1_DoWork”事件处理程序中,它会引发我“Cross Thread usage ...”的异常 –

+0

@Justin:是的,如果您要响应进度更新,则该代码需要进入ProgressChanged事件处理程序。 'DoWork'事件处理程序方法就是你在后台线程上做什么的地方。你必须记住的是,你只能从一个线程(主要的,被指定为UI线程)与你的UI进行交互。其他一切都无法触及它,否则你会得到一个关于交叉线程的例外。 'BackgroundWorker'使这种分离变得容易,因为你可以将代码放入事件处理程序中,让它担心在正确的线程中引发它。 –

0

如果您的btnGo_Click()在主线程内运行,那么在耗时的任务中无法正确更新用户界面。
您可以通过BackgroundWorker运行您的方法来执行所需的最佳方法。

+0

我只是不明白为什么人们仍然建议'Application.DoEvents'。是的,它可能在这里工作。不,这不是一个好主意。不,对于不了解线程,重入和Windows消息循环的人来说,在代码中使用它并不是一件好事。其明显的简单性掩盖了其真正复杂和潜在困难的现实。在这里推荐它特别有害,人们有一个众所周知的倾向,将直接在答案中找到的粘贴代码直接复制到他们的项目中,而不理解它是如何工作的,为什么会起作用,或者使用它的陷阱是什么。 –

+0

@CodyGray:对于你的评论我感到抱歉:我知道'Application.DoEvents'不好,事实上我首先想到的是我使用Backgroundworker。我的例子只是告诉OP可以完成,即使它不好...我要编辑我的文章... – Marco

3

由于您试图执行一个可能需要一些时间的函数,因此我建议您使用线程。在.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的实例。

+0

嗨狂想曲,好文章。我在这里有一个查询:我有一个文本框得到更新,上传(数据库更新)正在发生。那么我应该将这个特定的代码移动到“backgroundWorker1_ProgressChanged”事件处理程序中吗?因为在“backgroundWorker1_DoWork”事件处理程序中,它会引发我“Cross Thread usage ...”的异常 –

+0

是的,将BackgroundWorker的WorkerReportsProgress属性设置为true,并将bgwWorker.ReportProgress与ProgressChanged事件组合使用以更新用户界面。 – Rhapsody

+0

我在这里面临一个问题,即文本框被更新,但并非总是如此。就像我的情况,如果有10个记录上传。然后在texbox中有10行更新。但文本框中只显示几行。它再次是重绘问题吗?请建议... Bcos所有其他事情都按照您的建议完成 –

0

我只是试图禁用按钮,Update ing表格,Sleep ing,并重新启用它。它仍然执行了点击操作(点击是在“禁用”按钮时“睡眠”时完成的)。

我想形式“记住”点击。

(编辑:我在C#这样做)

+0

@downvoter谨慎解释为什么?我的回答是第一个真正回答问题的人。 (并且不会在用户界面上提供错误的信息,并且没有更新。) – ispiro

+0

这是如何回答这个问题的?我不明白你所做的“测试”如何证明什么,更不用说证明问题不是阻塞UI的结果。 –

+0

问题是他做错了什么,按钮“虽然显示为禁用仍允许点击”。答案是(可能) - 他没有做错任何事情。这就是表单如何工作。至于用户界面,尽管这也是我的第一个想法 - a)“更新”未能纠正这种情况。 b)OP写道:“显示为禁用”。 – ispiro

0

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

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

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

0

管理提交按钮的状态通常不是一个好主意。相反,对提交进行验证。