问题
Have some main form on which I am calling file downloading from FTP. When this operation is raised i want to see new form as ShowDialog
and progress bar on it to be shown meantime, then show the progress and close new form and back to main form. My code is working however, when it will process is started my main form freezes and after while new form is appearing and then closing. What I would like to correct is to show this new form to be showed straightaway after process is executed. Can you take a look and tell me whats wrong?
This is out of my main form the download process called:
Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)
FrmProgressBarWinscp
is as follows:
Public Class FrmProgressBarWinscp
Property _winScp As WinScpOperation
Property _remotePicture As String
Property _ladujZdjecia As String
Property _removesource As String
Public Sub New()
InitializeComponent()
End Sub
Sub New(winscp As WinScpOperation, remotePicture As String, ladujzdjecia As String, removesource As Boolean)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
_winScp = winscp
_remotePicture = remotePicture
_ladujZdjecia = ladujzdjecia
_removesource = removesource
ShowDialog()
End Sub
Sub Run()
Try
Cursor = Cursors.WaitCursor
_winScp.GetFile(_remotePicture, _ladujZdjecia, _removesource)
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = 1
ProgressBar1.Value = 0
Do
ProgressBar1.Value = WinScpOperation._lastProgress
ProgressBar1.Refresh()
Loop Until ProgressBar1.Value = 1
Cursor = Cursors.Default
'Close()
Catch ex As Exception
Finally
If _winScp IsNot Nothing Then
_winScp.SessionDispose()
End If
System.Threading.Thread.Sleep(10000)
Close()
End Try
End Sub
Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Run()
End Sub
End Class
Winscp my own class and used methods:
...
Function GetFile(source As String, destination As String, Optional removeSource As Boolean = False)
Dim result As Boolean = True
Try
session.GetFiles(source, destination, removeSource).Check()
Catch ex As Exception
result = False
End Try
Return result
End Function
Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
_lastProgress = e.FileProgress
End Sub
Public Shared _lastProgress As Integer
...
Further discussion nr 3:
Main form:
Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)
End Function)
Dim forma As New FrmProgressBar
forma.ShowDialog()
Progress bar form:
Public Class FrmProgressBar
Public Sub New()
InitializeComponent()
End Sub
Sub Run()
Try
Do
ProgressBar1.Value = WinScpOperation._lastProgress
ProgressBar1.Refresh()
Loop Until ProgressBar1.Value = 1
Cursor = Cursors.Default
Catch ex As Exception
Finally
MsgBox("before sleep")
System.Threading.Thread.Sleep(10000)
MsgBox("after sleep sleep")
Close()
End Try
End Sub
Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Run()
End Sub
End Class
Point nr. 4:
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()
Task.WaitAll(tsk)
pic.Close()
Point 5:
Dim pic As New Waiting
pic.ShowDialog()
Dim tsk As Task = Task.Factory.StartNew(Sub() WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, pic, True))
Task.WaitAll(tsk)
'pic.Close()
In some other class (maybe didn't mentioned before this method is placed in diffrent class - my custom one)
Public Function GetFile(source As String, destination As String, formclose As InvokeCloseForm, Optional removeSource As Boolean = False) As Boolean
Dim result As Boolean = True
Try
session.GetFiles(source, destination, removeSource).Check()
Catch ex As Exception
result = False
End Try
formclose.RUn()
Return result
End Function
Interface:
Public Interface InvokeCloseForm
Sub RUn()
End Interface
Waiting form :
Public Class Waiting
Implements InvokeCloseForm
Public Sub RUn() Implements InvokeCloseForm.RUn
Me.Close()
End Sub
End Class
回答1:
The Session.GetFiles method in blocking.
It means it returns only after the transfer finishes.
The solution is to:
Run the WinSCP transfer (the
Session.GetFiles
) in a separate thread, not to block the GUI thread.For that see WinForm Application UI Hangs during Long-Running Operation
Handle the Session.FileTransferProgress event.
Though note that the event handler will be called on the background thread, so you cannot update the progress bar directly from the handler. You have to use the
Control.Invoke
to make sure the progress bar is updated on the GUI thread.For that see How to update the GUI from another thread in C#?
A trivial implementation is like:
Public Class ProgressDialog1 Private Sub ProgressDialog1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' Run download on a separate thread ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Download)) End Sub Private Sub Download(stateInfo As Object) ' Setup session options Dim mySessionOptions As New SessionOptions With mySessionOptions ... End With Using mySession As Session = New Session AddHandler mySession.FileTransferProgress, AddressOf SessionFileTransferProgress ' Connect mySession.Open(mySessionOptions) mySession.GetFiles(<Source>, <Destination>).Check() End Using ' Close form (invoked on GUI thread) Invoke(New Action(Sub() Close())) End Sub Private Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs) ' Update progress bar (on GUI thread) ProgressBar1.Invoke(New Action(Of Double)(AddressOf UpdateProgress), e.OverallProgress) End Sub Private Sub UpdateProgress(progress As Double) ProgressBar1.Value = progress * 100 End Sub End Class
You may want to disable the progress form (or its parts) during the operation, if you want to prevent the user from doing some operations.
Use the
.Enabled
property of the form or control(s).
Easier, but hacky and generally not recommendable solution, is to call the Application.DoEvents
method from your existing SessionFileTransferProgress
handler.
And of course, you have to update the progress bar from the the SessionFileTransferProgress
as well.
Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
ProgressBar1.Value = e.FileProgress
Application.DoEvents
End Sub
And the progress bar's .Minimum
and .Maximum
must be set before the Session.GetFiles
.
But do not do that! That's a wrong approach.
And still, you need to disable the forms/controls the same way as in the correct solution above.
来源:https://stackoverflow.com/questions/33012517/showing-winscp-net-assembly-transfer-progress-on-winforms-progress-bar