Get files one at a time from a folder in VB.NET - vb.net

I am trying to read some text files path in a folder sequentially. However, I get only the first file.
I need to get the first file, execute a timer, get the next file path, execute a timer right up to the last file in the folder, and stop. How can I get around this?
Private zMailbox As String = "c:\Fold\"
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles Button1.Click
Timer1.Start()
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
Dim finfo As New IO.DirectoryInfo(zMailbox)
For Each fi In finfo.GetFiles("*.txt")
TextBox1.Text = fi.FullName
Next
End Sub
Thanks to the contributions below I got the code to work with the text box value. However, it gives the index count instead of the path which I want to retrieve.
Private zMailbox As String = "c:\Fold\"
Dim files As FileInfo()
Dim index As Integer = 0
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
Handles Button1.Click
Dim finfo As New IO.DirectoryInfo(zMailbox)
files = finfo.GetFiles("*.txt")
Timer1.Start()
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Timer1.Tick
If index >= files.Length Then
index = 0
End If
TextBox1.Text = (ListBox1.Items.Add(files(index)))
index += 1
End Sub

Your code loads all the files in the Timer event and assign them to the TextBox1.Text property inside the loop. Every loop overwrites the data that has been written in the previous loop.
At the end of the loop you see only the last value.
To show sequentially the files inside the Timer Tick event, you need to read the directory content before starting the Timer in a global FileInfo array. Another global variable will be used as indexer to show a particular file from this FileInfo array in your Timer.Tick event.
The index will be incremented and, at the next Tick, you could show the next file
Dim files as FileInfo()
Dim index As Integer = 0
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
Dim finfo As New IO.DirectoryInfo(zMailbox)
files = finfo.GetFiles("*.txt")
Timer1.Start()
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
if index >= files.Length Then
index = 0
End If
TextBox1.Text = files(index)
index += 1
End Sub
EDIT
According to your comment, you need to set the MultiLine property of the TextBox to true (using the form designer) and then, at every Tick, instead of replacing the Text property, append to it
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
if index >= files.Length Then
return ' Reached the end of the array. Stop the Timer???
End If
TextBox1.AppendText(files(index) & Environment.NewLine)
index += 1
End Sub
As a side note, if you want to show all file names together then it is not clear why you need a timer at all.
You could get the same result with code like this
Dim finfo As New IO.DirectoryInfo(zMailbox)
Dim files = finfo.EnumerateFiles("*.txt")
TextBox1.Text = string.Join(Environment.NewLine, files.Select(Function(x) x.FullName).ToArray())

On the original code you posted you where getting all files in the for loop each time the timer clicks.
After reading steve answer, and your comments, probably you always got all the files, but you override the textbox.text value.
TextBox1.Text += < String > & vbNewLine
Where < String >, of course, is the string returned by DirectoryInfo.GetFiles()
I think steve answer works just fine, but you are not implementing it well.

I would try and make this as easy as possible for you. You Microsoft's Reactive Framework for this. Just NuGet "Rx-Main".
Here's what you can then do:
finfo.GetFiles("*.txt").ToObservable() _
.Zip(Observable.Interval(TimeSpan.FromSeconds(1.0)), Function(f, _) f.Name) _
.ObserveOn(TextBox1) _
.Subscribe(Function(n) textbox_text += n + Environment.NewLine)
That's it. No timers. No separate methods. No need for module-level variables. Just one line of code and you're done.
It's processed on a background thread and then marshalled back to the UI via the .ObserveOn(TextBox1) call.
You can even keep a reference to the IDisposable returned by the .Subscribe(...) call to terminate the observable (timer) early.
Simple.

This seems a bit Rube Goldberg-ish. Just get all the files and loop through them in your Button_Click method:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim finfo As New IO.DirectoryInfo(zMailbox)
For Each fi In finfo.GetFiles("*.txt")
TextBox1.Text = fi.FullName
Next
End Sub

Related

Save changes made to file names in folder. All 762 of them

I downloaded a pack of roms for a SNES emulator and when I went to transfer them over I received an error on the illegal file names. So I started coding a simple app that will remove the problem char from the roms title. I got that part figured out however I cannot figure out how to save the edited file names. Keep in mind there are 762 roms in the folder so manually doing this is not an option. Thanks in advance for any guidance.
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ListBox1.Items.AddRange(IO.Directory.GetFiles("c:\ROMS"))
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Index As Integer = ListBox1.SelectedIndex
Dim Results = (From T In ListBox1.Items
Select System.Text.RegularExpressions.Regex.Replace(CStr(T),
"[""]", String.Empty).Replace("[!]", "")
).ToArray
ListBox1.Items.Clear()
ListBox1.Items.AddRange(Results)
If Index <> -1 Then
ListBox1.SelectedIndex = Index
End If
End Sub
End Class
I am using button1 to open the folder into a listbox.
Button 2 removes the unwanted [!] from the title
Button 3 would be where the save feature is.
####### EDIT #####
I figured out what the real problem was for my particular case.
I was transferring the folder of roms over to my Xbox and apparently the is a character limit not so much a restriction on chars used. Dont know the actual allowed amount of chars to be used but for anyone who may be trying to do what I did just use my code to remove unnecessary chars in the title.
Add another Listbox to your form called Listbox2 then use this code below. Also notice in Button2 the use of GetDirectoryName and GetFileName so that you don't replace any chars in the folder name...only the filename
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ListBox1.Items.AddRange(IO.Directory.GetFiles("c:\ROMS"))
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Index As Integer = ListBox1.SelectedIndex
Dim Results = (From T In ListBox1.Items
Select IO.Path.GetDirectoryName(CStr(T)) & "\" & System.Text.RegularExpressions.Regex.Replace(IO.Path.GetFileName(CStr(T)),
"[""]", String.Empty).Replace("[!]", "")
).ToArray
ListBox2.Items.Clear()
ListBox2.Items.AddRange(Results)
If Index <> -1 Then
ListBox1.SelectedIndex = Index
ListBox2.SelectedIndex = Index
End If
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
For x As Integer = 0 To Me.ListBox1.Items.Count - 1
IO.File.Move(Me.ListBox1.Items(x), Me.ListBox2.Items(x))
Next
MsgBox("Done!")
End Sub
End Class

It maybe inaccessible due to its protection level

I was trying to code a download manager but get this error:
'save' is not declared. It maybe inaccessible due to its protection level
Here is my code:
Imports System.Net
Public Class Form1
Private WithEvents httpclient As webClient
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
ListBox1.Items.Add(TextBox1.Text)
TextBox1.Clear()
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
SaveFileDialog1.ShowDialog()
End Sub
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
Try
httpclient = New WebClient
Dim total As Integer = ListBox1.Items.Count
Dim current As Integer = -1
While current < total
ListBox1.SelectedIndex = current + 1
Dim download As String = ListBox1.SelectedItem
httpclient.DownloadFileAsync(New Uri(download), save)
Label3.Text = "Current Status: Downloading:..."
Do While httpclient.IsBusy
Application.DoEvents()
Loop
current = current + 1
End While
Catch ex As Exception
ListBox1.Items.Clear()
End Try
End Sub
End Class
DownloadFileAsync takes 2 parameters. The first is the URL of the file to download and the second is the local location to save the file. The tutorial you were following likely set the variable Save somewhere in the code.
To get this to work, you can replace it with something like "C:\Test.txt" to download it to that location or define the variable Save and set it's value to the location you want to save the file.
With your existing code, to allow a user to select the save location, you need to make the following changes. Declare the Save variable private to the form, under the Public Class Form1 line:
Private Save as string
Then in your Button1_Click event, it should look more like this:
If SaveFileDialog1.ShowDialog() = DialogResult.OK Then
Save = SaveFileDialog1.FileName
End If
Dim download As String = ListBox1.SelectedItem
httpclient.DownloadFileAsync(New Uri(download), "save")

Converting drag and drop application that shows directory into showing hash

I am having trouble converting this code into allowing me to drop files into my application and my application create a message box saying its md5 hash code. Currently, I have the code to give the directory of the file I dropped in.
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.AllowDrop = True
End Sub
Private Sub Form1_DragDrop(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragDrop
Dim files() As String = e.Data.GetData(DataFormats.FileDrop)
For Each path In files
MsgBox(path)
Next
End Sub
Private Sub Form1_DragEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragEnter
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
e.Effect = DragDropEffects.Copy
End If
End Sub
End Class
Seems like you have most of the code you need, only needs a function to calculate the md5, and then you can plug that into your message box:
MsgBox(ComputeMD5Hash(path))
Here is the function, taken mostly from this Microsoft Support Article
Private Function ComputeMD5Hash(ByVal path As String) As String
Dim tmpSource() As Byte
Dim tmpHash() As Byte
'Create a byte array from source data.
tmpSource = My.Computer.FileSystem.ReadAllBytes(path)
'Compute hash based on source data.
tmpHash = New Security.Cryptography.MD5CryptoServiceProvider().ComputeHash(tmpSource)
Dim i As Integer
Dim sOutput As New System.Text.StringBuilder(tmpHash.Length)
For i = 0 To tmpHash.Length - 1
sOutput.Append(tmpHash(i).ToString("X2"))
Next
Return sOutput.ToString()
End Function
In-case you want to drop a directory and get all the hashes of the files inside you first need to check if you're dealing with a file or a folder, if its a folder you loop over all the files inside and call the function for each one, which would make your DragDrop event handler look something like this, and have the added benefit both files and folders can be taken care of.
Private Sub Form1_DragDrop(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Me.DragDrop
Dim files() As String = e.Data.GetData(DataFormats.FileDrop)
For Each path In files
If IO.File.GetAttributes(path) = FileAttribute.Directory Then
'Dealing with a Directory
Dim di As New IO.DirectoryInfo(path)
Dim fiArr As IO.FileInfo() = di.GetFiles()
Dim fri As IO.FileInfo
For Each fri In fiArr
MessageBox.Show(fri.FullName & " " & ComputeMD5Hash(fri.FullName))
Next fri
Else
'Dealing with a File
MessageBox.Show(path & " " & ComputeMD5Hash(path))
End If
Next
End Sub

How can I do something after Process.Start("explorer.exe", OpenFolder)

I have the following code that simply opens up a folder with txt files.
Private Sub OpenTabpageTextFolderToolStripMenuItem_Click( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles OpenTabpageTextFolderToolStripMenuItem.Click
Dim OpenFolder = (RootDrive & "QuickEmailer2\TabTxt")
Process.Start("explorer.exe", OpenFolder)
End Sub
The user then edits a txt file, and closes.
I would like to call my refresh code and make use of the changes to the txt file, but if I put the call after process.start, it runs without waiting?
I could use code to do these edit, but there are 80 files to choose from and they only need edit them once (or twice) when setting up the program for the first time.
I am sure a bit of code that says:
Private Sub OpenTabpageTextFolderToolStripMenuItem_Click( _
ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles OpenTabpageTextFolderToolStripMenuItem.Click
Dim OpenFolder = (RootDrive & "QuickEmailer2\TabTxt")
Process.Start("explorer.exe", OpenFolder)
'**I will hang on here while you do your stuff, then I will continue...**
Call RefreshfromAllTxtFiles()
End Sub
alternative solution: use 2 buttons/steps to setting up the program for the first time!
button/step #1: open setup files
button/step #2: setup files modified... PROCEED!
Along the lines of Steve's comment, you can use a FileSystemWatcher to monitor changes to the directory. Something like this can get you started:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim fsw As FileSystemWatcher
fsw = New System.IO.FileSystemWatcher()
'this is the folder we want to monitor
fsw.Path = "c:\temp"
fsw.NotifyFilter = IO.NotifyFilters.Attributes
AddHandler fsw.Changed, AddressOf IveBeenChanged
fsw.EnableRaisingEvents = True
End Sub
Private Sub IveBeenChanged(ByVal source As Object, ByVal e As System.IO.FileSystemEventArgs)
If e.ChangeType = IO.WatcherChangeTypes.Changed Then
'this displays the file that changed after it is saved
MessageBox.Show("File " & e.FullPath & " has been modified")
' you can call RefreshfromAllTxtFiles() here
End If
End Sub

VB.Net - Updating progress bar from background worker

I am trying to build a log parser in VB.Net that will take IIS logs and insert them into a database. Having never really made a full out desktop application, I'm hitting a number of stumbling blocks, so please forgive me if my questions a very uninformed; I'm learning to walk while running.
The code that I'm working with looks like this:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim logfile = "C:\ex111124.log"
Dim FileLength As Long = New System.IO.FileInfo(logfile).Length
logFileLabel.Text = logfile
Dim objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
OngoingLog.AppendText(objReader.ReadLine)
'BackgroundWorker1.ReportProgress(e.percentProgress)
Loop()
objReader.Close()
objReader = Nothing
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'Me.crunchingProgress.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Close()
End Sub
So the function works, when this window is opened it starts to read the log file and updates a textbox with all of the rows currently read, but I also want it to update a progress bar in my main thread called crunchingProgress.
Any help would be greatly appreciated.
This should do the trick:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Invoke(Sub()
Me.crunchingProgress.Value = e.ProgressPercentage
End Sub)
End Sub
You don't set the BackgroundWorker to report progress (WorkerReportsProgress)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
Now your BackgroundWorker1_ProgressChanged will be called in the context of the UI Thread and you can set the progressbar value
Of course, when you call ReportProgress to raise the ProgressChanged event, you need to pass the percentage of work done so far.
Using objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
Dim line = objReader.ReadLine()
OngoingLog.AppendText(line)
Dim pct = Convert.ToInt32((100 * line.Length) / FileLength )
BackgroundWorker1.ReportProgress(pct)
Loop
End Using