如何防止UI在漫长的过程中冻结?

时间:2010-03-15 12:45:43

标签: vb.net user-interface

我需要编写一个VB.Net 2008小程序来浏览所有寻找某些文件的固定驱动器。如果我将代码放在ButtonClick()中,UI会冻结,直到代码完成:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    'TODO Find way to avoid freezing UI while scanning fixed drives

    Dim drive As DriveInfo
    Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
    Dim filepath As String

    For Each drive In DriveInfo.GetDrives()
        If drive.DriveType = DriveType.Fixed Then
            filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
            For Each filepath In filelist
                'Do stuff
            Next filepath
        End If
    Next drive
End Sub

Google返回了BackGroundWorker控件的相关信息:这是解决此问题的正确/方法吗? 如果没有,你会推荐什么解决方案,可能只有一个非常简单的例子?

FWIW,我读到Application.DoEvents()是VBC经典的遗留物,应该避免使用。

谢谢。

4 个答案:

答案 0 :(得分:5)

BackgroundWorker是解决问题的好方法。实际上文档说明了这一点:

  

BackgroundWorker类允许您在单独的专用线程上运行操作。下载和数据库事务等耗时的操作可能会导致用户界面(UI)在运行时停止响应。当您需要响应式UI并且您遇到与此类操作相关的长时间延迟时,BackgroundWorker类提供了一种方便的解决方案。

答案 1 :(得分:2)

将流程放入单独的线程.... ...使用BackgroundWorker组件。

禁用在流程工作时不应使用的UI组件。

已完成 - 用户界面仍然会响应。

答案 2 :(得分:2)

关键是要从实际功能代码中分离UI代码。 耗时的功能应该在单独的线程上运行。为此,您可以:

  1. 创建并启动Thread对象 你自己
  2. 创建Delegate并使用 异步调用(使用 BeginInvoke)。
  3. 创建并启动BackgroundWorker
  4. 正如您所提到的,您应该避免Application.DoEvents()。正确分解应用程序的功能将允许您创建一个旨在响应的应用程序,而不是使用DoEvents“修复”创建一个无响应的应用程序(这是昂贵的,被认为是不好的做法,并暗示一个糟糕的设计)。

    由于您的方法没有返回值并且没有更新UI,因此最快的解决方案可能是创建一个Delegate并使用“fire and forget”异步调用:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Call New Action(AddressOf DrivesIteration).BeginInvoke(Nothing, Nothing)
    End Sub
    
    Private Sub DrivesIteration()
        Dim drive As DriveInfo
        Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
        Dim filepath As String
    
        For Each drive In DriveInfo.GetDrives()
            If drive.DriveType = DriveType.Fixed Then
                filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
                For Each filepath In filelist
                    DoStuff(...)
                Next
            End If
        Next 
    End Sub
    

    BTW,For..Next块不再需要以“Next(something)”结束,它已经过时了 - VB现在自己推断(某些东西),所以没有必要明确说明它。

答案 3 :(得分:-1)

一个。提出一个进度条...更新它和.REFRESH它...如果你想要的只是表明你没死。

B中。 DoEvents是邪恶的声音有点像“从不使用GOTO ......”pleeeze pleeeze pleeeze pleezeze pleeeze有时和任何语言的语法可能有害和有帮助的情况。为什么要跳过一百万个篮球只是为了基本上做“A”以上?

< 肥皂盒>

如果你知道事情花了很长时间,而且你也知道在你等待的时候(即它本质上是一个连续的过程)不会发生任何其他操作,而不是像你那样做任何事情并将其推入“背景”那么你将在代码的其余部分中直接使用“ITS_OK_TO_CONTINUE”布尔值,等待文件进程无论如何......最重要的是什么?你所做的一切都是为了让...代码变得复杂......嗯......“编程好”?不在我的书中。

谁关心DoEvents是否从ICE AGE“遗留下来”。在许多情况下,它确实是正确的。例如:框架为您提供ProgressBar.Refresh 但是您将看到它不完全“正常工作”,除非您在它之后后挂几个DoEvent。

< /肥皂盒>

℃。后台任务就是 - 背景;并且您通常使用它来操作非SERIAL任务或至少在某些时候可能或不可能更新前景的异步任务。但我认为,任何时候所谓的后台任务都会使前景变为HALTS,那么它(几乎)按照定义--- FOREGROUND任务;不管它需要多长时间。