正确的进度通知
这是要以极大的怀疑满足,但我会说,无论如何??只有一个最好的方式展现了长时间操作的进度窗口。答案是一个模式进度窗口,同时在UI线程上的工作是做的工作线程上显示。
首先,让我们来看看为什么进度窗口必须在UI线程上:如果您尝试一个工作线程上显示一个对话框,该对话框不能从调用线程的所有者。尝试,你会得到异常:Cross-thread operation not valid: Control 'Form1' accessed from a thread
other than the thread it was created on.
和显示在另一个线程中的对话框没有所有者(模态或非)出现对话框非模态,因此允许用户点击你的主窗口和宽松的进度窗口的Z -顺序决策的深处为用户很难知道如果操作仍在进行中或没有。
一招,你可能会尝试设置进度窗口的TopMost属性,迫使它在上面。不幸的是,这使所有其他窗口的顶部,而不是理想的用户尝试多任务,而他们等待您的应用程序做它的事。
接下来,让我们来看看为什么应该在一个单独的线程完成所有工作。如果你不和的应用程序不调用的DoEvents,应用程序将很快成为无响应尽可能Windows的关注,用户将看到在标题栏的消息"没有响应??如果用户尝试一下就可以了。而我们都知道,用户觉得有权告诉你,你的应用程序"冻结??或"挂起??当他们看到这。
那么为什么不利用的DoEvents,让您的应用程序回应?你不仅需要调用频繁的DoEvents给响应的应用程序的外观,但的DoEvents是邪恶的。在您的操作调用的DoEvents使你的代码要少得多可重复使用的。假设你有一个可爱的小程序,是能够排序号码。在这种情况下,您可以锁定用户界面和显示一个进度条,这样的DoEvents似乎完成预期的效果。但想象有人在向导的步骤之间重用您的排序例程。用户点击下一步,代码运行和调用的DoEvents,用户无论出于何种原因,决定再次单击"下一步",程序已完成之前。也许,用户只需双击,因为他们很单击高兴。按钮的Click事件将再次触发,并可能陷入各种麻烦您,因为您的代码将运行两次。应该预计可以重复使用而不用担心再入同步的代码。如果我们所有的承诺,将有较少的错误,在我们的应用程序和我们的代码会更容易重用。这种在我看来细节,根本就不是例行的消费者应该需要担心的东西。
最后,进度对话框模式或无模式?您可能认为这并不重要。假设你的代码,需要运行工作线程完成后,不要紧,如果代码是在一些"线程运行完成??的事件处理程序或后,立即关闭进度窗口呢?在许多情况下,我承认这一点并不重要。但我断言,这是很好的做法,后来做了。这是因为你的代码始终处于某种方式被称为一个事件处理程序。无论是一个按钮单击事件或提高小组主要的例外事件,一些对象,你总是运行在某种事件处理程序。如果你生成辅助线程并立即返回,该事件的调用者可能会遇到一些额外的代码。现在,因为你显示一个进度条,你是表明,没有完成此操作的用户。那么,为什么你会从事件中返回,如果操作没有完成?如果有该事件的多个观察员。有什么能阻止第二个事件处理程序添加到同一事件的另一个模块。而如果你立即返回,代码将运行之前,你长的操作已经完成。这可能不是一个问题,但它可能。它可能会认为,没有一个模式对话框显示的情况下,它需要展现自己,或它可能是依赖于你的代码之前完成它的东西。
此外,事件可能做自己的事情,事后假设你操作。例如,假设一个事件BeforeOpen和另一AfterOpen。如果您在BeforeOpen长时间操作并立即返回,AfterOpen火之前就大功告成了。消费者对这一事件可能假设你不管它是什么,你正在做成品。最后:如果事件呼叫者诱捕例外。等待你的线程来完成也可以让你的陷阱你的线程和泡沫备份事件的发行人,或甚至可能适当EventArgs.Cancel标志的例外。到目前为止,所有这些原因,我断言,这是很好的做法显示为模态进度对话框,除非你有令人信服的理由不。
因此,如何做,我们做了这一切?虽然有不止一种方法,在我看来最容易使用的System.ComponentModel.BackgroundWorker对象。具体方法如下:{C}
瞧!它的那样简单。想必你也报告通过BackgroundWorker对象及其关联的ProgressChanged事件完成一个百分点,但也许你刚刚有进步一点点的重复的动画,而不是在这种情况下,这是所有你需要的。
警告:用户仍可以进入Alt键F4键或单击在上面的进度对话框右上角的红色X,如果它的存在,所以你需要为了防止这种情况。一个简单的方法来解决这个问题,只是取消形成紧密的,除非它是从我们的代码。Private AllowClose As Boolean = False
Private Sub FormProgress_FormClosing(ByVal sender As Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If e.CloseReason = CloseReason.UserClosing And AllowClose Then
e.Cancel = True
End If
End Sub
Public Sub ForceClose()
AllowClose = True
Me.Close()
AllowClose = False
End Sub
只需拨打FormProgress.ForceClose BackgroundWorker的RunWorkerCompleted事件,而不是和你业务。历史三月十一日,2008年:战后初期