在 WinForm 的进度条上显示 WinSCP .NET 程序集传输进度
Showing WinSCP .NET assembly transfer progress on WinForm's progress bar
有一些主窗体,我在上面调用从 FTP 下载文件。提出此操作后,我希望看到新表单 ShowDialog
和它上面的进度条同时显示,然后显示进度并关闭新表单并返回主表单。然而,我的代码正在运行,当它开始处理时,我的主窗体冻结,然后新窗体出现然后关闭。我想更正的是在执行流程后立即显示此新表格。你能看一下并告诉我有什么问题吗?
这是我主窗体的下载过程调用:
Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)
FrmProgressBarWinscp
如下:
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 class和使用的方法:
...
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
...
进一步讨论 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()
进度条形式:
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
点编号。 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()
第 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()
在其他一些class中(可能之前没有提到这个方法放在不同的地方class - 我的自定义)
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
接口:
Public Interface InvokeCloseForm
Sub RUn()
End Interface
等待表格:
Public Class Waiting
Implements InvokeCloseForm
Public Sub RUn() Implements InvokeCloseForm.RUn
Me.Close()
End Sub
End Class
意思是returns传输完成后
解决方法是:
运行 WinSCP 传输(Session.GetFiles
)在单独的线程中,不阻塞 GUI 线程。
请参阅 WinForm Application UI Hangs during Long-Running Operation
处理 Session.FileTransferProgress
event.
不过请注意,事件处理程序将在后台线程上调用,因此您不能直接从处理程序更新进度条。您必须使用 Control.Invoke
来确保进度条在 GUI 线程上更新。
请参阅 How do I update the GUI from another thread?
下面是一个简单的实现。
更多版本的代码,参见WinSCP文章Displaying FTP/SFTP transfer progress on WinForms ProgressBar。
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
如果您想阻止用户进行某些操作,您可能希望在操作过程中禁用进度表(或其部分)。
使用窗体或控件的 .Enabled
属性。
从您现有的 SessionFileTransferProgress
处理程序中调用 Application.DoEvents
方法是更简单但不可靠且通常不推荐的解决方案。
当然,您还必须从 SessionFileTransferProgress
更新进度条。
Private Shared Sub SessionFileTransferProgress(
sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
ProgressBar1.Value = e.FileProgress
Application.DoEvents
End Sub
并且进度条的.Minimum
和.Maximum
必须设置在Session.GetFiles
之前。
但是不要那样做!这是错误的做法。
而且,您仍然需要按照上述正确解决方案中的相同方式禁用 forms/controls。
有一些主窗体,我在上面调用从 FTP 下载文件。提出此操作后,我希望看到新表单 ShowDialog
和它上面的进度条同时显示,然后显示进度并关闭新表单并返回主表单。然而,我的代码正在运行,当它开始处理时,我的主窗体冻结,然后新窗体出现然后关闭。我想更正的是在执行流程后立即显示此新表格。你能看一下并告诉我有什么问题吗?
这是我主窗体的下载过程调用:
Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)
FrmProgressBarWinscp
如下:
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 class和使用的方法:
...
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
...
进一步讨论 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()
进度条形式:
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
点编号。 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()
第 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()
在其他一些class中(可能之前没有提到这个方法放在不同的地方class - 我的自定义)
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
接口:
Public Interface InvokeCloseForm
Sub RUn()
End Interface
等待表格:
Public Class Waiting
Implements InvokeCloseForm
Public Sub RUn() Implements InvokeCloseForm.RUn
Me.Close()
End Sub
End Class
意思是returns传输完成后
解决方法是:
运行 WinSCP 传输(
Session.GetFiles
)在单独的线程中,不阻塞 GUI 线程。请参阅 WinForm Application UI Hangs during Long-Running Operation
处理
Session.FileTransferProgress
event.不过请注意,事件处理程序将在后台线程上调用,因此您不能直接从处理程序更新进度条。您必须使用
Control.Invoke
来确保进度条在 GUI 线程上更新。请参阅 How do I update the GUI from another thread?
下面是一个简单的实现。
更多版本的代码,参见WinSCP文章Displaying FTP/SFTP transfer progress on WinForms ProgressBar。
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
如果您想阻止用户进行某些操作,您可能希望在操作过程中禁用进度表(或其部分)。
使用窗体或控件的
.Enabled
属性。
从您现有的 SessionFileTransferProgress
处理程序中调用 Application.DoEvents
方法是更简单但不可靠且通常不推荐的解决方案。
当然,您还必须从 SessionFileTransferProgress
更新进度条。
Private Shared Sub SessionFileTransferProgress(
sender As Object, e As FileTransferProgressEventArgs)
'Print transfer progress
ProgressBar1.Value = e.FileProgress
Application.DoEvents
End Sub
并且进度条的.Minimum
和.Maximum
必须设置在Session.GetFiles
之前。
但是不要那样做!这是错误的做法。
而且,您仍然需要按照上述正确解决方案中的相同方式禁用 forms/controls。