Поместите в задачу длительный метод, покажите новую форму и закройте ее после завершения задачи.

У моего приложения есть какой-то давно работающий метод, который требует некоторого времени для завершения, поэтому я решил перенести его в отдельную задачу, а затем покажу некоторую новую форму как ShowDialog внутри которого помещена анимация песочных часов, а затем эта форма должна быть закрыта, когда задача завершит работу. В тот момент ситуация такова, что моя новая форма ожидания совсем не собирается закрываться, она просто появляется и остается. Я где-то читал, что его, потому что ShowDialog в этом случае ничего не вернется, поэтому Close никогда не будет достигнут до тех пор, пока пользователь не нажмет кнопку Закрыть в форме вручную, но насколько это возможно, как если бы я положил form.Close оно должно быть таким же, как пользователь нажимает на эту форму. Пожалуйста, объяснений и поддержки здесь, что следует изменить, чтобы достичь цели. Ниже мой код.

Основная форма:

WinScp = New WinScpOperation("ftp", "myserver", "login", "password", 21, 0)

Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
   Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
End Function)

Dim pic As New Waiting
pic.ShowDialog() 'show waiting form

Task.WaitAll(tsk) 'waiting on task to be finalized

pic.Close() 'close waiting form
...

Форма ожидания (ничего, кроме песочных часов GIF)

Public Class Waiting
End Class

Дальнейшее обсуждение № 1:

Вариант 1: (ваша рабочая версия)

 Dim pic As New Waiting

                            Dim tsk As Task(Of Boolean) =
    Task.Factory.StartNew(Of Boolean)(
        Function()
            ' Run lenghty task
            Dim Result As Boolean = WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
            ' Close form once done (on GUI thread)
            pic.Invoke(New Action(Sub() pic.Close()))
            Return Result
        End Function)

                            ' Show the form
                            pic.ShowDialog()
                            Task.WaitAll(tsk)

Вариант 2: (не работает, почему??)

    Dim pic As New Waiting

                            Dim tsk As Task(Of Boolean) =
    Task.Factory.StartNew(Of Boolean)(
        Function()
            ' Run lenghty task
            Dim Result As Boolean = WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)

            Return Result
        End Function)

                            ' Show the form
                            pic.ShowDialog()
                            Task.WaitAll(tsk)
  ' Close form once done (on GUI thread)
            pic.Invoke(New Action(Sub() pic.Close()))

Дополнительный вопрос:

                        Dim pic As New Waiting

                        Dim tsk As Task(Of Boolean) =
Task.Factory.StartNew(Of Boolean)(
    Function()
        ' Run lenghty task
        Dim Result As Boolean = WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
        ' Close form once done (on GUI thread)
        pic.Invoke(New Action(Sub() pic.Close()))
        Return Result
    End Function)

                        ' Show the form
                        pic.ShowDialog()
                        Task.WaitAll(tsk)

                        If tsk.Result Then 'if return value is true
                            'Do something when file was downloaded correctly
                        Else
                            'Do something when file was NOT downloaded
                        End If

Дополнительный № 2: Это было первоначально:

           If WinScp.GetFile(lsbxPicPaths.SelectedItem, temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem), False) Then
                temp_pic = temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem)
            End If

Я реализовал наше решение:

Dim pic As New Waiting


                                Dim tsk As Task(Of Boolean) =
                Task.Factory.StartNew(Of Boolean)(
                   Function()
               Run lenghty task
                      Dim Result As Boolean = WinScp.GetFile(lsbxPicPaths.SelectedItem, temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem), False)
                    ' Close form once done (on GUI thread)
                      pic.Invoke(New Action(Sub() pic.Close()))
                      Return Result
                 End Function)

                           ' Show the form
                              pic.ShowDialog()
                            Task.WaitAll(tsk)

                    If tsk.Result Then 'if return value is true

                                   temp_pic = temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem)
                '                Else

                                End If

                '*************************************

Решение выше: понятия не имею, почему, когда определены две переменные, вместо того, чтобы напрямую поместить их в аргументы GetFile, решите это, потому что ранее также не было переменных и они работали. Может кто-нибудь объяснить это поведение?

                Dim remotefile As String = lsbxPicPaths.SelectedItem
                Dim temp_file As String = temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem)


                'http://stackru.com/questions/33030706/put-long-running-method-into-task-showing-new-form-meantime-and-closing-it-once
                Dim pic2 As New Waiting



                Dim tsk2 As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()

                                                                                     'Run lenghty task

                                                                                     Dim Result As Boolean = WinScp.GetFile(remotefile, temp_file, False)
                                                                                     'Close form once done (on GUI thread)
                                                                                     pic2.Invoke(New Action(Sub() pic2.Close()))
                                                                                     Return Result
                                                                                 End Function)
                pic2.ShowDialog()
                Task.WaitAll(tsk2)


                If tsk2.Result = True Then
                    MsgBox("GetFile zwrocilo true")
                    temp_pic = temp_dir & "\" & Path.GetFileName(lsbxPicPaths.SelectedItem)
                End If

2 ответа

Решение

У вас уже есть ответ здесь:
Отображение прогресса переноса сборки WinSCP .NET на панели прогресса WinForm

Проще говоря, я просто извлекаю соответствующую часть:

  • Вам необходимо закрыть форму в конце задания.
  • Поскольку задача выполняется в фоновом потоке, вам необходимо использовать .Invoke метод для вызова закрытия в потоке GUI.

Простая реализация выглядит так:

' Create the form before the task, so that we can reference it in the task
Dim pic As New Waiting

Dim tsk As Task(Of Boolean) =
    Task.Factory.StartNew(Of Boolean)(
        Function()
            ' Run lenghty task
            Dim Result As Boolean =
                WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
            ' Close form once done (on GUI thread)
            pic.Invoke(New Action(Sub() pic.Close()))
            Return Result
        End Function)

' Show the form
pic.ShowDialog()

Внутренне WinForms Form работает как:

Class Form                                      

    Private closed as Boolean                   

    Function ShowDialog                         

        While Not closed                        

            If ' X button clicked                 
                Close                           
            End IF                              

            If ' anything in the Invoke queue     
                ' Get action from the Invoke queue
                ' Run the action                  
                ' In our case the "action" is .Close
            End If                              

            ' Process message queue (mouse clicks, key presses, draw form)

        End While                               

    End Sub                                     

    Sub Close                                   
        closed = True                           
    End Sub                                     

    Sub Invoke(action)                          
        ' Add action to invoke queue              
    End Sub                                     

End Class                                       

Что касается универсального обработчика без задач, это то, как я добился в одном из приложений некоторое время назад

  1. Добавить новую форму (frmProgress)

  2. Добавьте изображение GIF песочных часов (picProgress) в центр этой формы

  3. Установите следующие свойства формы (frmProgress) в графическом интерфейсе или в коде события загрузки:

    Private Sub frmProgress_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load  
        FormBorderStyle = FixedSingle  
        Opacity = 0.5R  
        ShowInTaskbar = False  
        TopMost = True  
        WindowState = Maximized  
    End Sub  
    
  4. Центрировать изображение в форме по событию изменения размера, если требуется

    Private Sub frmProgress_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize  
        picProgress.Left = CInt(Me.Width / 2 - picProgress.Width / 2)  
        picProgress.Top = CInt(Me.Height / 2 - picProgress.Height / 2)  
    End Sub  
    
  5. Загрузите форму перед началом обработки (НЕ используйте ShowDialog ())

    frmProgress.Show()'DO NOT use ShowDialog()  
    
  6. Инициируйте вашу обработку

  7. Скрыть форму прогресса после выполнения задачи

    frmProgress.Hide()  
    
Другие вопросы по тегам