Adding sub folders and files to menu strip - vb.net

Basically, I'm trying to populate a menu strip with a list of directories from a static location and then nested any sub directories underneath its parent and then display the files in the folder
I've got the basic
Dim loc As String = "C:\test\"
For Each dirs As String In Directory.GetDirectories(loc)
Dim dirinfo As New DirectoryInfo(dirs)
p1.DropDownItems.Add(dirinfo.Name)
Next
Which gives me the first line of directories but just not to sure where to go from here. Ultimately I would then like to be able to open the files form here.
Can anyone point me in the right direction
Thanks

This works for me, though it seems to only go four levels deep
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' assuming you have a single top level menu item named p1
Dim loc As String = "C:\test\"
AddSubDirsToMenuRecursive(p1, loc)
End Sub
Private Sub AddSubDirsToMenuRecursive(m As ToolStripMenuItem, d As String)
Dim dirs = Directory.GetDirectories(d)
For Each dir As String In dirs
Dim dirinfo As New DirectoryInfo(dir)
Dim mi As New ToolStripMenuItem(dirinfo.Name)
m.DropDownItems.Add(mi)
m.DropDownItems.AddRange(
dirinfo.GetFiles.Select(Of ToolStripMenuItem) _
(
Function(fi As FileInfo) New ToolStripMenuItem(fi.Name)
).ToArray())
AddSubDirsToMenuRecursive(mi, dirinfo.FullName)
Next
End Sub
If you are interested in opening the file when clicking it, replace the m.DropDownItems.AddRange block with this
m.DropDownItems.AddRange(
dirinfo.GetFiles.Select(Of ToolStripMenuItem) _
(Function(fi As FileInfo)
Dim mi1 = New ToolStripMenuItem(fi.Name)
AddHandler mi1.Click,
Sub(sender As Object, e As EventArgs)
Process.Start(fi.FullName)
End Sub
Return mi1
End Function
).ToArray())

Related

Multiple Selections from Listbox to rename Folders

Have a vb.net userform with a listbox set to multiselect. This is for personal use. The listbox populates itself when the form loads with all the subfolders, by name only, in a designated folder. I want to append a prefix to each selected folder in the listbox.
By default, all folders would have the prefix, let's say X. So, for example, Xfolder1 becomes folder1 if selected and the submit button is pressed (not on listbox change).
Here is my code and pseudocode so far. Getting string errors. Only the first sub, loading the form and populating the list works. Many thanks for any help. Health and safety to all during this pandemic.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each folder As String In System.IO.Directory.GetDirectories("D:\TestFolder\")
ListBox1.Items.Add(Path.GetFileName(folder))
Next
End Sub
This is ofc pseudocode
Private Sub RenameFolders(sender As Object, e As EventArgs) Handles Button1.Click
For i = 0 To ListBox1.Items.Count - 1
If Prefix Exists Then
FileIO.FileSystem.RenameDirectory(Prefix & FolderName, FolderName)
Else
FileIO.FileSystem.RenameDirectory(FolderName, Prefix & FolderName)
End If
Next
End Sub
What is above and lots of research. Issue might be with whether my strings are the full path or just the folder name. Listbox returns folder names, but is that what the code returns? Very confused.
Hi. Sorry for the lack of clarity. Because the listbox is populated on load, it will show the current state of the folders. This could be Xfolder1, folder2, xfolder3 etc. It will be the folder names as they currently exist.
Another way to look at it.
Selecting folders will remove any prefix from all selected when submit is hit.
Not selecting folders will add the prefix to all non-selected when submit is hit.
If xfolder1 appears in the listbox and is selected, it becomes folder1.
If xfolder1 appears in the listbox and is NOT selected, it remains xfolder1.
If folder1 appears in the listbox and it is selected, it remains folder1.
If folder1 appears in the listbox but is NOT selected, it changes to xfolder1
I hope that makes more sense?
Assuming that I'm understanding your question properly now, I would suggest that you use the DirectoryInfo class. You can create one for the parent folder and then get an array for the subfolders. You can then bind that array to the ListBox and display the Name property, which is just the folder name, while still having access to the FullName property, which is the full path. It also has an Exists property and a MoveTo method for renaming. Here is how I would do what you're asking for:
Imports System.IO
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim folderPath = Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "Test")
Dim folder As New DirectoryInfo(folderPath)
Dim subFolders = folder.GetDirectories()
With ListBox1
.DisplayMember = "Name"
.DataSource = subFolders
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Const prefix As String = "X"
For Each selectedSubFolder As DirectoryInfo In ListBox1.SelectedItems
'This is required to update the Exists property if the folder has been deleted since loading.
selectedSubFolder.Refresh()
If selectedSubFolder.Exists Then
Dim parentFolderPath = selectedSubFolder.Parent.FullName
Dim folderName = selectedSubFolder.Name
If folderName.StartsWith(prefix) Then
folderName = folderName.Substring(prefix.Length)
Else
folderName = prefix & folderName
End If
Dim folderPath = Path.Combine(parentFolderPath, folderName)
selectedSubFolder.MoveTo(folderPath)
End If
Next
End Sub
End Class
Note that this doesn't update the ListBox as it is. If you want that too, here's how I would do it with the addition of a BindingSource:
Imports System.IO
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim folderPath = Path.Combine(My.Computer.FileSystem.SpecialDirectories.MyDocuments, "Test")
Dim folder As New DirectoryInfo(folderPath)
Dim subFolders = folder.GetDirectories()
BindingSource1.DataSource = subFolders
With ListBox1
.DisplayMember = "Name"
.DataSource = BindingSource1
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Const prefix As String = "X"
For Each selectedSubFolder As DirectoryInfo In ListBox1.SelectedItems
'This is required to update the Exists property if the folder has been deleted since loading.
selectedSubFolder.Refresh()
If selectedSubFolder.Exists Then
Dim parentFolderPath = selectedSubFolder.Parent.FullName
Dim folderName = selectedSubFolder.Name
If folderName.StartsWith(prefix) Then
folderName = folderName.Substring(prefix.Length)
Else
folderName = prefix & folderName
End If
Dim folderPath = Path.Combine(parentFolderPath, folderName)
selectedSubFolder.MoveTo(folderPath)
End If
Next
BindingSource1.ResetBindings(False)
End Sub
End Class
Note that this will still not resort the data but I'll leave that to you if you want it. The reason is that you could do a simple sort of the DirectoryInfo array on Name but that will do a straight alphabetic sort, which the ListBox can already do. If you want a logical sort like File Explorer does, where actual numbers in folder names are sorted numerically, then you need to use a Windows APi function too, which is beyond the scope of this question. If you want that, see here for more information.

Is there a way to extract a variable from a subroutine in vb .net? or how do I declare custom event handlers and trigger them in a linked fashion?

I am trying to build this file copy utility in VB.Net, and I have this window:
The current folder button opens up a folder browser dialog, and I save the selected path from the dialog to a string variable which I then pass to a function. The function adds all files and directories present in that folder to the current folder listbox.
Now I need this filepath for the all files checkbox, which when triggered lists all the subdirectories and their contents in the currentfolder listbox.
Is there any way I can extract that filepath variable dynamically without hardcoding it? Or can I create custom event handlers and trigger them inside the current folder button handler?
Here is my code:
Public Class Form1
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim FB As FolderBrowserDialog = New FolderBrowserDialog()
Dim srcpath As String
Dim flag As Integer = 1
FB.ShowDialog()
FB.ShowNewFolderButton = True
If (DialogResult.OK) Then
srcpath = FB.SelectedPath()
End If
listfiles(srcpath)
End Sub
Public Function listfiles(srcpath As String)
Dim dir As DirectoryInfo = New DirectoryInfo(srcpath)
Dim dirs As DirectoryInfo() = dir.GetDirectories()
Dim d As DirectoryInfo
Dim files As FileInfo() = dir.GetFiles()
Dim file As FileInfo
For Each file In files
CurrentFolderListBox.Items.Add(file.Name)
Next
For Each d In dirs
CurrentFolderListBox.Items.Add(d)
Next
'If CheckBox1.Checked = True Then
' CheckBox1_CheckedChanged(sender, New System.EventArgs())
'End If
End Function
Public Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChange
Dim item As DirectoryInfo
For Each i As DirectoryInfo In CurrentFolderListBox.Items
item = i
Next
End Sub
Any help would be most appreciated.
Well, for the list side lbox, and the right side lbox?
Why not fill each list box with a data source? You can have 1, or 2, or even 5 columns of data. The ListBox can display TWO of the columns.
So, VERY often a list box will have two values. the "value" based on what you select (often a PK database row value), and then you have the "text" value for display.
from FoxPro, ms-access, vb.net, and even asp.net?
A list box traditional has had the ability to "store" a set of values for your selection.
So, why not just put out the file list to a on the fly "data structure". You can quite much use a struct, a class or whatever.
However, might as well use a data table, since listbox supports "binding" to a table.
So, in the ListBox settings, you have these two settings:
so above is our "display"
And then set the "value" to the FULL file name like this:
So now we can say create a form like this:
So, our code to select the "from folder" can look like this:
Private Sub cmdSelFrom_Click(sender As Object, e As EventArgs) Handles cmdSelFrom.Click
Dim f As New FolderBrowserDialog
If f.ShowDialog = DialogResult.OK Then
txtFromFolder.Text = f.SelectedPath
ListBox1.DataSource = GetFileData(txtFromFolder.Text)
End If
End Sub
Public Function GetFileData(sFolder As String) As DataTable
Dim rstData As New DataTable
rstData.Columns.Add("FullFile", GetType(String))
rstData.Columns.Add("FileName", GetType(String))
' get all files from this folder
Dim folder As New DirectoryInfo(sFolder)
Dim fList() As FileInfo = folder.GetFiles
For Each MyFile As FileInfo In fList
Dim OneRow As DataRow = rstData.NewRow
OneRow("FullFile") = MyFile.FullName
OneRow("FileName") = MyFile.Name
rstData.Rows.Add(OneRow)
Next
Return rstData
End Function
so, we setup a two column "thing" (in this case a data table).
We fill it with our two values (FileName and FullFile).
So, we now have this:
I have selected two files on the left side, and thus we get this:
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
For Each MySel As DataRowView In ListBox1.SelectedItems
Debug.Print(MySel.Item("FileName"))
Debug.Print(MySel.Item("FullFile"))
Next
End Sub
OutPut:
a.pdf
C:\Test2\a.pdf
b.pdf
C:\Test2\b.pdf
We could even include say file size in that table. But the "basic" concept here is that we can store + save data items into the list box, and that REALLY makes the code simple, since the list box now has JUST the file for display, but also enables us to have the full path name also.

vb.net. play file from selected item

im make a music box from an old pc.
Im trying to get the selected song from listbox1 to play when hitting the play button. All songs have a genre category. Form 1 has buttons to choose which genre and when clicked it opens another form with a listbox. For example on form 2(the genre for this form is country), The listbox is set to search in "C:/newfiles/country/" And any songs in the folder are shown on the list box.
The problem is im trying to get the program to play the selected song from the listbox without having to set each song's directory.
The reason im trying to make the program this way is so that i can just add/delete songs from a directory and not have to set/delete directories from the program.
Code:
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles Me.Load
' make a reference to a directory
Dim di As New IO.DirectoryInfo("c:\newfiles\Country\")
Dim diar1 As IO.FileInfo() = di.GetFiles()
Dim dra As IO.FileInfo
'list the names of all files in the specified directory
For Each dra In diar1
ListBox1.Items.Add(dra)
Next
End Sub
Private Sub backButton1_Click(sender As Object, e As EventArgs) Handles backButton1.Click
Me.Close()
Form1.Show()
End Sub
Private Sub PlayButton1_Click(sender As Object, e As EventArgs) Handles PlayButton1.Click
?
End Sub
Private Function AxWindowsMediaPlayer1() As Object
Throw New NotImplementedException
End Function
End Class
From my comments, declare a variable for your file path:
Private musicPath As String = "c:\newfiles\Country"
then use the Path class to combine your music path and file name:
If ListBox1.SelectedIndex > -1 Then
Dim s As String = IO.Combine(musicPath, ListBox1.SelectedItem.ToString)
' do something with s
End If

VB.NET Program is always reading last created textfile

Trying to create a login form,
My coding is currently:
Imports System
Imports System.IO
Public Class frmLogin
Dim username As String
Dim password As String
Dim fileReader As String
Dim folderpath As String
Dim files As Integer
Dim filepath As String
Public Structure info
Dim U As String
Dim P As String
End Structure
Dim details As info
Private Sub btnlogin_Click(sender As Object, e As EventArgs) Handles btnlogin.Click
If txtusername.Text = details.U And txtpassword.Text = details.P Then
MessageBox.Show("Correct!")
frmmenu.Show()
Me.Hide()
Else
MessageBox.Show("wrong")
txtusername.Clear()
txtpassword.Clear()
End If
End Sub
Private Sub btncreate_Click(sender As Object, e As EventArgs) Handles btncreate.Click
frmcreate.Show()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
files = files + 1
filepath = "C:\Users\TheGlove\Desktop\Alex's Program\loginfile" & files & ".txt"
Dim di As DirectoryInfo = New DirectoryInfo("C:\Users\TheGlove\Desktop\Alex's Program")
folderpath = "C:\Users\TheGlove\Desktop\Alex's Program"
files = System.IO.Directory.GetFiles(folderpath, "*.txt").Count
For Each fi In di.GetFiles()
MsgBox(fi.Name)
Dim FILE = System.IO.File.ReadAllLines("C:\Users\TheGlove\Desktop\Alex's Program\loginfile" & files & ".txt")
Dim myArray As String() = FILE
details.U = myArray(0)
details.P = myArray(1)
Next
End Sub
End Class
Button 1 will be merged with btnlogin when i get it working and for now is currently just a seperate button to read each textfile.
When each button is pressed (Button 1 -> btnlogin), only the last created textfile is correct.
By the looks of things, your code does read all the text files, but keeps overwriting details.u and details.p with the value retrieved from each file. So, when the loop gets to the last file, those values are what ends up in the details object.
I'm assuming that you want to read all the usernames and passwords into a list and check the details in the TextBoxes against that list, so .. Your code should probably be something like the code below (see the code comments for an explanation of some of the differences.
Before we get to the code, can give you a couple of pointers.
Firstly, always try to use names that are meaningful. Defining your structure as Info is not as meaningful as it could be. For example, you would be better calling it UserInfo and rather than use P and U, you would be better using Password and UserName. It may not matter so much right now, but when you start writing larger more complex programs, and have to come back to them in 6 months time to update them, info.P or details.P aren't as informative as the suggested names.
Secondly, as #ajd mentioned. Don't use magic strings. Create one definition at the beginning of your code which can be used throughout. Again it makes maintenance much easier if you only have to change a string once instead of multiple times, and reduces the chance of mistakes.
Finally, several of the variables you have defined aren't used in your code at all. Again, at this level, it isn't a major issue, but with large programs, you could end up with a bigger memory footprint than you want.
Dim username As String
Dim password As String
Dim fileReader As String
Dim folderpath As String = "C:\Users\TheGlove\Desktop\Alex's Program"
Dim files As Integer
Dim filepath As String
Public Structure UserInfo
Dim Name As String
Dim Password As String
End Structure
'Change details to a list of info instead of a single instance
Dim userList As New List(Of UserInfo)
Private Sub Btnlogin_Click(sender As Object, e As EventArgs) Handles btnlogin.Click
'Iterate through the list of details, checking each instance against the textboxes
For Each tempUserInfo As UserInfo In userList
If txtusername.Text = tempUserInfo.Name And txtpassword.Text = tempUserInfo.Password Then
MessageBox.Show("Correct!")
frmmenu.Show()
Me.Hide()
'This is here, because after your form has opened an closed, the loop
'that checks usernames and passwords will continue. The line below exits the loop safely
Exit For
Else
MessageBox.Show("wrong")
txtusername.Clear()
txtpassword.Clear()
End If
Next
End Sub
Private Sub Btncreate_Click(sender As Object, e As EventArgs) Handles btncreate.Click
frmcreate.Show()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'clear the list of user details otherwise, if the files are loaded a second time,
'you'll get the same details added again
userList.Clear()
'This line replaces several lines in your code that searches the folder for files
'marching the search pattern
Dim fileList() As FileInfo = New DirectoryInfo(folderpath).GetFiles("loginfile*.txt")
For Each fi As FileInfo In fileList
MsgBox(fi.Name)
Dim userDetails() As String = System.IO.File.ReadAllLines(fi.FullName)
Dim tempInfo As New UserInfo With {.Name = userDetails(0), .Password = userDetails(1)}
'An expanded version of the above line is
'Dim tempInfo As New info
'tempInfo.U = userDetails(0)
'tempInfo.P = userDetails(1)
userList.Add(tempInfo)
Next
files = fileList.Count
End Sub

Files from sub directories

Can somebody help me to implement to add files from sub directories to my listbox ? I have looked for some information from google, but source code from those examples was really diffrent than my and I'm newbie in VB.NET. I think it may be the System.IO.SearchOption.AllDirectories but I have no clue how to implement it in my code.
Private Sub ButtonFolder_Click(sender As System.Object, e As System.EventArgs) Handles ButtonFolder.Click
FolderBrowserDialog1.ShowDialog()
FilePathLabel.Text = System.IO.Path.GetFileName(FolderBrowserDialog1.SelectedPath)
Dim folder As New IO.DirectoryInfo(System.IO.Path.GetFullPath(FolderBrowserDialog1.SelectedPath))
Dim arrfile() As IO.FileInfo
Dim file As IO.FileInfo
arrfile = folder.GetFiles("*.*")
dicPaths.Clear()
For Each file In arrfile
'ListBox1.Items.Add(file.FullName)
dicPaths.Add(file.Name, file.FullName)
Next file
For Each item As String In dicPaths.Keys
ListBox1.Items.Add(item)
Next item
Label1.Text = "Total Items : " + ListBox1.Items.Count.ToString
End Sub
Try This:
Public Function FindFiles(ByVal Path As String) As Boolean
Dim Directories As New IO.DirectoryInfo(Path)
Dim Directory As IO.DirectoryInfo
Dim File As IO.FileInfo
For Each Directory In Directories.GetDirectories
For Each File In Directory.GetFiles
ListBox1.Items.Add(File.Name)
Next
If Directory.GetDirectories.Length > 0 Then
FindFiles(Directory.FullName)
End If
Next
End Function
Usage: FindFiles(FolderBrowserDialog1.SelectedPath)