Put long running method into task, showing new form meantime and closing it once the task completes

浪尽此生 提交于 2019-12-04 06:33:25

问题


My application has some long running method which taking some time to be finalised therefore i decided to push it into the separate task then meantime show some new form as ShowDialog which inside is placed hourglass animation and then this form should be closed when task finished the job. On that moment situation is that my new waiting form is not going to Close at all its just show up and stay. I read somewhere that its because ShowDialog in that case will not return nothing that's why Close will be never reached until user click Close manually on form, but how its possible as if I put form.Close it should be the same as user would click on that form. Please of explanation and support here what should be changed to achieve the point. Below my code.

Main form:

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
...

Waiting form (nothing but the hourglass gif)

Public Class Waiting
End Class

Further discussion #1:

Option 1: (your working version)

 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)

Option2: (not working why??)

    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()))

Additional question:

                        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

Additional #2: This was oryginally:

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

I implemented our solution:

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

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

Solution to above: have no idea why when defined two variables instead directly put it into GetFile arguments solve it because previously also there was not variables and was working. Can someone explain that behaviour?

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


                'http://stackoverflow.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

回答1:


You already have the answer here:
Showing WinSCP .NET assembly transfer progress on WinForm's progress bar

To put it simple, I'm just extracting the relevant part:

  • You need to close the form at the end of the task.
  • As the task runs on a background thread, you need to use .Invoke method to invoke the closing on a GUI thread.

A simple implementation is like:

' 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()

Internally the WinForms Form works like:

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                                       



回答2:


As of generic handler without tasks, this is how I achieved in one of applications a while ago

  1. Add a new form (frmProgress)

  2. Add hourglass GIF image (picProgress) to the center of this form

  3. Set following form (frmProgress) properties either in GUI or in code on load event:

    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. Center image in form on resize event, if required

    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. Load the form before initiating your processing (DO NOT use ShowDialog())

    frmProgress.Show()'DO NOT use ShowDialog()  
    
  6. Initiate your processing

  7. Hide progress form after task is finished

    frmProgress.Hide()  
    


来源:https://stackoverflow.com/questions/33030706/put-long-running-method-into-task-showing-new-form-meantime-and-closing-it-once

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!