Прогрессбар зависает при загрузке другой формы

У меня 3 формы;

Форма 1 - Основная форма

Форма 2 - Форма Sub (содержит индикатор выполнения и таймер)

Форма 3 - Форма с тяжелым содержимым, которая требует времени для загрузки (например, анализ данных с веб-страницы и запись их в Datagridview при событии загрузки формы)

Мне нужно показать form2 с индикатором выполнения во время загрузки form3

У меня есть следующие коды в 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

Есть идеи для решения?

2 ответа

Решение

Если вы новичок в программировании, то это может немного сбить с толку, но ответ состоит в том, чтобы вытолкнуть код из Load обработчик события Form3 в Async метод и жду его. Ваш пользовательский интерфейс зависает, потому что вы выполняете работу синхронно в потоке пользовательского интерфейса. Вам нужно либо явно использовать вторичный поток, либо использовать 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)т.е. Task который асинхронно выполняет функцию, которая возвращает DataTable, в Load обработчик события, вы вызываете этот метод и ожидаете Task, что означает, что ваш код будет ждать до Task выполнил и возвратил свои данные, но без блокировки потока пользовательского интерфейса, как это сделал бы синхронный вызов. Синхронный эквивалент будет следующим:

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

Попробуйте сделать процесс асинхронным, в моем понимании, отметка таймера уже асинхронна, но в форме1 вы могли бы использовать этот код внутри Задачи.

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

Я никогда не заходил так далеко на vb.net, так как начал программировать на C#, но это должно сработать

Другие вопросы по тегам