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
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
As of generic handler without tasks, this is how I achieved in one of applications a while ago
Add a new form (frmProgress)
Add hourglass GIF image (picProgress) to the center of this form
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
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
Load the form before initiating your processing (DO NOT use ShowDialog())
frmProgress.Show()'DO NOT use ShowDialog()
Initiate your processing
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