VB.net file handling progresses to slowly - vb.net

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

Related

How to search multiple text files in a directory for a string of text at once

I have a ListBox with a certain amount of items in it.
For each item in the ListBox a corresponding text file exists in the file directory.
I need to search each text file (based on what's in the ListBox) for a persons name. Each text file may contain the name or it may not.
I would then like a return which text file contains the name.
I have tried this as a way to search a text file: it works, but I'm not sure how to get this to repeat based on whats in a ListBox.
Dim sFileContents As String = String.Empty
If (System.IO.File.Exists((Application.StartupPath) & "\Project_Green.txt")) Then
sFileContents = (System.IO.File.ReadAllText((Application.StartupPath) & "\Project_Green.txt"))
End If
If sFileContents.Contains(TextBox4.Text) Then
MessageBox.Show("yup")
Else
MessageBox.Show("nope")
End If
Also, if it would be possible to ignore case that would be great.
If you have a bunch of files in a directory and you have their names in a ListBox, and you want to search their contents for something.
One liner query:
Imports System.IO
'...
Sub TheCaller()
Dim dir = My.Application.Info.DirectoryPath
Dim ext = ".txt" ' If the extensions are trimmed in the list.
Dim find = TextBox4.Text
Dim files = Directory.EnumerateFiles(dir).Where(Function(x) ListBox1.Items.Cast(Of String).
Any(Function(y) String.Concat(y, ext).
Equals(Path.GetFileName(x),
StringComparison.InvariantCultureIgnoreCase) AndAlso File.ReadLines(x).
Any(Function(z) z.IndexOf(find, StringComparison.InvariantCultureIgnoreCase) >= 0))).ToList
ListBox2.Items.Clear()
ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub
Or if you prefer the For Each loop:
Sub Caller()
Dim dir = My.Application.Info.DirectoryPath
Dim find = TextBox4.Text
Dim files As New List(Of String)
For Each f As String In ListBox1.Items.Cast(Of String).
Select(Function(x) Path.Combine(dir, $"{x}.txt"))
If File.Exists(f) AndAlso
File.ReadLines(f).Any(Function(x) x.IndexOf(find,
StringComparison.InvariantCultureIgnoreCase) <> -1) Then
files.Add(f)
End If
Next
ListBox2.Items.Clear()
ListBox2.Items.AddRange(files.Select(Function(x) Path.GetFileNameWithoutExtension(x)).ToArray)
End Sub
Either way, the files list contains the matches if any.
Plus a pseudo-parallel async method, for the very heavy-duty name searches.
The Async Function SearchNameInTextFiles returns a named Tuple:
(FileName As String, Index As Integer)
where FileName is the file parsed and Index is the position where the first occurrence of the specified name (theName) was found.
If no matching sub-string is found, the Index value is set to -1.
The caseSensitive parameter allows to specify whether the match should be, well, case sensitive.
You can start the search from a Button.Click async handler (or similar), as shown here.
Imports System.IO
Imports System.Threading.Tasks
Private Async Sub btnSearchFiles_Click(sender As Object, e As EventArgs) Handles btnSearchFiles.Click
Dim filesPath = [Your files path]
Dim theName = textBox4.Text ' $" {textBox4.Text} " to match a whole word
Dim ext As String = ".txt" ' Or String.Empty, if extension is already included
Dim tasks = ListBox1.Items.OfType(Of String).
Select(Function(f) SearchNameInTextFiles(Path.Combine(filesPath, f & ext), theName, False)).ToList()
Await Task.WhenAll(tasks)
Dim results = tasks.Where(Function(t) t.Result.Index >= 0).Select(Function(t) t.Result).ToList()
results.ForEach(Sub(r) Console.WriteLine($"File: {r.FileName}, Position: {r.Index}"))
End Sub
Private Async Function SearchNameInTextFiles(filePath As String, nameToSearch As String, caseSensitive As Boolean) As Task(Of (FileName As String, Index As Integer))
If Not File.Exists(filePath) then Return (filePath, -1)
Using reader As StreamReader = New StreamReader(filePath)
Dim line As String = String.Empty
Dim linesLength As Integer = 0
Dim comparison = If(caseSensitive, StringComparison.CurrentCulture,
StringComparison.CurrentCultureIgnoreCase)
While Not reader.EndOfStream
line = Await reader.ReadLineAsync()
Dim position As Integer = line.IndexOf(nameToSearch, comparison)
If position > 0 Then Return (filePath, linesLength + position)
linesLength += line.Length
End While
Return (filePath, -1)
End Using
End Function
You can do these simple steps for your purpose:
First get all text files in the application's startup directory.
Then iterate over all names in the ListBox and for each one, search in all text files to find the file that contains that name.
To make the process case-insensitive, we first convert names and text file's contents to "lower case" and then compare them. Here is the full code:
Private Sub findTextFile()
'1- Get all text files in the directory
Dim myDirInfo As New IO.DirectoryInfo(Application.StartupPath)
Dim allTextFiles As IO.FileInfo() = myDirInfo.GetFiles("*.txt")
'2- Iterate over all names in the ListBox
For Each name As String In ListBox1.Items
'Open text files one-by-one and find the first text file that contains this name
Dim found As Boolean = False 'Changes to true once the name is found in a text file
Dim containingFile As String = ""
For Each file As IO.FileInfo In allTextFiles
If System.IO.File.ReadAllText(file.FullName).ToLower.Contains(name.ToLower) Then 'compares case-insensitive
found = True
containingFile = file.FullName
Exit For
End If
Next
'Found?
If found Then
MsgBox("The name '" + name + "' found in:" + vbNewLine + containingFile)
Else
MsgBox("The name '" + name + "' does not exist in any text file.")
End If
Next
End Sub

Writing a string to a new .csv in VB.net

I am trying to write a string to a .csv file, but unable to get it to display.
I have managed to do it in VBA, but when writing in VB.net it's not working.
I first create the file and set the headers for each column. After this I am getting information on each required attribute and writing it to a string s.
All i want to do now is write the string to the .csv file so that each attribute is in the right column under the right header.
Each time the string s simply needs to be on a new row.
This is what I have so far (I have cut out some bits of code so some syntax may look incorrect). What am i doing wrong or missing?
Sub Main()
Dim sOutput As String
' Create a header for the output file
sOutput = ("Level,Occurrence Name,Reference Name,Object type, Visibility, Path" & vbLf)
If Occs.Count > 0 Then
For i = 1 To Occs.Count
iLevel = 0
curOcc = Occs.Item(i)
GetOccurrenceData(curOcc, sOutput, oSel, False, iLevel)
Next
End If
' Write the output string to a file
Dim sPath As String
Dim bWrite As Boolean
sPath = ("C:\temp\data3.csv")
bWrite = WriteFile(sPath, sOutput)
End Sub
Sub GetOccurrenceData(curOcc As VPMOccurrence, s As String, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
'CODE TO GET DATA REMOVED AS IRRELEVANT
' Append the output string with the data for the current occurrence.
s = (s & curLevel & "," & sName & "," & sRefName & "," & sType & "," & sVisibility & vbLf)
' Repeat this data gathering procedure on any children the current occurrence may have.
Occs = curOcc.Occurrences
If Occs.Count > 0 Then
For i = 1 To Occs.Count
GetOccurrenceData(Occs.Item(i), s, sel, bChildrenInheritNoShow, curLevel)
Next
End If
In GetOccurrenceData you pass in a string and change it in the method, but you did not pass it in as a ByRef so anything done to the string in the method stays in the method.
Change the header of your method to read
Sub GetOccurrenceData(curOcc As VPMOccurrence,ByRef s As String, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
I would however recommend using a StringBuilder to accomplish what you are doing.
Like This:
Sub Main()
Dim sb As New Text.StringBuilder()
sb.AppendLine("Level,Occurrence Name,Reference Name,Object type, Visibility, Path")
If Occs.Count > 0 Then
For i = 1 To Occs.Count
iLevel = 0
curOcc = Occs.Item(i)
GetOccurrenceData(curOcc, sb, oSel, False, iLevel)
Next
End If
' Write the output string to a file
Dim sPath As String
Dim bWrite As Boolean
sPath = ("C:\temp\data3.csv")
bWrite = WriteFile(sPath, sb.ToString())
End Sub
Sub GetOccurrenceData(curOcc As VPMOccurrence, sb As Text.StringBuilder, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
'CODE TO GET DATA REMOVED AS IRRELEVANT
' Append the output string with the data for the current occurrence.
sb.Append(curLevel).Append(",").Append(sName).Append(",").Append(sRefName).Append(",").Append(sType).Append(",").AppendLine(sVisibility)
' Repeat this data gathering procedure on any children the current occurrence may have.
Occs = curOcc.Occurrences
If Occs.Count > 0 Then
For i = 1 To Occs.Count
GetOccurrenceData(Occs.Item(i), sb, sel, bChildrenInheritNoShow, curLevel)
Next
End If
End Sub

Renaming files listed within a listbox

I'm trying to create a little program thats able to alter the names of shotcuts, from a listbox-index.
I've created a button wich list every file with the targeted "extension"(targeted with combobox1), from where I want another button to be able to alter the files names:
Button 1 code(Searching for the files):
Dim kilde As New FolderBrowserDialog
If kilde.ShowDialog = Windows.Forms.DialogResult.OK Then
Dim mappe = New System.IO.DirectoryInfo(kilde.SelectedPath)
txtbSti.Text = kilde.SelectedPath
End If
For Each f In Directory.GetFiles(txtbSti.Text, "*" & ComboBox1.Text & "*", SearchOption.AllDirectories)
If File.Exists(f) Then
With ListBox1
With .Items
.Add(f)
End With
End With
End If
Next f
This gives me a list with the desired files.
Is there a way, to rename the files, in my case,listed within listbox1, line by line?
Button 2 (not functioning)
For Each r As String In ListBox1.Items
System.IO.Path.ChangeExtension(ComboBox1.Text, " ")
Next r
You can use File.Move to "rename" a file:
System.IO.File.Move("oldfilename", "newfilename")
For example:
For Each oldFilePath As String In ListBox1.Items
If System.IO.File.Exists(oldFilePath) Then
Dim dir = System.IO.Path.GetDirectoryName(oldFilePath)
Dim newFilePath = System.IO.Path.Combine( dir, "newFileName")
System.IO.File.Move(oldFilePath, newFilePath)
End If
Next
Edit: In this case, remove the automatic generated extension ".avi - Shortcut"
If you just want to change an extension you can use Path.ChangeExtension:
System.IO.Path.ChangeExtension(oldFilePath, "new_extension")
Update: oh, sorry, it's a part of the name; the file is named, example; J:\Homemovies\Jumping around.avi - Shortcut.lnk, I want to remove the ".avi - Shortcut" part of the name on the actual file(s) listed within the listbox1 that is set up to find all the files within a targeted folder with that particular extension within it's name; J:\Homemovies\Jumping around.lnk
You can use String.Replace:
Dim name = System.IO.Path.GetFileName(oldFilePath).ToLowerInvariant()
Dim newName = name.Replace(".avi - shortcut.", ".")
Dim newPath = System.IO.Path.Combine(
System.IO.Path.GetDirectoryName(oldFilePath), newName)
Update2: still can't get either of those solutions to work
Here is a complete working sample:
First, read the shortcuts from your directory:
Dim dir = "C:\Temp\Homemovies"
For Each fn In Directory.EnumerateFileSystemEntries(dir, "*avi - shortcut*.*", SearchOption.AllDirectories)
ListBox1.Items.Add(fn)
Next
Second (in your button-click handler), rename those .Ink files by removing the part that contains(substring)"avi - shortcut", also handling the case that it already exists:
For Each fn As String In ListBox1.Items
If File.Exists(fn) Then
Dim folder = Path.GetDirectoryName(fn)
Dim fileName = Path.GetFileNameWithoutExtension(fn)
Dim extension = Path.GetExtension(fn)
Dim dotParts = fileName.Split("."c)
Dim allButAviShortCut = From part In dotParts
Where part.IndexOf("avi - shortcut", StringComparison.InvariantCultureIgnoreCase) = -1
Dim newFileName = String.Join(".", allButAviShortCut)
Dim newPath = System.IO.Path.Combine(dir, newFileName & extension)
Dim number = 0
While File.Exists(newPath)
' exists already, append number
number += 1
Dim newFileNameWithNum = String.Format("{0}_{1}", newFileName, number)
newPath = System.IO.Path.Combine(dir, newFileNameWithNum & extension)
End While
System.IO.File.Move(fn, newPath)
End If
Next
Consider the following:
ListBox1 contains list of all files in the selected directory.
txtbSti is the text box which holds the path to the selected directory.
Each file is renamed as New_fileX where X is the index of the file in the ListBox1
Now take a look into the code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim i As Integer = 0
For Each r As String In ListBox1.Items
i += 1
My.Computer.FileSystem.RenameFile(txtbSti.Text & ":\" & r, "New_file" & i & ".txt")
Next r
End Sub

Remove items from a listbox if it appears in another

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

Background Worker and SaveDialog

I am very new with Background worker control. I have an existing project that builds file but throughout my project while building files I get the deadlock error.
I am trying to solve it by creating another project that will only consist out of the background worker. I will then merge them.
My problem is I don't know where it will be more effective for my background worker to be implemented and also the main problem is how can I use the SaveDialog with my background worker? I need to send a parameter to my background worker project telling it when my file is being build en when it is done.
This is where my file is being build:
srOutputFile = New System.IO.StreamWriter(strFile, False) 'Create File
For iSeqNo = 0 To iPrintSeqNo
' Loop through al the record types
For Each oRecord As stFileRecord In pFileFormat
If dsFile.Tables.Contains(oRecord.strRecordName) Then
' Loop through al the records
For Each row As DataRow In dsFile.Tables(oRecord.strRecordName).Rows
' Check record id
If oRecord.strRecordId.Length = 0 Then
bMatched = True
Else
bMatched = (CInt(oRecord.strRecordId) = CInt(row.Item(1)))
End If
' Match records
If iSeqNo = CInt(row.Item(0)) And bMatched Then
strRecord = ""
' Loop through al the fields
For iLoop = 0 To UBound(oRecord.stField)
' Format field
If oRecord.stField(iLoop).iFieldLength = -1 Then
If strRecord.Length = 0 Then
strTmp = row.Item(iLoop + 1).ToString
Else
strTmp = strDelimiter & row.Item(iLoop + 1).ToString
End If
ElseIf oRecord.stField(iLoop).eFieldType = enumFieldType.TYPE_VALUE Or _
oRecord.stField(iLoop).eFieldType = enumFieldType.TYPE_AMOUNT_CENT Then
strTmp = row.Item(iLoop + 1).ToString.Replace(".", "").PadLeft(oRecord.stField(iLoop).iFieldLength, "0")
strTmp = strTmp.Substring(strTmp.Length - oRecord.stField(iLoop).iFieldLength)
Else
strTmp = row.Item(iLoop + 1).ToString.PadRight(oRecord.stField(iLoop).iFieldLength, " ").Substring(0, oRecord.stField(iLoop).iFieldLength)
End If
If oRecord.stField(iLoop).iFieldLength > -1 And (bForceDelimiter) And strRecord.Length > 0 Then
strTmp = strDelimiter & strTmp
End If
strRecord = strRecord & strTmp
Next
' Final delimiter
If (bForceDelimiter) Then
strRecord = strRecord & strDelimiter
End If
srOutputFile.WriteLine(strRecord)
End If
Next
End If
Next
Next
You could try this:
Private locker1 As ManualResetEvent = New System.Threading.ManualResetEvent(False)
Private locker2 As ManualResetEvent = New System.Threading.ManualResetEvent(False)
Dim bOpenFileOK As Boolean
Dim myOpenFile As OpenFileDialog = New OpenFileDialog()
Private Sub FileOpener()
While Not bTerminado
If myOpenFile.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
bOpenFileOK = True
Else
bOpenFileOK = False
End If
locker2.Set()
locker1.WaitOne()
End While
End Sub
' Detonator of the action
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim tFileOp As Thread = New Thread(AddressOf FileOpener)
tFileOp.SetApartmentState(ApartmentState.STA)
tFileOp.Start()
' Start BackgroundWorker
BW1.RunWorkerAsync()
End Sub
Private Sub AsyncFunctionForBW(ByVal args As ArrayList)
'[...]
'Change options dinamically for the OpenFileDialog
myOpenFile.Filter = ""
myOpenFile.MultiSelect = True
'Calling the FileDialog
locker1.Set()
locker2.WaitOne()
locker1.Reset()
locker2.Reset()
If bOpenFileOK Then
myStream = myOpenFile.OpenFile()
'[...]
End If
End Sub
It's a little bit complicated but it works.
ManualResetEvents interrupt the execution of code (if they are told to stop) when reached until you use .Set(). If you use .WaitOne() you set it in stop mode, so it will stop again when reached.
This code defines two ManualResetEvents. When you click the Button1 starts the function FileOpener() in a new Thread, and then starts the BackgroundWorker. The FileOpener() function shows a FileOpenDialog and waits in the locker1 so when you use locker1.Set() the function shows the file dialog.
As the myOpenFile is a "global" variable (as well as bOpenFileOK), once the user select the file (or not) you could detect the dialog result (bOpenFileOK) and the selected file.