VB.Net Webclient Upload Hanging - vb.net

I have multiple files to upload (to FTP server) using this code:
Private Sub UploadFile(ByVal local As String)
If wc.IsBusy = True Then Throw New Exception("An upload is already ongoing!")
wc.Credentials = New NetworkCredential(usr.ToString, pass.ToString) 'Set the credentials.
'total_dl_size = GetDownloadSize(url) 'Get the size of the current file.
Try
Dim FileName As String = Path.GetFileName(local) 'Get the current file's name.
AppendWarning("Uploading " & FileName & "... ") 'Download notice.
wc.UploadFileAsync(New Uri(info_srv & local), Path.Combine(mc_dir, local)) 'Download the file to the desktop (use your own path here).
Catch ex As Exception
AppendWarning("-ERR: Could not download file: " & local & ControlChars.NewLine)
End Try
End Sub
Private Sub AppendWarning(ByVal Text As String)
If tb_warnings.InvokeRequired Then
tb_warnings.Invoke(Sub() tb_warnings.AppendText(Text))
Else
tb_warnings.AppendText(Text)
End If
End Sub
Private Sub wc_UploadProgressChanged(sender As Object, e As System.Net.UploadProgressChangedEventArgs) Handles wc.UploadProgressChanged
total_ul = e.BytesSent
Dim Progress As Integer = CType(Math.Round((baseline + total_ul) * 100) / total_ul_size, Integer)
If ProgressBar1.InvokeRequired Then
ProgressBar1.Invoke(Sub()
If Progress > 100 Then Progress = 100
If Progress < 0 Then Progress = 0
ProgressBar1.Value = Progress
End Sub)
Else
If Progress > 100 Then Progress = 100
If Progress < 0 Then Progress = 0
ProgressBar1.Value = Progress
End If
If lbl_progress.InvokeRequired Then
lbl_progress.Invoke(Sub() lbl_progress.Text = ((total_ul + baseline) / 1024).ToString("N0") & " KB / " & (total_ul_size / 1024).ToString("N0") & " KB")
Else
lbl_progress.Text = ((total_ul + baseline) / 1024).ToString("N0") & " KB / " & (total_ul_size / 1024).ToString("N0") & " KB | " & Progress.ToString & "%"
End If
End Sub
Private Sub wc_uploadFileCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Handles wc.UploadDataCompleted
If e.Cancelled Then
MessageBox.Show(e.Cancelled)
ElseIf Not e.Error Is Nothing Then
MessageBox.Show(e.Error.Message)
Else
If files.Count > 0 Then
AppendWarning("Upload Complete!" & ControlChars.NewLine)
baseline = baseline + total_ul
Dim file As String = files.Dequeue()
MsgBox(file)
UploadFile(file) 'Download the next file.
Else
AppendWarning("All Uploads Finished!" & ControlChars.NewLine)
End If
End If
However, using my two test files, it always stops at what would otherwise be the end of the first file I've given it, and doesn't go onto the second one.
However, I have an FTP client connected to this same server, and when I refresh I can see (at least for the first file) the data is being properly uploaded.
Any suggestions as to what's going wrong here?
Edit, log: http://pastebin.com/kqG28NGH
Thank you for any assistance!

This works for me...I tried to mimic what I think is in your form. I tested with a queue of 8 files ranging from 150K to 400K each. I couldn't quite work out what you were trying to do with the progress bar. Mine fills for each file and resets for the next, finishing empty with the last call to DoUpload where there are no more files. Hopefully, this will help.
Imports System.IO
Imports System.Net
Public Class Form1
Const info_srv As String = "ftp://example.com/SomeFolder/"
Const usr As String = ""
Const pass As String = ""
Const mc_dir As String = "D:\Source\Folder"
Private WithEvents wc As New Net.WebClient
' Contains file names only, no paths
Private Files As New Queue(Of String)
Private Sub Button1_Click(sender As Object, e As EventArgs) _
Handles Button1.Click
wc.Credentials = New NetworkCredential(usr, pass)
' Put the work in a task so UI is responsive
Task.Run(Sub() DoUpload())
End Sub
Private Sub DoUpload()
ShowProgress("", 0)
If Files.Count > 0 Then
Dim local As String = Files.Dequeue
Dim FileName As String = Path.Combine(mc_dir, local)
AppendWarning("Uploading " & FileName & "... ")
Try
wc.UploadFileAsync(New Uri(info_srv & local), FileName)
Catch ex As Exception
AppendWarning("-ERR: Could not upload file: " & local & Environment.NewLine)
End Try
Else
AppendWarning("All Uploads Finished!" & Environment.NewLine)
End If
End Sub
Private Sub wc_UploadProgressChanged(sender As Object, e As UploadProgressChangedEventArgs) _
Handles wc.UploadProgressChanged
' Do not use e.ProgressPercentage - it's inaccurate by half by design per Microsoft
With String.Format("{0} KB / {1} KB", Int(e.BytesSent / 1024).ToString("N0"), Int(e.TotalBytesToSend / 1024).ToString("N0"))
ShowProgress(.ToString, Int(e.BytesSent / e.TotalBytesToSend * 100))
End With
End Sub
Private Sub wc_UploadFileCompleted(sender As Object, e As UploadFileCompletedEventArgs) _
Handles wc.UploadFileCompleted
Select Case True
Case e.Cancelled
MessageBox.Show("Cancelled")
Case e.Error IsNot Nothing
MessageBox.Show(e.Error.Message)
Case Else
AppendWarning("Upload Complete!" & Environment.NewLine)
' I needed this just so I could see it work, otherwise too fast
Threading.Thread.Sleep(500)
DoUpload()
End Select
End Sub
Private Sub AppendWarning(ByVal Text As String)
If Me.InvokeRequired Then
Me.Invoke(Sub() AppendWarning(Text))
Else
tb_warnings.AppendText(Text)
End If
End Sub
Private Sub ShowProgress(LabelText As String, Progress As Integer)
If Me.InvokeRequired Then
Me.Invoke(Sub() ShowProgress(LabelText, Progress))
Else
Me.lbl_progress.Text = LabelText
Me.lbl_progress.Refresh()
Me.ProgressBar1.Value = Progress
Me.ProgressBar1.Refresh()
End If
End Sub
End Class

For posterity:
Check your network trace settings in the VB config. I used a really verbose catch-all config I found to do the trace, but it seems the overhead was killing the upload. I've since found a much leaner focus-on-ftp set of xml to modify this and the files now upload properly. Thank you everyone!

Related

VB6 to VB.NET conversion (Syntax : Print to StreamWriter/Reader)? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 1 year ago.
Improve this question
The following code is a part of VB6 application & I'm currently converting to VB.NET Windows Service. Line starts with Open gives me an error(I assume the 'Open' syntax does not support with VB.NET). I tried converting the code utilizing all the VB.NET knowledge I have but would like to know the ideal/optimistic solution.
VB6 Code
Private Sub Text1_GotFocus()
Me.lblCompanyName.Caption = ""
Me.lblCompanyName.Refresh
lngPosted = 0
lngSkipped = 0
lngClosed = 0
strMsg = Dir(strPath & "\WisysDataCollector_*.log", vbNormal)
Do While strMsg <> ""
On Error Resume Next
If strMsg < "WisysDataCollector_" & Format(DateAdd("m", -12, Now), "yyyyMM") Then
Kill(strPath & "\" & strMsg)
End If
On Error GoTo 0
strMsg = Dir()
Loop
datTimeStart = Now
Do
On Error Resume Next
Open strPath & "\WisysDataCollector_" & Format(Now, "yyyyMM") & ".log" For Append Lock Read Write As #1
lngST = Err.Number
strMsg = Err.Description
On Error GoTo 0
If lngST = 0 Then
Exit Do
End If
dblTimeElapsed = (Now - datTimeStart) * 24 * 60 * 60
If dblTimeElapsed > 20 Then
varResponse = vbCancel
If varResponse = vbCancel Then
strStatus = "Log file busy. Process aborted."
GoTo EXITFORM
End If
datTimeStart = Now
End If
Loop
Code continues.......
What I've tried : Created a 'FileIO' class as following with IO.StreamWriter and IO.StreamReader
Public Class FileIO
Public Shared Sub WriteLog(strToWrite As String)
Dim filePath As String = AppDomain.CurrentDomain.BaseDirectory + "\WisysDataCollector_" + Format(Now, "MMddyy") + ".log"
Dim streamWr As IO.StreamWriter = Nothing
Try
streamWr = New IO.StreamWriter(filePath, True)
streamWr.Write(Now + " - " + strToWrite + vbNewLine)
streamWr.Flush()
streamWr.Close()
Catch ex As Exception
End Try
End Sub
Public Shared Sub ReadLog(strToWrite As String)
Dim filePath As String = AppDomain.CurrentDomain.BaseDirectory + "\WisysDataCollector_" + Format(Now, "MMddyy") + ".log"
Dim streamRd As IO.StreamReader = Nothing
Try
streamRd = New IO.StreamReader(filePath, True)
streamRd.Read()
streamRd.Close()
Catch ex As Exception
End Try
End Sub
End Class
Please let me know the errors I've made in the above code also how should I use the 'FileIO' class to correct the errors with 'Open' and 'Print #1'?
Also if someone can please clarify what were they trying to do by this code line(honestly I'm trying to understand but not sure why they've multiplied the time difference by 24 * 60 * 60) dblTimeElapsed = (Now - datTimeStart) * 24 * 60 * 60?
The ampersand, &, is the concatenation character for vb.net. Although the plus sign will usually work, if numbers are involved you could get unexpected results.
Streams must be disposed to released unmanaged resources. Using...End Using blocks take care of this for us.
I made filePath a class level variable because it is used in more than one method. This must also be Shared because it is used in Shared methods. I changed the format of date so it will appear chronologically in File Explorer.
It makes no sense to read the log and do nothing with it. I changed the ReadLog method to a Function. It also makes no sense to pass a string to it.
I believe the vb6 code was trying to express elapsed time in seconds with the 24 60 60 business. I gave you an example of that with the Form.Load setting the startTime and then hitting a button some time later and calculating the seconds that had passed.
In the form class...
Private StartTime As DateTime
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
StartTime = Now
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
FileIO.WriteLog(TextBox1.Text)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
TextBox2.Text = FileIO.ReadLog
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim elapsedTime As TimeSpan = Now - StartTime
Dim TotalSeconds = elapsedTime.TotalSeconds
MessageBox.Show($"The elapsed time since the program started is {TotalSeconds}")
End Sub
Your class would look like this...
Public Class FileIO
Private Shared filePath As String = AppDomain.CurrentDomain.BaseDirectory & "\WisysDataCollector_" & Format(Now, "yyyyMMdd") & ".log"
Public Shared Sub WriteLog(strToWrite As String)
Using sw = File.AppendText(filePath)
sw.WriteLine(strToWrite)
End Using
End Sub
Public Shared Function ReadLog() As String
If File.Exists(filePath) Then
Return File.ReadAllText(filePath)
Else
Return ""
End If
End Function
End Class

vb.net statusbar text doesn't show

I have a code that searches for words in documents and fills a Listview with the found documents. Because the process can be rather lengthly I put a warning in the statusbar.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim foundList As Boolean
Dim docImage As VariantType
Dim PresetName As String
ListView1.Items.Clear()
ToolStripLabel1.Text = "Searching your documents, please wait."
Try
For Each row As DataRowView In CheckedListBox1.CheckedItems
SearchRegX = row("RegX")
Next
If TextBoxFreeText.Text <> "" Then
'look for a space in the search criteria, meaning there are more words
Dim counter As Integer = TextBoxFreeText.Text.IndexOf(" ")
If counter <> -1 Then
'more words were entered to search
Dim varSplit As Object
varSplit = Split(TextBoxFreeText.Text, " ")
'if more than two words are entered our regex doesnt work, so we exit the sub
If varSplit.length > 2 Then
MsgBox("Your search criteria are to complex, use a maximum of two words",, Title)
Exit Sub
End If
iWords = NumericUpDown1.Value
SearchRegX = "(?i)\b(?:" + varSplit(0) + "\W+(?:\w+\W+){0," + iWords + "}?" + varSplit(1) + "|" + varSplit(1) + "\W+(?:\w+\W+){0," + iWords + "}?" + varSplit(0) + ")\b"
Else
'just one word was entered
SearchRegX = "(?i)\b" + TextBoxFreeText.Text + "\b"
End If
End If
If SearchRegX = "" Then
MsgBox("No Keyword was selected",, Title)
Exit Sub
End If
If ListBox1.SelectedIndex > -1 Then
For Each Item As Object In ListBox1.SelectedItems
Dim ItemSelected = CType(Item("Path"), String)
SearchFolder = ItemSelected
'check if the folder of the archive still exists
If (Not System.IO.Directory.Exists(SearchFolder)) Then
Dim unused = MsgBox("The archive " + SearchFolder.Substring(SearchFolder.Length - 5, Length) + " was not found",, Title)
Continue For
End If
Dim dirInfo As New IO.DirectoryInfo(SearchFolder)
Dim files As IO.FileInfo() = dirInfo.GetFiles()
Dim file As IO.FileInfo
docImage = ImageList1.Images.Count - 1
Dim items As New List(Of ListViewItem)
For Each file In files
Dim filename As String = file.Name.ToString
If file.Extension = ".pdf" Or file.Extension = ".PDF" Then
foundList = PDFManipulation.GetTextFromPDF2(SearchFolder + filename, SearchRegX)
If foundList = True Then
If ListView1.FindItemWithText(filename.ToString) Is Nothing Then
items.Add(New ListViewItem(New String() {"", filename.ToString, SearchFolder.ToString}, docImage))
End If
End If
End If
Next
ListView1.Items.AddRange(items.ToArray)
Next
ToolStripLabel1.Text = ListView1.Items.Count.ToString + " Documents found."
Else
MsgBox("No archive was selected",, Title)
End If
SearchRegX = ""
SearchFolder = ""
'now save the search word to our textfile
PresetName = TextBoxFreeText.Text
If PresetName <> "" Then
AddSearchWord()
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Somehow the text doesn't show in the statusbar before the process starts.
What should I change?
I thought about a BackgroundWorker showing an image like waiting but the BackgroundWorker doesn't work because inside my Sub I call a function elsewhere.
This happens because the status strip control is not rendered properly before the pending workload is finished. To do so, you can refresh the status strip manually:
StatusStrip1.Refresh()
That is, if your only goal is to set the text. If you thought about running the code in the background so that the form is still responsive to user input, you'll need asynchronous programming using System.Threading.Tasks or System.Threading.Thread to run the code as a seperate thread. Be aware though that you may face difficulties when trying to access controls outside of the main thread.
Add Async to your button handler declaration:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Now change your call to PDFManipulation.GetTextFromPDF2() so that it is within a Task:
Await Task.Run(Sub()
foundList = PDFManipulation.GetTextFromPDF2(SearchFolder + filename, SearchRegX)
End Sub)
If foundList = True Then
...

upload multiple files to a local folder in vb.net

I am trying to create a function that will allow user to upload multiple files to a local folder.
currently i am able to upload just one file. i needed to upload more files in one go.
what i use for opening files/folder
a.Multiselect = True
If a.ShowDialog() = Windows.Forms.DialogResult.OK Then
removeatt.Show()
removeatt.Text = "Remove Attachment"
fpath.Text = a.FileName
address.Text = System.IO.Path.GetFileName(a.FileName)
Dim file As String
file = fpath.Text.ToString
Label7.Text = file
If fpath.Text = "-" Then
removeatt.Hide()
Else
removeatt.Show()
End If
End If
what i use for saving attachment
If fpath.Text = "-" Then
Else
My.Computer.FileSystem.CopyFile(fpath.Text = "-", dir2 + Upload.Label16.Text, Microsoft.VisualBasic.FileIO.UIOption.AllDialogs, Microsoft.VisualBasic.FileIO.UICancelOption.DoNothing)
End If
any help is appreciated
thanks
It is not entirely clear to me where you handle the selected files, the first one is about removing attachments and the saving-part is not about uploading, it saves a file to the disk of the user as it seems.
Generally i'd recommend you to write a function that handles one file at a time so you can feed the function with the list of files to be copied in a for each-loop. The function is a bit "basic" to demonstrate what i mean.
Public Function CopyToDisk(ByVal DestinationPath As String, ByVal Sourcepath As String) As String
If Not System.IO.File.Exists(Sourcepath) Then
Return "Source missing" & Sourcepath
End If
Try
File.Copy(Sourcepath, DestinationPath)
Catch ex As Exception
Return ex.Message
End Try
Return "ok"
End Function
Well, have a look at the example from MSDN here, it has a filepicker and then it puts the objects in an array you can loop through and copy it where you want it to copy.
Here is the MSDN-original
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
InitializeOpenFileDialog()
End Sub
Private Sub Selectfilebutton_Click_1(sender As Object, e As EventArgs) Handles Selectfilebutton.Click
Dim dr As DialogResult = Me.OpenFileDialog1.ShowDialog()
If (dr = System.Windows.Forms.DialogResult.OK) Then
' Read the files
Dim file As String
For Each file In OpenFileDialog1.FileNames
'' you can loop through the array of objects and use a function to do the copying
' so for instance with my function it would be :
' copytodisk(Destination, file.filename)
' Create a PictureBox for each file, and add that file to the FlowLayoutPanel.
Try
Dim pb As New PictureBox()
Dim loadedImage As Image = Image.FromFile(file)
pb.Height = loadedImage.Height
pb.Width = loadedImage.Width
pb.Image = loadedImage
FlowLayoutPanel1.Controls.Add(pb)
Catch SecEx As SecurityException
' The user lacks appropriate permissions to read files, discover paths, etc.
MessageBox.Show("Security error. Please contact your administrator for details.\n\n" & _
"Error message: " & SecEx.Message & "\n\n" & _
"Details (send to Support):\n\n" & SecEx.StackTrace)
Catch ex As Exception
' Could not load the image - probably permissions-related.
MessageBox.Show(("Cannot display the image: " & file.Substring(file.LastIndexOf("\"c)) & _
". You may not have permission to read the file, or " + "it may be corrupt." _
& ControlChars.Lf & ControlChars.Lf & "Reported error: " & ex.Message))
End Try
Next file
End If
End Sub
Public Sub InitializeOpenFileDialog()
' Set the file dialog to filter for graphics files.
Me.OpenFileDialog1.Filter = _
"Images (*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|" + _
"All files (*.*)|*.*"
' Allow the user to select multiple images.
Me.OpenFileDialog1.Multiselect = True
Me.OpenFileDialog1.Title = "My Image Browser"
End Sub

vb.net update progress bar multithread

Long time reader, first time poster. Usually I'm able to find the answer and make it work. Not this time..... I'm using VB.NET in VS2013. I am trying to update a progress bar with work done in a secondary thread. Easy right? No. I had to make it more complicated. The progress bar (ToolStripProgressBar1) is on the main form (frmMain), the MDI of the project. A secondary form (frmShipping) has a button which initiates a second thread to do some COMM Port communications in a class (cApex). I can get the progress bar to update on the frmMain from the main UI thread (frmShipping button).
This is the code from button on frmShiping and the multithread procedure:
Private Sub btnreadScanner_Click(sender As Object, e As EventArgs) Handles btnreadScanner.Click
Dim thrReadScanner As New System.Threading.Thread(AddressOf ReadScanner)
thrReadScanner.IsBackground = True
thrReadScanner.Start()
End Sub
Private Sub ReadScanner()
Dim strRowCount As String
ShipmentMsg(2)
strRowCount = objShipping.RecordsExisit.ToString()
Try
objApex.ImmediateMode()
If objApex.FileDownload = False Then
Throw New Exception(Err.Description)
End If
Catch ex As Exception
ShipmentMsg(1)
MessageBox.Show("No Data downloaded from Scanner. Try Again. Error#: " & Err.Number & " : " & Err.Description)
Exit Sub
End Try
RecordCount()
DataGridUpdate()
btnProcessShipment.Enabled = True
ShipmentMsg(5)
ScanErrors()
End Sub
This all works great. As expected. The call to objApex.FileDownload in class cApex is where progress bar is to be updated from (actually in another function called from FileDownload). So here is the code there.
Try
GetHeaderRecord()
If Count <> 0 Then intTicks = Math.Round((100 / Count), 1)
For intcount As Integer = 1 To Count
Dim intLength As Integer = Length
Do While intLength > 0
literal = Chr(_serialPort.ReadChar.ToString)
If literal = ">" Then Exit Do
strRecord = strRecord & literal
intLength = intLength - 1
Loop
REF = strRecord.Substring(0, 16).TrimEnd
SKID = strRecord.Substring(16, 16).TrimEnd
REEL_BC = strRecord.Substring(32, 15).TrimEnd
ScanDate = strRecord.Substring(47, 8).TrimEnd
ScanDate = DateTime.ParseExact(ScanDate, "yyyyMMdd", Nothing).ToString("MM/dd/yyyy")
ScanTime = strRecord.Substring(55, 6).TrimEnd
ScanTime = DateTime.ParseExact(ScanTime, "HHmmss", Nothing).ToString("HH:mm:ss")
strRecordTotal = strRecordTotal & strRecord & CRLF
Dim strSQL As String
strSQL = "INSERT INTO tblScanData (PONo,Barcode,SkidNo,ScanDate,ScanTime) " & _
"VALUES (" & _
Chr(39) & REF & Chr(39) & _
"," & Chr(39) & REEL_BC & Chr(39) & _
"," & Chr(39) & SKID & Chr(39) & _
"," & Chr(39) & ScanDate & Chr(39) & _
"," & Chr(39) & ScanTime & Chr(39) & ")"
objData.Executecommand(strSQL)
strRecord = ""
Next
And finally this is how I was calling the progress bar update.
Dim f As frmMain = frmMain
System.Threading.Thread.Sleep(100)
DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep()
I really need to put the PerformStep in the For loop. Each time around the loop will step the progress bar the percentage of steps needed to make bar fairly accurate (done by the math code before loop). Also I setup the properties of the progress bar control on frmMain. So, am I crazy, or is there a way to accomplish this? I tried using a delegate; Me.Invoke(New MethodInvoker(AddressOf pbStep)) to make code cross thread safe. I don't get an error about cross thread calls, but the progress bar doesn't update either. Sorry it's a long one but I'm lost and my ADHD won't let me scrap this idea.
EDIT AS REQUESTED:
Public Sub pbStep()
Dim f As frmMain = frmMain
If Me.InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf pbStep))
Else
DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep()
System.Threading.Thread.Sleep(100)
End If
End Sub
Both responses helped lead me to the correct answer I was needing. The code provided by James was a great starting point to build on, and Hans has several post explaining the BackgroundWorker. I wanted to share the "Answer" I came up with. I'm not saying its the best way to do this, and I'm sure I'm violating some rules of common logic. Also, a lot of the code came from a MSDN example and James's code.
Lets start with the form from which I am calling the bgw, frmShipping. I added this code:
Dim WithEvents bgw1 As New System.ComponentModel.BackgroundWorker
Private Sub bgw1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bgw1.RunWorkerCompleted
If e.Error IsNot Nothing Then
MessageBox.Show("Error: " & e.Error.Message)
ElseIf e.Cancelled Then
MessageBox.Show("Process Canceled.")
Else
MessageBox.Show("Finished Process.")
End If
End Sub
Private Sub bgw1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles bgw1.ProgressChanged
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Maximum = 1960
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Step = 2
Dim state As cApex.CurrentState =
CType(e.UserState, cApex.CurrentState)
DirectCast(Me.MdiParent, frmMain).txtCount.Text = state.LinesCounted.ToString
DirectCast(Me.MdiParent, frmMain).txtPercent.Text = e.ProgressPercentage.ToString
DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.PerformStep()
End Sub
Private Sub bgw1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles bgw1.DoWork
Dim worker As System.ComponentModel.BackgroundWorker
worker = CType(sender, System.ComponentModel.BackgroundWorker)
Dim objApex As cApex = CType(e.Argument, cApex)
objApex.CountLines(worker, e)
End Sub
Sub StartThread()
Me.txtCount.Text = "0"
Dim objApex As New cApex
bgw1.WorkerReportsProgress = True
bgw1.RunWorkerAsync(objApex)
End Sub
Next I added the following code the my cApex class.
Public Class CurrentState
Public LinesCounted
End Class
Private LinesCounted As Integer = 0
Public Sub CountLines(ByVal worker As System.ComponentModel.BackgroundWorker, _
ByVal e As System.ComponentModel.DoWorkEventArgs)
Dim state As New CurrentState
Dim line = ""
Dim elaspedTime = 20
Dim lastReportDateTime = Now
Dim lineCount = File.ReadAllLines(My.Settings.strGenFilePath).Length
Dim percent = Math.Round(100 / lineCount, 2)
Dim totaldone As Double = 2
Using myStream As New StreamReader(My.Settings.strGenFilePath)
Do While Not myStream.EndOfStream
If worker.CancellationPending Then
e.Cancel = True
Exit Do
Else
line = myStream.ReadLine
LinesCounted += 1
totaldone += percent
If Now > lastReportDateTime.AddMilliseconds(elaspedTime) Then
state.LinesCounted = LinesCounted
worker.ReportProgress(totaldone, state)
lastReportDateTime = Now
End If
System.Threading.Thread.Sleep(2)
End If
Loop
state.LinesCounted = LinesCounted
worker.ReportProgress(totaldone, state)
End Using
End Sub
I also added a couple of text boxes to my main form to show the current line count from the file being read from and the overall progress as a percentage of a 100. Then on the Click event of my button I just call StartThread(). It is not 100% accurate, but its close enough to give the user a very good idea where the process stands. I have a little more work to do to add it to the "ReadScanner" function, where I originally was wanting to use the progress bar. But this function it the longer of the two that I perform on the scanner, writing almost 2,000 lines of code through a COMM Port. I'm happy with the results.
Thank you guys for helping out!
P.S. I have also now added variables to set the pbar.Maximum and the pbar.step since those can change if the scanner file is changed.
Background workers are useful for this purpose. Just use it in combination with a delegate. All the threaded work is done within the DoWork event of the worker. As progress is made, progress is reported within the DoWork event. This in turn fires the ProgressedChanged event of the worker class which is on the same thread as the progressbar. Once the DoWork has completed and is out of scope, the RunWorkerCompleted event is fired. This can be used to do inform the user that the task is complete, etc. Here is a working solution that I threw together. Just paste it behind an empty form and run.
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Threading
Public Class Form1
Private _progressBar As ProgressBar
Private _worker As BackgroundWorker
Sub New()
' This call is required by the designer.
InitializeComponent()
Initialize()
BindComponent()
End Sub
Private Sub Initialize()
_progressBar = New ProgressBar()
_progressBar.Dock = DockStyle.Fill
_worker = New BackgroundWorker()
_worker.WorkerReportsProgress = True
_worker.WorkerSupportsCancellation = True
Me.Controls.Add(_progressBar)
End Sub
Private Sub BindComponent()
AddHandler _worker.ProgressChanged, AddressOf _worker_ProgressChanged
AddHandler _worker.RunWorkerCompleted, AddressOf _worker_RunWorkerCompleted
AddHandler _worker.DoWork, AddressOf _worker_DoWork
AddHandler Me.Load, AddressOf Form1_Load
End Sub
Private Sub Form1_Load()
_worker.RunWorkerAsync()
End Sub
Private Sub _worker_ProgressChanged(ByVal o As Object, ByVal e As ProgressChangedEventArgs)
_progressBar.Increment(e.ProgressPercentage)
End Sub
Private Sub _worker_RunWorkerCompleted(ByVal o As Object, ByVal e As RunWorkerCompletedEventArgs)
End Sub
Private Sub _worker_DoWork(ByVal o As Object, ByVal e As DoWorkEventArgs)
Dim worker = DirectCast(o, BackgroundWorker)
Dim value = 10000
SetProgressMaximum(value)
For x As Integer = 0 To value
Thread.Sleep(100)
worker.ReportProgress(x)
Next
End Sub
Private Sub SetProgressMaximum(ByVal max As Integer)
If _progressBar.InvokeRequired Then
_progressBar.Invoke(Sub() SetProgressMaximum(max))
Else
_progressBar.Maximum = max
End If
End Sub
End Class

Setting A Progress Bar Based On Directory Copy in Visual Basic

I am using Visual Studio 2010 and coding in Visual Basic.
I have to display a progress bar while copying a directory.
I have never worked with a progress bar before and not sure where to start.
Here is the code I currently have.
If My.Computer.FileSystem.DirectoryExists(filePath & "IETMS\" & installFile) Then
frmWait.Show()
My.Computer.FileSystem.CopyDirectory(strFileName, filePath & "IETMS", True)
ListView1.Items.Clear()
testReload()
frmWait.Close()
Else
My.Computer.FileSystem.CreateDirectory(filePath & "IETMS\" & installFile)
frmWait.Show()
My.Computer.FileSystem.CopyDirectory(strFileName, filePath & "IETMS", True)
ListView1.Items.Clear()
testReload()
frmWait.Close()
End If
I am assuming that I need to calculate the size of the source folder and then monitor the destination folder size and set the progress bar max to the source folder size and set the value of the progress bar to the destination size, but I am not sure how to go about doing this.
You can count the files in the source directory and then every so often count the files in the destination directory. To count the files in all subdirectories you can use a recursive sub:
Private Sub CountFiles(InFolder As String, ByRef Result As Integer)
Result += IO.Directory.GetFiles(InFolder).Count
For Each f As String In IO.Directory.GetDirectories(InFolder)
CountFiles(f, Result)
Next
End Sub
To use this do
Dim FileCount as Integer = 0
CountFiles("C:\test", FileCount)
Messagebox.Show(FileCount.ToString)
Set the progressbar to the percentage value like pbProgress.Value = CInt(DestCount/SourceCount * 100).
Edit: Following up on your question: You should use for example a backgroundworker, or a task, or a thread, to perform the copy and then update the progressbar in a Timer.
For example you can create a sub that does the copying and then start the sub in a new task:
Private WithEvents tmrUpdatePBG As Timer
Private Sub StartCopy(SourceFolder As String, DestFolder As String)
'copy copy copy
CopyComplete()
End Sub
Private Sub CopyComplete()
tmrUpdatePBG.Stop()
End Sub
[...]
'Whereever you start the copy process
Dim ct As New Task(Sub() StartCopy("C:\source", "C:\dest"))
ct.Start()
tmrUpdatePBG = New Timer
tmrUpdatePBG.Interval = 1000
tmrUpdatePBG.Start()
tmrUpdatePGB would be the timer. In the tick event update the progressbar. It is started when the copying process starts and stops when the process is complete.
I ended up counting the files in the source folder and setting the progress bar max to that number. Then inside the timer I counted the files in the destination folder and set the progress bar value to that number. Then just closed the window I created with the progress bar after the copy was finished.
I also had an issue with the progress bar (not responding) so I put the CopyDirectory inside a BackgroundWorker.
Private Sub tmrWait_Tick(sender As System.Object, e As System.EventArgs) Handles tmrWait.Tick
Dim srcFile As String = strFileName & "\" & installFile
Dim srcDir As New System.IO.DirectoryInfo(srcFile)
Dim srcFolders, srcFiles As Integer
srcFolders = srcDir.GetDirectories.GetUpperBound(0) + 1
srcFiles = srcDir.GetFiles.GetUpperBound(0) + 1
pbInstall.Maximum = srcFolders.ToString()
Dim desFile As String = filePath & "IETMS\" & installFile & "\" & installFile
Dim desDir As New System.IO.DirectoryInfo(desFile)
Dim desFolders, desFiles As Integer
desFolders = desDir.GetDirectories.GetUpperBound(0) + 1
desFiles = desDir.GetFiles.GetUpperBound(0) + 1
pbInstall.Value = desFolders.ToString()
pbInstall.Refresh()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
System.Threading.Thread.Sleep(1000)
My.Computer.FileSystem.CopyDirectory(strFileName, filePath & "IETMS\" & installFile, True)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
frmMain.ListView1.Items.Clear()
frmMain.testReload()
Me.Close()
End Sub
Private Sub frmWait_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub