【发布时间】:2016-06-29 11:31:10
【问题描述】:
我有一个名为 SFTPConnectorManager.vb 的类,它负责管理与服务器的 FTP 连接。我有 Form1.vb 类,它是主要的 GUI。我想更新 Form1 上的 ProgressBar 以显示文件传输的进度。负责与 FTP 服务器建立连接的函数在一个新线程上启动,这允许 form1 不会被冻结,这很好,但是对我来说挑战是能够更新进度条,这对我来说是行不通的完全是我。
我尝试过/做过的事情:
- 使用委托从单独的线程更新 UI
- 使用后台工作程序并使用它的进度更改事件,我想我可能会在这里做一些事情,但后来我记得 UI 的更新需要在文件传输事件期间发生,特别是 SessionFileTransferProgress,而不是当我提出进度改变事件。
- 阅读了超过 20 页有关多线程和事件处理的文档,我猜还是不明白...
我需要做什么:
- 更新进度条 UI 控件,在文件传输正在进行时更新,并且它不需要冻结 UI,因此它需要在单独的线程上运行(我相信到目前为止我已经实现了)
我正在使用的代码:
- https://winscp.net/eng/docs/library_session
- https://winscp.net/eng/docs/library_session_filetransferprogress
Form1.vb
Public Sub sub1(ByVal x As Integer, y As Integer)
StatusLabel2.Text = "Connected"
ProgressBar1.Maximum = ProgressBar1.Maximum + x
ProgressBar1.Value = ProgressBar1.Value + y
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles StartBtn.Click
' If (BackgroundWorker1.IsBusy) <> True Then
' BackgroundWorker1.RunWorkerAsync()
' End If
'Call Xml reader To Get respective values And store them into Class Property
Dim oConnect = New SFTPConnectorManager
Dim oXmlRead As XmlReader = XmlReader.Create("D:\dale_documents\projects\programming\vbnet\remote_data_backup\sftp_backup\settings.xml")
While (oXmlRead.Read())
Dim eType = oXmlRead.NodeType
If (eType = XmlNodeType.Element) Then
If (oXmlRead.Name = "HostName") Then
oConnect.HostName = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "UserName") Then
oConnect.UserName = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "Password") Then
oConnect.Password = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "Port") Then
oConnect.Port = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "Protocol") Then
oConnect.ProtocolSelection = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "FTPMode") Then
oConnect.FtpModeSelection = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "SSHFingerPrint") Then
oConnect.SSHKey = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "Remotepath") Then
oConnect.RemotePath = oXmlRead.ReadInnerXml.ToString
End If
If (oXmlRead.Name = "Localpath") Then
oConnect.LocalPath = oXmlRead.ReadInnerXml.ToString
End If
End If
End While
Dim eProtocolOptions = oConnect.ProtocolSelection
Dim sUserName = oConnect.UserName
Dim sHostName = oConnect.HostName
Dim sPassword = oConnect.Password
Dim sSSHKey = oConnect.SSHKey
Dim iPort = oConnect.Port
Dim sRemotePath = oConnect.RemotePath
Dim sLocalPath = oConnect.LocalPath
Dim bFlag = oConnect.bFlag
Dim asOptions = New String() {eProtocolOptions, sHostName, sUserName, iPort, sPassword, sSSHKey, sRemotePath, sLocalPath}
oConnect.TestThread(asOptions)
SFTPConnectorManager.vb
Function StartConnectionThread(asOptions)
Try
Dim oSessionOptions As New SessionOptions
With oSessionOptions
.Protocol = ProtocolSelection
.HostName = HostName
.UserName = UserName
.PortNumber = Port
.Password = Password
.SshHostKeyFingerprint = SSHKey
End With
Using oSession As New Session
AddHandler oSession.FileTransferProgress, AddressOf SessionFileTransferProgress
oSession.Open(oSessionOptions)
Dim oTransferOptions As New TransferOptions
oTransferOptions.TransferMode = TransferMode.Binary
oSession.GetFiles(RemotePath, LocalPath, False, oTransferOptions)
oSession.Close()
bFlag = False
End Using
MessageBox.Show("File Transfer Compelete")
Return 0
Catch ex As Exception
MessageBox.Show(ex.ToString())
Return 1
End Try
End Function
Public Delegate Sub SetbarValues(maximum As Integer, value As Integer)
Private Sub SessionFileTransferProgress(ByVal sender As Object, ByVal e As FileTransferProgressEventArgs)
Dim oForm1 = Form1
Dim msd As SetbarValues = AddressOf oForm1.sub1
If oForm1.InvokeRequired Then
msd.Invoke(1, 1)
Else
oForm1.sub1(1, 1)
End If
'oForm1.ProgressUpdate()
'If (Form1.CheckForIllegalCrossThreadCalls) Then
' MsgBox("Illegal cross-thread operation deteced.")
'End If
End Sub
Public Sub TestThread(asOption())
Dim oSFTPConnectionManager = New SFTPConnectorManager
Dim Thread As New Thread(AddressOf oSFTPConnectionManager.StartConnectionThread)
oSFTPConnectionManager.ProtocolSelection = asOption(0)
oSFTPConnectionManager.HostName = asOption(1)
oSFTPConnectionManager.UserName = asOption(2)
oSFTPConnectionManager.Port = asOption(3)
oSFTPConnectionManager.Password = asOption(4)
oSFTPConnectionManager.SSHKey = asOption(5)
oSFTPConnectionManager.RemotePath = asOption(6)
oSFTPConnectionManager.LocalPath = asOption(7)
Thread.Start()
End Sub
所以你可能会看到我尝试使用委托,我对它进行了相当多的阅读,我相信这是我需要从一个单独的线程更新 UI 元素,但我显然误解了它,因为我可以不要在我自己的项目中实现这个概念。 UI 更改需要在 SessionFileTransferProgress 事件期间发生。
请各位男孩女孩们,我对此束手无策,这是我最后的救命之恩,如果我无法理解和实施这些概念,我认为我将无法继续学习编程。
【问题讨论】:
-
您的代码看起来一般。它做错了什么?
-
oConnect.TestThread(asOptions) 这个函数在主窗体中被调用,它在一个单独的线程上运行一个 FTP 连接,我这样做是为了在文件传输过程中 UI 不会冻结。单步执行代码,我可以看到值被分配给指定的控件,但它们没有反映在 UI 上,所以出了点问题,我真的不确定是什么。
-
现在,如果您查看 SFTPConnectorManager.vb,您将看到 StartConnectionThread,如果您阅读此函数,您将看到一行显示 AddHandler oSession.FileTransferProgress, AddressOf SessionFileTransferProgress。这基本上是“在执行函数时执行这段代码,在这种情况下,在调用 oSession.GetFiles 时运行指定的函数)。
-
我的想法是我可以用它作为更新进度条的点,当操作在与 UI 相同的线程上触发时它工作得很好,但我们不希望这样,因为我们希望主 UI 保持响应。因此,挑战是在文件传输发生时从单独的线程更新 UI 控件。
-
好的,我明白了。我看到了
.Invoke的电话,但没有意识到这是一个错误的.Invoke。请参阅重复问题的答案。您必须致电Me.Invoke。
标签: .net vb.net multithreading winforms winscp