【问题标题】:Progressbar freezing while the other form loading进度条在其他表单加载时冻结
【发布时间】:2018-10-18 12:29:29
【问题描述】:

我有 3 个表格;

Form1 - 主窗体

Form2 - 子表单(包含进度条和计时器)

Form3 - 包含大量内容的子表单需要时间来加载(例如从网页解析数据并在表单加载事件中将其写入 Datagridview)

我需要在 form3 加载时显示 form2 并运行进度条

我在 Form1 有以下代码;

Me.Hide
Form2.Show()
Form3.Show()

表格 2 中的代码;

Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    LoadingTimer.Enabled = True
End Sub

Private Sub LoadingTimer_Tick(sender As Object, e As EventArgs) Handles LoadingTimer.Tick

    If MyProgressBar.Value <= MyProgressBar.Maximum - 1 Then
        MyProgressBar.Value += 10
    End If
    If MyProgressBar.Value = 100 Then
        LoadingTimer.Enabled = False
        Me.Close()
    End If
    If Label1.ForeColor = Color.LimeGreen Then
        Label1.ForeColor = Color.White
    Else
        Label1.ForeColor = Color.LimeGreen
    End If
End Sub
End Class

问题是 Form3 加载时进度条开始但在开始时冻结

有什么解决办法吗?

【问题讨论】:

    标签: vb.net timer progress-bar form-load


    【解决方案1】:

    如果您是编程新手,那么这可能有点令人困惑,但答案是将代码从 Form3Load 事件处理程序推送到 Async 方法并等待它。您的 UI 冻结,因为您正在 UI 线程上同步工作。您需要明确使用辅助线程或使用Async/Await。这个:

    Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        'Do some work.
    End Sub
    

    会变成这样:

    Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Await DoWork()
    End Sub
    
    Private Async Function DoWork() As Task
        Await Task.Run(Sub()
                           'Do some work.
                       End Sub).ConfigureAwait(False)
    End Function
    

    实际上,这可能比必要的复杂,这应该可以正常工作:

    Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Await Task.Run(Sub()
                           'Do some work.
                       End Sub).ConfigureAwait(False)
    End Sub
    

    重新阅读您的问题后,您可能需要做的是让您的异步方法成为一个函数,该函数从网页或其他任何地方检索和返回数据,然后您将这些数据同步加载到您的 DataGridView 之后,例如

    Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DataGridView1.DataSource = Await GetDataAsync()
    End Sub
    
    Private Async Function GetDataAsync() As Task(Of DataTable)
        Return Await Task.Run(Function()
                                  Dim table As New DataTable
    
                                  'Populate table here.
    
                                  Return table
                              End Function).ConfigureAwait(False)
    End Function
    

    所以GetDataAsync 方法返回一个Task(Of DataTable),即异步执行一个返回DataTable 的函数的Task。在Load 事件处理程序中,您调用该方法并等待Task,这意味着您的代码将等待Task 执行并返回其数据,但不会像同步调用那样阻塞UI 线程。同步等效项是这样的:

    Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DataGridView1.DataSource = GetData()
    End Sub
    
    Private Function GetData() As DataTable
        Dim table As New DataTable
    
        'Populate table here.
    
        Return table
    End Function
    

    【讨论】:

    • 抱歉我的新手级问题,但我应该为“在 Async Sub 上做一些工作,form2.show 是否足够或需要向 form3 添加计时器?”
    • @Ali :在Task.Run() 中,您应该将所有与UI(用户界面)交互的繁重处理代码。任何直接涉及访问您的表单或其任何控件的操作都需要在 Task.Run() 之前或之后完成。
    • @Ali :如果您绝对必须访问任务内的 UI,您可以使用 Control.Invoke() 从 UI 线程访问和返回信息(有关 here 的更多信息,在 下访问 UI 线程)。 但要小心! 一次只能访问尽可能少的部分,并将尽可能多的处理留给任务。例如,如果您需要处理来自某个控件的一组项目,请从 UI 中克隆它并让您的任务处理循环等。
    • @VisualVincent,非常感谢您的及时回答,我使用的是 Visual Studio 2017,目标网络框架为 4.6.1。我相信这个主题会很困难,因为我有一些简单的问题,比如表单加载顺序应该如何,从 Form1 -> 加载 Form3 和从 Form3 -> 加载 Form2 (@ task.run 的外部。我不在此过程中需要任何用户交互,因为 form3 正在从网页读取数据并在开始时进行解析
    • @Ali :如果您使用 jmcilhinney 建议的任务,那么长时间运行的代码不会中断您的其余代码,因此加载顺序应该无关紧要,除非您特别需要一种表格来在另一个之前打开(例如,如果 Form3 需要更新 Form2)。但是,按照您的建议让 Form3 打开 Form2 会使事情变得更加动态,因为即使您要从代码中的其他位置打开 Form3,它也会自动显示加载表单。
    【解决方案2】:

    尝试使流程异步,据我了解,计时器滴答已经是异步的,但在 form1 中,您可以使用可以在任务中包含该代码

    Me.Hide
    Task.Run(Function() Form2.Show())
    Form3.Show()
    

    自从我开始在 c# 上编程以来,我从未在 vb.net 上达到过这么远,但这应该可以解决问题

    【讨论】:

    • 您不应在不同的线程/任务中修改 UI 元素。这样做最终会破坏事情,即使它现在有效。所有与 UI 相关的工作都应仅在 UI 线程上完成。
    • 你的想法是正确的,但不是你的代码。仅将繁重的处理和其他非 UI 工作移至任务,而不是整个表单。
    • @nalnpir,您好,您的代码出错了,请您重新检查一下吗?
    • 阿里尝试使用 Sub() 代替 Function()。 @VisualVincent 老实说你是对的,我总是在我的 c# 代码中使用 Invoke,我不认为像这样的简单任务会产生任何错误,但是 Ali 你应该更多地调查调用方法的用法。
    • @nalnpir 谢谢你,但你能再给我一个提示吗?如果你不介意的话,可以在 C## 中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-02
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多