How can I count files in multiple listbox items - vb.net

The purpose...
How can I read how many files in multiple folders.
So within the program I'm supposed to add and remove folders that I wanna monitor. So I'm adding folders to a listbox. The listbox will eventually contain a few items that are paths like \\server\parent directory\directory
The issue
Now this all works, adding the specified paths as items to the listbox, but now I want to count files in all the folders that are in the listbox and output a number to a textbox.
I've figured out how to do this if I have a textbox that contains a single path;
Dim counter = My.Computer.FileSystem.GetFiles(tbchannel1.Text)
tbCount1.Text = ("" & CStr(counter.Count))
But I can't figure out how to twist this around to work with all the items in a listbox instead.
...And btw, this is going to happen at the press of a button. Eventuelly I'll hook up a timer that button.performclick
Thanks!

This should do it for you
Private Sub CountFilesButton_Click(sender As Object, e As EventArgs) Handles CountFilesButton.Click
Try
Dim fileTotal As Integer
For Each item As String In DirListBox.Items
fileTotal += My.Computer.FileSystem.GetFiles(item.ToString).Count
Next
FileCountLabel.Text = String.Format("File count: {0}", fileTotal.ToString)
Catch ex As Exception
MessageBox.Show(String.Concat("An error occurred ", ex.Message))
End Try
End Sub
It'll be up to you to validate the path exists and handle other errors.

Related

VB.NET multithreading functions

So I've been working on a program that will find all files from a directory and subdirectories and my application is "freezing" while executing because its single-threaded, but I don't know how to multithread a function with an argument in it. so the use is like getallfiles("C:/Folder") and after this it will add each file in a listbox using the following code.
Private Sub getallfiles(filelocation As String)
Try
For Each item As String In My.Computer.FileSystem.GetFiles(filelocation)
If Path.GetExtension(item) = My.Settings.scanfor & "filter" Then
Me.Invoke(Sub() ListBox1.Items.Add(item))
Me.Invoke(Sub() ListBox1.SelectedIndex = ListBox1.Items.Count - 1)
End If
Next
For Each folder As String In My.Computer.FileSystem.GetDirectories(filelocation)
Me.Invoke(Sub() getallfiles(folder))
Next
Catch ex As Exception
End Try
End Sub
and I'm trying to use Thread1 = New System.Threading.Thread(AddressOf getcache(item))
Error BC30577 'AddressOf' operand must be the name of a method
(without parentheses).
if you know any ways of fixing or doing this, I would be happy to hear your answer
Instead of using the vb file functions try the .net System.IO functions. With Directory.GetFiles("Path of Directory to search", "*.txt", SearchOption.AllDirectories) you can search with an extension filter as the second parameter and the last parameter will search all sub-directories. Don't add your items one by one to the listbox. That would be very slow because the listbox has to redraw on each iteration. The GetFiles function returns an array of Strings. You can add this all at once with the listbox items .AddRange Don't forget to add Imports System.IO at the top of the file.
https://msdn.microsoft.com/en-us/library/ms143316(v=vs.110).aspx
Dim FilesFromDir() As String = Directory.GetFiles("C:\Users\maryo\Documents\TextNotes", "*.txt", SearchOption.AllDirectories)
ListBox3.Items.AddRange(FilesFromDir)

How do I select specific variables based on checkbox state as I iterate through a For Each

I'm working on a project that requires I iterate through a list of controls on a tabpage to find all of the checkboxes. Then depending on the state of the box (checked or unchecked) select individual variables (filenames) to then perform either a batch rename or delete of files on the filesystem (cb.checked = perform action).
I have managed to create the "for each" for the iteration of the controls (thanks google) but I'm struggling to figure out how to pick the variables. They are all named differently, obviously, as are the checkboxes. Also the checkboxes are statically assigned to the form/tabpage. Here's what I have at the moment.
Public Sub delBut_code(ByRef fname As String)
If (Sanity = 1) Then
For Each cb As Control In Form1.Controls
If TypeOf cb Is CheckBox AndAlso DirectCast(cb,
CheckBox).Checked Then
If My.Computer.FileSystem.FileExists(fname) Then
My.Computer.FileSystem.DeleteFile(fname)
End If
End If
Next
MessageBox.Show("All Actions Completed Successfully")
Else
MessageBox.Show("Please select a File To Delete")
End If
End Sub
and here is an example of some of the variables:
Dim castle As String = selPath & "\zm_castle_loadingmovie.txt"
Dim factory As String = selPath &
"\zm_factory_load_factoryloadingmovie.txt"
Dim island As String = selPath & "\zm_island_loadingmovie.txt"
N.B selpath collects a user entered folder path and can be ignored here
I would really appreciate any pointers.
First, you can do much better with the loop:
Public Sub delBut_code(ByRef fname As String)
If Sanity <> 1 Then
MessageBox.Show("Please select a File To Delete")
Exit Sub
End If
Dim checked = Form1.Controls.OfType(Of CheckBox)().Where(Function(c) c.Checked)
For Each box As CheckBox in checked
Try
'A file not existing is only one reason among many this could fail,
' so it needs to be in a Try/Catch block.
' And once you're using a Try/Catch block anyway,
' the FileExists() check becomes a slow and unnecessary extra trip to the disk.
My.Computer.FileSystem.DeleteFile(fname)
Catch
'Do something here to let the user know it failed for this file
End Try
Next
MessageBox.Show("All Actions Completed")
End Sub
But now you need to know how have the right value in that fname variable. There's not enough information in the question for us to fully answer this, but we can give some suggestions. There a number of ways you could do this:
Set the Tag property in the Checkboxes when you build the string variables. Then fname becomes DirectCast(box.Tag, String).
Inherit a custom control from CheckBox to use instead of a normal Checkbox that has an additional String property for the file name. Set this property when you build the string variables.
Name your string variables in a way that you can derive the string variable name from the CheckBox variable name, and then use a Switch to pick the right string variable from each box.Name.
Keep a Dictionary(Of CheckBox, String) that maps the Checkboxes to the right string values.
But without knowing more context of the application, I hesitate to recommend any of these over the others as best for your situation.

Using Filewatcher for a progress bar with subdirectories, Wont Update Properly

I'm trying to copy Files from a local Computer to a Network device. I'm trying to get a Progress Bar working for the File copy, and got it working for a Single Directory with no Subdirectory:
Private Sub CopyPictures()
Try
If Not Directory.Exists(DestinationPath) Then
My.Computer.FileSystem.CreateDirectory(DestinationPath)
End If
Dim counterLocalFiles = My.Computer.FileSystem.GetFiles(SourcePath)
UpdateProgressBarMaximum1(CInt(counterLocalFiles.Count))
UpdateLabelText2(CStr(counterLocalFiles.Count)) 'is a label which shows copied X files of Label2 Files
fsw1 = New IO.FileSystemWatcher(DestinationPath)
fsw1.EnableRaisingEvents = True
My.Computer.FileSystem.CopyDirectory(SourcePath, DestinationPath)
GetSettingsFromFile()
Catch Exec As System.IO.IOException
Dim dr As DialogResult = MessageBox.Show("Some Random Error Code", "Exception Title", MessageBoxButtons.OKCancel)
If (Not DialogResult.OK = dr) Then
Exit Sub
Return
End If
End Try
End Sub
Private Sub fsw1_Created(sender As Object, e As FileSystemEventArgs) Handles fsw1.Created
Dim counterRemoteFiles = My.Computer.FileSystem.GetFiles(DestinationPath)
UpdateProgressBar1(CInt(counterRemoteFiles.Count))
UpdateLabelText1(CStr(counterRemoteFiles.Count))
End Sub
The Update ObjectX Subs are just invoke Functions since the CopyPictures is raised by a backgroundworker as well looking all like this one for example
Private Sub UpdateProgressBar1(Value As Int32)
If ProgressBar1.InvokeRequired Then
ProgressBar1.Invoke(New Action(Of Integer)(AddressOf UpdateProgressBar1), Value)
Else
'We are on the UI thread so update the control.
ProgressBar1.Value = Value
End If
End Sub
This code works perfectly fine for me, but I have to deal with SubDirectories which contain the Images, and the names of the subs are random so i cant predetermine them so I came up with slight changes:
The Counter is looking now like this:
Dim counterLocalFiles = System.IO.Directory.GetFiles(SourcePath, "*.jpg*", SearchOption.AllDirectories).Length
UpdateProgressBarMaximum1(CInt(counterLocalFiles))
UpdateLabelText2(CStr(counterLocalFiles))
And this:
Dim counterRemoteFiles = IO.Directory.GetFiles(DestinationPath, "*.jpg", SearchOption.AllDirectories).Length
UpdateProgressBar1(CInt(counterRemoteFiles))
UpdateLabelText1(CStr(counterRemoteFiles))
And I added:
fsw1.IncludeSubdirectories = True
Now the weired Problems started: It would properly count the file in the source Directory setting label2 to the correct amount of files in all subdirectories and then start copying. It would NOT update the Progressbar though in real time. It just updated it once when it was done with the first directory and just adding the amount of files to it which it contained. After that it completly stoppedd nored the second directory and didn't add that at all to the progressbar. What am I doing wrong here? I hope my english is fine, If you have any question or If I was not clear enough, please let me know. Thank you
You don't have an event consumer that triggers your progressbar update routine - you call it once when your filesystemwatcher is instantiated.
You need to declare an event that handles the copy event and fires off your progress update code. Because Filesystemwatcher cannot monitor network drives, you may want to declare an event that fires off your progress update method when the counterRemoteFiles count increments.
Turns out I just made a mistake with correctly putting the
fsw1.IncludeSubdirectories = True
I was setting it to true in the Form Editor instead of doing it in the code. Once i actually put that in the code after initialising the fsw, it would work just fine

How to remove a Specific Button from Flow Layout Panel

I am creating dynamic buttons, each has a unique Tag, each tag is then sent to a list box. i want to be able to select the tag from the list box and click a button to remove the button with that specific tag i tried making this code but no go....
Private Sub cmdRemove_Click(sender As Object, e As EventArgs) Handles cmdRemove.Click
Try
Dim curItem As String = RmvList.SelectedItem.ToString()
MsgBox(curItem)
For Each Button As System.Windows.Forms.Control In Main.FloLay.Controls
If Button.Tag = curItem Then
Main.FloLay.Controls.RemoveAt(CurItem)
End If
Next
Catch ex1 As Exception
End Try
End Sub
How can i Achieve this Feat? Thank you In Advance.
RemoveAt takes a number as parameter that is the index in the collection.
You have to do two things:
Use Remove instead of RemoveAt
Exit the for loop
So the code:
For Each Button As System.Windows.Forms.Control In Main.FloLay.Controls
If Button.Tag = curItem Then
Main.FloLay.Controls.Remove(Button)
Exit For
End If
Next
The first change is obvious, the second change is needed because you alter the enumerator when you remove the button and it would throw an exception if you try to continue the for loop after the deletion.
If you want to delete multiple items you can use the following trick:
For i = Main.FloLay.Controls.Count -1 To 0 Step -1
If Main.FloLay.Controls(i).Tag = curItem Then
Main.FloLay.Controls.RemoveAt(i) 'Here you actually provide the index!
End If
Next
This will work because you don't alter any index yet to come be deleting an item if you move backwards through the collection.

Recursive search for all folders and subfolders in system

I wrote a Windows Forms script that searched for all non-hidden and non-readonly folders in my system. But the script itself, when run initially, runs for like 5 minutes. Subsequent opens take much less time. I was wondering if there is a logical error to it, so as to why its running so very slow.
Private Function FindSubFolders(ByVal dir As DirectoryInfo, ByVal node As TreeNode) As TreeNode
Dim subnode As New TreeNode
For Each folder As DirectoryInfo In dir.GetDirectories()
If (folder.Attributes And FileAttributes.Hidden) <> FileAttributes.Hidden Then
subnode = node.Nodes.Add(folder.FullName, folder.Name)
subnode = FindSubFolders(folder, subnode)
End If
Next
Return subnode
End Function
Private Sub SetFolders_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Is it possible to load this on 1st (initial) form load???
Try
Dim node As TreeNode
If TreeView1.Nodes.Count < 1 Then
For Each drive As String In Directory.GetLogicalDrives
Directory.GetLogicalDrives()
Dim folders As DirectoryInfo = New DirectoryInfo(drive)
If (folders.Attributes And FileAttributes.ReadOnly) <> FileAttributes.ReadOnly Then
node = TreeView1.Nodes.Add(drive, drive)
Try
node = FindSubFolders(folders, node)
Catch ex As Exception
Console.WriteLine(ex.Message)
Continue For
End Try
End If
Next
End If
If Not IsNothing(My.Settings.Folders) Then
If ListBox1.Items.Count < 1 Then
For Each col As String In My.Settings.Folders
ListBox1.Items.Add(col)
Next
End If
Else
My.Settings.Folders = New StringCollection
End If
Catch ex As Exception
Logs.Add("04", ex.Message)
End Try
Logs.Add("01", "Loaded.")
End Sub
Thanks for the help! :)
Here are a few tips:
One thing you can do to speed things up is to make sure the TreeView-control does not have to repaint itself every time you add an item to it.
Before adding any item, run Treeview1.BeginUpdate and after you have added all items run Treeview1.EndUpdate
If possible, get the directories as an array, and use the node.addrange to add a whole range of directiryes at once.
From MSDN:
To maintain performance while items
are added one at a time to the
TreeView, call the BeginUpdate method.
The BeginUpdate method prevents the
control from painting until the
EndUpdate method is called. The
preferred way to add items to a tree
view control is to use the AddRange
method to add an array of tree node
items to a tree view. However, if you
want to add items one at a time, use
the BeginUpdate method to prevent the
TreeView control from painting during
the add operations. To allow the
control to resume painting, call the
EndUpdate method when all the tree
nodes have been added to the tree
view.
Check out this question for a (maybe) more easy way to fetch the subfolders:
Get all folder / directories list in VB.net