Remove items from a listbox if it appears in another - vb.net

I have 2 listbox's on my form. The first populates from an array and displays files names that relate to the value of a Date Time picker. When that item is double clicked it moves over to the 2nd list box, clears from the 1st and the relevant files are transferred from one directory to another. The problem I have is that as the population is part of the load event once the application is closed and then re-opened the files names appear in both listbox's.
Is there a way to say if the object appears in 1 textbox then it shouldn't appear in the other?
I've tried the following but re-opening still displays the object in both
Dim item As Object
For Each item In lstPlanned.Items
If lstProgress.Contains(item) Then
lstPlanned.Items.Remove(item)
End If
Next
For the 2nd listbox I'm using the following to populate it
For Each Dir As String In System.IO.Directory.GetDirectories(aMailbox)
Dim dirInfo As New System.IO.DirectoryInfo(Dir)
lstProgress.Items.Add(dirInfo.Name)
Full Load code as follows
Private Sub Main_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim loaddate As String = Calendar.Value.ToString("dd/MM/yy")
ReDim AllDetail(0 To 0)
numfiles = 0
lstPlanned.Items.Clear()
Dim allfiles = lynxin.GetFiles("*.txt")
ReDim AllDetails(allfiles.Count)
lstProgress.Items.Clear()
lstPlanned.Items.Add("No Jobs Planned Today!")
lstPlanned.Enabled = False
For Each txtfi In (allfiles)
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
AllDetails(numfiles) = New FileDetail()
AllDetails(numfiles).uPath = Microsoft.VisualBasic.Left((txtfi.FullName), Len(txtfi.FullName) - 4)
AllDetails(numfiles).uFile = Path.GetFileNameWithoutExtension(txtfi.Name)
Dim line = allLines.Where(Function(x) (x.StartsWith("unitname="))).SingleOrDefault()
If line IsNot Nothing Then
AllDetails(numfiles).uName = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("unitcode="))).SingleOrDefault()
If line IsNot Nothing Then
AllDetails(numfiles).uCode = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("opername="))).SingleOrDefault()
If line IsNot Nothing Then
AllDetails(numfiles).uOps = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("plandate="))).SingleOrDefault()
If line IsNot Nothing Then
AllDetails(numfiles).uPlan = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("cliecode="))).SingleOrDefault()
If line IsNot Nothing Then
AllDetails(numfiles).uClient = line.Split("="c)(1)
End If
If AllDetails(numfiles).uPlan = loaddate Then
lstPlanned.Items.Remove("No Jobs Planned Today!")
lstPlanned.Enabled = True
lstPlanned.Items.Insert(0, AllDetails(numfiles).uName & " - " & AllDetails(numfiles).uCode & " - " & AllDetails(numfiles).uOps)
numfiles = numfiles + 1
End If
Next
For Each Dir As String In System.IO.Directory.GetDirectories(aMailbox)
Dim dirInfo As New System.IO.DirectoryInfo(Dir)
lstProgress.Items.Add(dirInfo.Name)
Dim item As Object
For Each item In lstPlanned.Items
If lstProgress.Contains(item) Then
lstPlanned.Items.Remove(item)
End If
Next
Next
End Sub

The Contains method of a listbox checks the controls collections not the items collection. It should have been lstProgress.Items.Contains(item). Also you can use GetDirectories of the DirectoryInfo class to get the directoryinfo objects directly.
Checking to see if lstPlanned contains each item as you add it to lstProgress will eliminate the extra loop, which wouldn't work right anyway, because you're not allowed to modify the iterated collection in a For Each loop.
I was looking over your code and noticed an improvement that could be made. Using the LINQ extension methods each you want to add a property value means a lot of extra iterating through each file line collection. Using select means you only iterate through the collection once.
For Each txtfi In (allfiles)
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
AllDetails(numfiles) = New FileDetail()
AllDetails(numfiles).uPath = Microsoft.VisualBasic.Left((txtfi.FullName), Len(txtfi.FullName) - 4)
AllDetails(numfiles).uFile = Path.GetFileNameWithoutExtension(txtfi.Name)
AllDetails(numfiles).uPlan = allLines.Where(Function(x) (x.StartsWith("plandate="))).SingleOrDefault().Split("="c)(1)
If AllDetails(numfiles).uPlan = loaddate Then
For Each line In allLines
If line Is Not Nothing Then
Dim fields As String() = line.Split("="c)
Select Case fields(0)
Case "unitname"
AllDetails(numfiles).uName = fields(1)
Case "unitcode"
AllDetails(numfiles).uCode = fields(1)
Case "opername"
AllDetails(numfiles).uOps = fields(1)
Case "plandate"
AllDetails(numfiles).uPlan = fields(1)
Case "cliecode"
AllDetails(numfiles).uClient = fields(1)
End Select
End If
Next
lstPlanned.Items.Remove("No Jobs Planned Today!")
lstPlanned.Enabled = True
lstPlanned.Items.Insert(0, AllDetails(numfiles).uName & " - " & AllDetails(numfiles).uCode & " - " & AllDetails(numfiles).uOps)
numfiles = numfiles + 1
End If
Next
Dim RootDir As New System.IO.DirectoryInfo(aMailbox)
For Each Dir As IO.DirectoryInfo In RootDir.GetDirectories
Dim item = Dir.Name
lstProgress.Items.Add(item)
If lstPlanned.Items.Contains(item) Then
lstPlanned.Items.Remove(item)
End If
Next

Thanks Tinstaafl
Your answer works a treat.
I also adapted your first edit to
Dim RootDir As New System.IO.DirectoryInfo(aMailbox)
For Each Dir As IO.DirectoryInfo In RootDir.GetDirectories
lstProgress.Items.Add(Dir.Name)
'item defaults to object, no need to explicitly declare it
For Each item In New System.Collections.ArrayList(lstPlanned.Items)
If lstProgress.Items.Contains(item) Then
lstPlanned.Items.Remove(item)
End If
Next
Next
amending the following line
For Each item In New System.Collections.ArrayList(lstPlanned.Items)
Thanks again

Related

VB.net file handling progresses to slowly

Hi i have a app that takes a list of files and searches each file for all the images referenced within each file. When the list is finished I sort and remove duplicates from the list then copy each item/image to a new folder. It works, but barely. I takes hours for the copying to occur on as little as 500 files. Doing the copying in windows explorer if faster, and that defeats the purpose of the application.
I don't know how to streamline it better. Your inputs would be greatly appreciated.
'Remove Dupes takes the list of images found in each file and removes any duplicates
Private Sub RemoveDupes(ByRef Items As List(Of String), Optional ByVal NeedSorting As Boolean = False)
statusText = "Removing duplicates from list."
Dim Temp As New List(Of String)
Items.Sort()
'Remove Duplicates
For Each Item As String In Items
'Check if item is in Temp
If Not Temp.Contains(Item) Then
'Add item to list.
Temp.Add(Item)
File.AppendAllText(ListofGraphics, Item & vbNewLine)
End If
Next Item
'Send back new list.
Items = Temp
End Sub
'GetImages does the actual copying of files from the list RemoveDup creates
Public Sub GetImages()
Dim imgLocation = txtSearchICN.Text
' Get the list of file
Dim fileNames As String() = System.IO.Directory.GetFiles(imgLocation)
Dim i As Integer
statusText = "Copying image files."
i = 0
For Each name As String In GraphicList
i = i + 1
' See whether name appears in fileNames.
Dim found As Boolean = False
' Search name in fileNames.
For Each fileName As String In fileNames
' GraphicList consists of filename without extension, so we compare name
' with the filename without its extension.
If Path.GetFileNameWithoutExtension(fileName) = name Then
Dim FileNameOnly = Path.GetFileName(fileName)
' Debug.Print("FileNameOnly: " & FileNameOnly)
Dim copyTo As String
copyTo = createImgFldr & "\" & FileNameOnly
System.IO.File.Copy(fileName, copyTo)
File.AppendAllText(ListofFiles, name & vbNewLine)
'items to write to rich text box in BackgroundWorker1_ProgressChanged
imgFilename = (name) + vbCrLf
ImageCount1 = i
' Set found to True so we do not process name as missing, and exit For. \
found = True
Exit For
Else
File.AppendAllText(MissingFiles, name & vbNewLine)
End If
Next
status = "Copying Graphic Files"
BackgroundWorker1.ReportProgress(100 * i / GraphicList.Count())
Next
End Sub
'BackgroundWorker1_ProgressChanged. gets file counts and writes to labels and rich text box
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'' This event is fired when you call the ReportProgress method from inside your DoWork.
'' Any visual indicators about the progress should go here.
ProgressBar1.Value = e.ProgressPercentage
lblStatus.Text = CType(e.UserState, String)
lblStatus.Text = status & " " & e.ProgressPercentage.ToString & " % Complete "
RichTextBox1.Text &= (fileFilename)
RichTextBox1.Text &= (imgFilename)
txtImgCount.Text = ImageCount1
Label8.Text = statusText
fileCount.Text = fCount
End Sub
I would change something in your code to avoid the constant writing to a file at each loop and the necessity to have two loops nested.
This is a stripped down version of your GetFiles intended to highlight my points:
Public Sub GetImages()
' Two list to collect missing and found files
Dim foundFiles As List(Of String) = New List(Of String)()
Dim notfoundFiles As List(Of String) = New List(Of String)()
Dim fileNames As String() = System.IO.Directory.GetFiles(imgLocation)
' Loop over the filenames retrieved
For Each fileName As String In fileNames
' Check if the files is contained or not in the request list
If GraphicList.Contains(Path.GetFileNameWithoutExtension(fileName)) Then
Dim FileNameOnly = Path.GetFileName(fileName)
Dim copyTo As String
copyTo = createImgFldr & "\" & FileNameOnly
System.IO.File.Copy(fileName, copyTo)
' Do not write to file inside the loop, just add the fact to the list
foundFiles.Add(FileNameOnly)
Else
notfoundFiles.Add(FileNameOnly)
End If
Next
' Write everything outside the loop
File.WriteAllLines(listofFiles, foundFiles)
File.WriteAllLines(MissingFiles, notfoundFiles)
End Sub

vb.net showdialog openfile to string

i'm trying to importe a csv file after creating on the same application, the only probleme is that even if a string matchs a line from the file, comparing them on vb.net dosn't give a true boolean i know i didn't make any mistakes because of the line with the commentary 'THIS LINE , i use a pretty small list to do my test and in that loop, there is always one element that matches exactly the string, but comparing them on vb.net dosn't return a true. the result of this is that just after saving the file to my computer while still having it on my listview1 on the application, i load it to the application again and it duplicates everything
Dim open As New OpenFileDialog
open.Filter = "CSV Files (*.csv*)|*.csv"
If open.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim File As String = My.Computer.FileSystem.ReadAllText(open.FileName)
Dim lines() As String = File.Split(Environment.NewLine)
For i = 1 To (lines.Count - 1)
Dim line As String = lines(i)
Dim Columns() As String = line.Split(";")
Dim addLin As New ListViewItem(Columns)
Dim Alr As Boolean = False
Dim st as string=listView1.Items.Item(k).SubItems.Item(0).Text
For k = 0 To (ListView1.Items.Count - 1)
Dim st as string=listView1.Items.Item(k).SubItems.Item(0).Text
MsgBox(Columns(0)& Environment.NewLine & st) 'THIS LINE
If ListView1.Items.Item(k).SubItems.Item(0).Text = Columns(0) Then
Alr = True
MsgBox("true") 'never showsup even when the previous one show the match
End If
next
If Alr = False Then
MsgBox("false") 'the msgbox is only to check
ListView1.Items.Add(addLin)
End If
next
end if

VB "Index was out of range, must be non-negative and less than the size of the collection." When trying to generate a random number more than once

So I'm trying to generate a random number on button click. Now this number needs to be between two numbers that are inside my text file with various other things all separated by the "|" symbol. The number is then put into the text of a textbox which is being created after i run the form. I can get everything to work perfectly once, but as soon as i try to generate a different random number it gives me the error: "Index was out of range, must be non-negative and less than the size of the collection." Here is the main code as well as the block that generates the textbox after loading the form. As well as the contents of my text file.
Private Sub generate()
Dim newrandom As New Random
Try
Using sr As New StreamReader(itemfile) 'Create a stream reader object for the file
'While we have lines to read in
Do Until sr.EndOfStream
Dim line As String
line = sr.ReadLine() 'Read a line out one at a time
Dim tmp()
tmp = Split(line, "|")
rows(lineNum).buybutton.Text = tmp(1)
rows(lineNum).buyprice.Text = newrandom.Next(tmp(2), tmp(3)) 'Generate the random number between two values
rows(lineNum).amount.Text = tmp(4)
rows(lineNum).sellprice.Text = tmp(5)
rows(lineNum).sellbutton.Text = tmp(1)
lineNum += 1
If sr.EndOfStream = True Then
sr.Close()
End If
Loop
End Using
Catch x As Exception ' Report any errors in reading the line of code
Dim errMsg As String = "Problems: " & x.Message
MsgBox(errMsg)
End Try
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
rows = New List(Of duplicate)
For dupnum = 0 To 11
'There are about 5 more of these above this one but they all have set values, this is the only troublesome one
Dim buyprice As System.Windows.Forms.TextBox
buyprice = New System.Windows.Forms.TextBox
buyprice.Width = textbox1.Width
buyprice.Height = textbox1.Height
buyprice.Left = textbox1.Left
buyprice.Top = textbox1.Top + 30 * dupnum
buyprice.Name = "buypricetxt" + Str(dupnum)
Me.Controls.Add(buyprice)
pair = New itemrow
pair.sellbutton = sellbutton
pair.amount = amounttxt
pair.sellprice = sellpricetxt
pair.buybutton = buybutton
pair.buyprice = buypricetxt
rows.Add(pair)
next
end sub
'textfile contents
0|Iron Sword|10|30|0|0
1|Steel Sword|20|40|0|0
2|Iron Shield|15|35|0|0
3|Steel Shield|30|50|0|0
4|Bread|5|10|0|0
5|Cloak|15|30|0|0
6|Tent|40|80|0|0
7|Leather Armour|50|70|0|0
8|Horse|100|200|0|0
9|Saddle|50|75|0|0
10|Opium|200|500|0|0
11|House|1000|5000|0|0
Not sure what else to add, if you know whats wrong please help :/ thanks
Add the following two lines to the start of generate():
Private Sub generate()
Dim lineNum
lineNum = 0
This ensures that you don't point to a value of lineNum outside of the collection.
I usually consider it a good idea to add
Option Explicit
to my code - it forces me to declare my variables, and then I think about their initialization more carefully. It helps me consider their scope, too.
Try this little modification.
I took your original Sub and changed a little bit take a try and let us know if it solve the issue
Private Sub generate()
Dim line As String
Dim lineNum As Integer = 0
Dim rn As New Random(Now.Millisecond)
Try
Using sr As New StreamReader(_path) 'Create a stream reader object for the file
'While we have lines to read in
While sr.Peek > 0
line = sr.ReadLine() 'Read a line out one at a time
If Not String.IsNullOrEmpty(line) And Not String.IsNullOrWhiteSpace(line) Then
Dim tmp()
tmp = Split(line, "|")
rows(lineNum).buybutton.Text = tmp(1)
rows(lineNum).buyprice.Text = rn.Next(CInt(tmp(2)), CInt(tmp(3))) 'Generate the random number between two values
rows(lineNum).amount.Text = tmp(4)
rows(lineNum).sellprice.Text = tmp(5)
rows(lineNum).sellbutton.Text = tmp(1)
lineNum += 1
End If
End While
End Using
Catch x As Exception ' Report any errors in reading the line of code
Dim errMsg As String = "Problems: " & x.Message
MsgBox(errMsg)
End Try
End Sub

Search line in text file and return value from a set starting point vb.net

I'm currently using the following to read the contents of all text files in a directory into an array
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
Within the text files are only 6 lines that all follow the same format and will read something like
forecolour=black
I'm trying to then search for the word "forecolour" and retrieve the information after the "=" sign (black) so i can then populate the below code
AllDetail(numfiles).uPath = ' this needs to be the above result
I've only posted parts of the code but if it helps i can post the rest. I just need a little guidance if possible
Thanks
This is the full code
Dim numfiles As Integer
ReDim AllDetail(0 To 0)
numfiles = 0
lb1.Items.Clear()
Dim lynxin As New IO.DirectoryInfo(zMailbox)
lb1.Items.Clear()
For Each txtfi In lynxin.GetFiles("*.txt")
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
ReDim Preserve AllDetail(0 To numfiles)
AllDetail(numfiles).uPath = 'Needs to be populated
AllDetail(numfiles).uName = 'Needs to be populated
AllDetail(numfiles).uCode = 'Needs to be populated
AllDetail(numfiles).uOps = 'Needs to be populated
lb1.Items.Add(IO.Path.GetFileNameWithoutExtension(txtfi.Name))
numfiles = numfiles + 1
Next
End Sub
AllDetail(numfiles).uPath = Would be the actual file path
AllDetail(numfiles).uName = Would be the detail after “unitname=”
AllDetail(numfiles).uCode = Would be the detail after “unitcode=”
AllDetail(numfiles).uOps = Would be the detail after “operation=”
Within the text files that are being read there will be the following lines
Unitname=
Unitcode=
Operation=
Requirements=
Dateplanned=
For the purpose of this array I just need the unitname, unitcode & operation. Going forward I will need the dateplanned as when this is working I want to try and work out how to only display the information if the dateplanned matches the date from a datepicker. Hope that helps and any guidance or tips are gratefully received
If your file is not very big you could simply
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
For each line in allLines
Dim parts = line.Split("="c)
if parts.Length = 2 andalso parts(0) = "unitname" Then
AllDetails(numFiles).uName = parts(1)
Exit For
End If
Next
If you are absolutely sure of the format of your input file, you could also use Linq to remove the explict for each
Dim line = allLines.Where(Function(x) (x.StartsWith("unitname"))).SingleOrDefault()
if line IsNot Nothing then
AllDetails(numFiles).uName = line.Split("="c)(1)
End If
EDIT
Looking at the last details added to your question I think you could rewrite your code in this way, but still a critical piece of info is missing.
What kind of object is supposed to be stored in the array AllDetails?
I suppose you have a class named FileDetail as this
Public class FileDetail
Public Dim uName As String
Public Dim uCode As String
Public Dim uCode As String
End Class
....
numfiles = 0
lb1.Items.Clear()
Dim lynxin As New IO.DirectoryInfo(zMailbox)
' Get the FileInfo array here and dimension the array for the size required
Dim allfiles = lynxin.GetFiles("*.txt")
' The array should contains elements of a class that have the appropriate properties
Dim AllDetails(allfiles.Count) as FileDetail
lb1.Items.Clear()
For Each txtfi In allfiles)
Dim allLines() As String = File.ReadAllLines(txtfi.FullName)
AllDetails(numFiles) = new FileDetail()
AllDetails(numFiles).uPath = txtfi.FullName
Dim line = allLines.Where(Function(x) (x.StartsWith("unitname="))).SingleOrDefault()
if line IsNot Nothing then
AllDetails(numFiles).uName = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("unitcode="))).SingleOrDefault()
if line IsNot Nothing then
AllDetails(numFiles).uName = line.Split("="c)(1)
End If
line = allLines.Where(Function(x) (x.StartsWith("operation="))).SingleOrDefault()
if line IsNot Nothing then
AllDetails(numFiles).uOps = line.Split("="c)(1)
End If
lb1.Items.Add(IO.Path.GetFileNameWithoutExtension(txtfi.Name))
numfiles = numfiles + 1
Next
Keep in mind that this code could be really simplified if you start using a List(Of FileDetails)

How do I get a loop if elseif is true?

I created an app that will browse through my favorite game's pages to find an online person by reading the html code on their profile page. However I am having trouble coming up with a way for it to loop back if it finds "UserOfflineMessage". I inserted ((((Location I want it to loop back)))) where I wanted it to loop back. Any suggestions?
BTW: This is not for malicious purposes, this is just a project a few of us were working on.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim Rlo As New IO.StreamReader(My.Resources.Preferences & "Preferences.txt")
Dim firstLine As String
'read first line
firstLine = Rlo.ReadLine()
'read secondline
TheText.Text = Rlo.ReadLine()
'read third line
Dim thirdLine As String = Rlo.ReadLine()
Dim Ro As New IO.StreamReader(My.Resources.Preferences & "sig.txt")
Dim first1Line As String
'read first line
first1Line = Ro.ReadLine()
'read secondline
The2Text.Text = Ro.ReadLine()
((((Location I want it to loop back))))
rndnumber = New Random
number = rndnumber.Next(firstLine, TheText.Text)
TextBox2.Text = ("http://www.roblox.com/User.aspx?ID=" & number.ToString)
WebBrowser2.Navigate(TextBox2.Text)
If WebBrowser2.DocumentText.Contains("[ Offline ]</span>") Then
check1 = 1
TextBox1.Text = ("http://www.roblox.com/My/NewMessage.aspx?recipientID=" & number.ToString)
WebBrowser1.Navigate(TextBox1.Text)
MsgBox(":R")
ElseIf WebBrowser1.DocumentText.Contains("UserOfflineMessage") Then
MsgBox(":D")
End If
End Sub
You could use a Do...While loop:
Dim tryAgain as Boolean
...
Do
'...your stuff here...
tryAgain = False
if BlahBlah Then
...
ElseIf CaseWhereYouWantToLoop Then
tryAgain = True
End If
Loop While tryAgain
Change your ((((Location I want it to loop back)))) with
BackHere :
And add here
ElseIf WebBrowser1.DocumentText.Contains("UserOfflineMessage") Then
MsgBox(":D")
Goto BackHere