The form just freeze looping throught lines - vb.net

I'm just stuck into something in which I can't solve it in any way. My UI freeze even with BackgroundWorker.
Regarding to my old solved problem: VB.NET - It keep replacing itself
'I have in a text file lines of this format:
word1|word2|word3
anotherword1|anotherword2
I'm trying to split each word one by one per every line of that file and once program detect if the richtextbox has one of these words will replace that word with the unsplitted line. Example: From word1 to word1|word2|word3'
Everything works great, but only if I'm using a file with a small set of lines to split. But I need to split a big one at once.
Here is what I have so far: http://pastebin.com/raw/k0MtPHbZ
As I said, everything works if I reduce the lines of the en.txt file and I'm kinda confused why. I would really appreciate if someone would tell me how to fix this problem.
UPDATE:
As you guys said look what I did:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim list As New List(Of String)()
Using reader As New StreamReader(Application.StartupPath & "\en.txt")
Dim line As String = Nothing
Dim input = RichTextBox1.Text
While (InlineAssignHelper(line, reader.ReadLine())) IsNot Nothing
Dim pat = String.Format("\b({0})\b", line)
input = Regex.Replace(input, pat, line)
End While
RichTextBox2.Text = input
End Using
End Sub
But it still does the same. Work fine with small amount of lines. Freeze with my 500kb text file.

I believe your background worker is still going to block on your UI thread because you're referencing UI controls in the DoWork portion. You would be better off pulling in the data on the UI thread, assigning it to a variable, and then processing that all in memory in the DoWork instead of attempting to manipulate UI from a background thread, this is going to give you grief consistently.
So in your button1.Click handler, get the input from the textbox and assign it to an instance variable. Reference that instance variable inside your DoWork for the input.
Example:
Public Class Form1
Private _textInput As String = String.Empty
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
_textInput = RichTextBOx1.Text
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
Private Sub BackgroundWorker1_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i = 0 To 100
Threading.Thread.Sleep(200)
Dim list As New List(Of String)()
Using reader As New StreamReader(Application.StartupPath & "\en.txt")
Dim line As String = Nothing
While (InlineAssignHelper(line, reader.ReadLine())) IsNot Nothing
Dim pat = String.Format("\b({0})\b", line)
_textInput = Regex.Replace(_textInput , pat, line)
End While
End Using
BackgroundWorker1.ReportProgress(i)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
MsgBox("done")
RichTextBox1.Text = _textInput
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
End Class

Your Pastebin link includes the following code:
For i = 0 To 100
Threading.Thread.Sleep(200)
[...]
Next
At a glance, why are you putting the thread to sleep for a fifth of a second on every iteration of the for-loop?
Remove this line for starters.

Related

VB.net BackgroundWorker UI

I am trying to create an app that produces an excel document based on user input. The document can become quite extensive (multiple sheets), so I want to put the creation of the document on a BackgroundWorker and pass the progress to a ProgressBar.
I'm having problems accessing UI controls while creating the document.
How can I access multiple UI controls from a BackgroundWorker?
This is a very basic example of two of the many things that i want to do in the BackgroundWorker:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
OpenWorkbook()
Dim Combobox() As ComboBox = {ComboBox1, ComboBox2, ComboBox3, ComboBox4}
With xlWorkBook.Sheets("Cover1")
.Range("E5").Value = TextBox1.Text
.Range("E6").Value = TextBox2.Text
.Range("E7").Value = TextBox3.Text
.Range("E8").Value = TextBox4.Text
For i = 0 To Combobox.Count - 1
.Range("A" & i + 1).Value = Combobox(i).Text
Next
End With
CloseWorkBook()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
I'm not experienced at all and after researching on this subject, I'm still not getting along with the Delegate/Invoke. Any help would be awesome. Thanks in advance.
As pointed out by Heinzi you cant just pass reference to a control into a bgw. instead you pass the values.
Option Strict On
Public Class FormControls
WithEvents Bgw As New ComponentModel.BackgroundWorker With {.WorkerReportsProgress = True}
Public Class BgwArgs
Public TxtBx1Txt As String = FormControls.TextBox1.Text
Public TxtBx2Txt As String = FormControls.TextBox2.Text
Public CmbBx1Txt As String = FormControls.ComboBox1.SelectedText
End Class
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim BgwArgs As New BgwArgs
Bgw.RunWorkerAsync(BgwArgs)
End Sub
Private Sub Bgw_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles Bgw.DoWork
Dim BgwArgs As BgwArgs = DirectCast(e.Argument, BgwArgs)
.Range("E5").Value = BgwArgs.TxtBx1Txt 'etc
End Sub
End Class
I assume the OpenWorkbook is a sub that uses interop to create xlWorkBook where you would instead want to do that within the thread also, same with CloseWorkbook all that should be handled in the DoWork event and not in their own separate routines.

How to do you make a ListBox display all files in a drive?

I'm coding an Anti-Virus at the moment, so it's been very complicated to code it and design it. Anyway, the other day I ran into a problem, where my ListBox is not displaying all the files that are in the selected drive/directory.
I'll put some code and images so you get the idea.
Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button6.Click
ListBox1.Items.Clear()
ListBox3.Items.Clear()
FolderBrowserDialog1.SelectedPath = Path.GetPathRoot(Environment.SystemDirectory) & "Boot\"
On Error Resume Next
For Each strDir As String In System.IO.Directory.GetDirectories(FolderBrowserDialog1.SelectedPath)
For Each strFile As String In System.IO.Directory.GetFiles(strDir)
ListBox1.Items.Add(strFile)
ListBox3.Items.Add(strFile)
Next
Next
Timer1.Start()
End Sub
However, instead of the files appearing in the ListBox (ListBox3), it just gives a black screen. Maybe I should remove the TabControl that it is surrounded by?
See how it's black? It even happens when I run it.
Hope this helps! Comment if you need more information.
You may want to simplify your events by creating subs and standardizing the code instead of embedding it directly into the button click. I have created a working example of how to load directories and files dynamically.
I would also recommend doing a isolated experiment, you can easily throw together a test project to isolate the directories in question and the layout you are trying to achieve. It could be that there are other events causing noise in your debugging.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
load_dirs(ListBox2, "H:\")
End Sub
Sub load_files(LB As ListBox,
IO_dir As String)
Dim Files_ = IO.Directory.GetFiles(IO_dir)
With LB.Items
.Clear()
.AddRange(Files_)
End With
End Sub
Sub load_dirs(LB As ListBox,
IO_dir As String)
Dim dir_ = IO.Directory.GetDirectories(IO_dir)
With LB.Items
.Clear()
.AddRange(dir_)
End With
End Sub
Private Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox2.SelectedIndexChanged
Try
Dim lb As ListBox = sender
load_files(ListBox1, lb.SelectedItem)
Catch ex As Exception
End Try
End Sub
End Class

Interact with form while working in the background

The goal is that the form should still be clickable while the code runs (mainly to be able to break the code) AND keeps getting updated by the running code at the same time. First part works, not the second part.
Public Class FormMain
Public PleaseStopAll As Boolean
Private Sub BreakCode_Click(sender As Object, e As EventArgs) Handles BreakCode.Click
PleaseStopAll = True
End Sub
Private Sub Testeur_Click(sender As Object, e As EventArgs) Handles Testeur.Click
PleaseStopAll = False
ThreadPool.QueueUserWorkItem(AddressOf LaunchTask, 0)
End Sub
Sub LaunchTask(o As Object)
TheTask(Me)
End Sub
Public Sub ShowSomeMessage(msg As String
Me.Displayer.text = msg
End Sub
End Class
Module TaskDoer
Sub TheTask(MyMenu As FormMain)
For i As Integer = 0 To 42
whatever() 'some tasks that cannot be interrupted safely
MyMenu.ShowSomeMessage("task #" & CStr(i) & " over") ' Here is the error
If MyMenu.PleaseStopAll Then Exit For
Next
End Sub
End Module
The "status update" line tells that this task cannot interact with its parent task.
If I remove this line, the code runs just fine : code breaks when I ask to, and form remains clickable.
I tried with BackgroundWorker, but it needs actual progression to report something with ReportProgress (in increasing %) do I cannot just give a status (as string)
Pass anything else via a variable or structure
BackgroundWorker1.WorkerReportsProgress = True '<Make sure this is set
BackgroundWorker1.RunWorkerAsync()
Private SomeStatus As String
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
SomeStatus = "Hello"
BackgroundWorker1.ReportProgress(0)
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ShowSOmeMEssage(SomeStatus)
End Sub
FYI: The progress thing is only so you can call a routine on the main UI thread. You can set as many thread-safe variables as you want on the form from within the backgroundworker. It's just updating the UI that's bothersome, though there is a workaround for that too.

Changing a Control's property via a delegate

First off, pardon me if my English is bad, I'm not a native English speaker.
I'm fairly new to programming and I'm trying to teach myself VB.NET
I came across a problem while trying to learn about Delegates. (see code below)
What I'm trying to accomplish is to update a specified Control's text property via a thread. However, as soon as I start the thread, I get an ArgumentException Error. I have completely no idea what's wrong. Anybody have an idea what i've done wrong here?
Public Class Form1
Delegate Sub myDelegate1(ByVal s_Name As Control, ByVal s_txt As String)
Public txtUpdate As New myDelegate1(AddressOf upd_ControlTextProperty)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = vbnullstring
End Sub
Private Sub upd_ControlTextProperty(ByVal ControlName As Control, ByVal txt As String)
ControlName.Text = txt
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thread1 As New Threading.Thread(AddressOf threadstart)
thread1.IsBackground = True
thread1.Start()
End Sub
Private Sub threadstart()
Me.Invoke(Me.txtUpdate, New Object(), {Label1, "This is Label 1"})
End Sub
End Class
As TheValyreanGroup said, your delegate is supposed to accept two arguments, and you pass it three :
Me.Invoke(Me.txtUpdate, New Object(), {Label1, "This is Label 1"})
^-1--------^ ^-2--------^ ^-3-----------------------^
So just remove the New Object() thing, and transform this {Label1, ...} into just a string :
Me.Invoke(Me.txtUpdate, "This is Label 1")
OK Better that way.
On a second hand, what you are doing is not very usefull.
You create a new Thread from your UI Thread.
With this new Thread, you invoke back the UI Thread and you stop your Thread...
Remember that a Control can be updated only by the Thread who created the Form (the UI thread).
Unless you have a good reason to work with your background thread, you can resume your code to :
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = vbnullstring
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Label1.Text = "This is Label 1"
End Sub
End Class
UPDATE
(from comments)
To make it more clear, here is a schema (that I took on https://androidkennel.org/android-networking-tutorial-with-asynctask/, if any restrictions apply I will remove the image)
The Main UI Thread is used for things :
React to user events (clicks, inputs...) and start background threads that will do the process
Update the User Interface when the background thread is over or during the task.
When I say what you're doing is not usefull is because your background thread does not do any processing, it just signals the UI thread to update the UI...
I would try this approach. upd_ControlTextProperty can be called successfully either from the UI thread or your new thread.
Public Class Form1
Delegate Sub myDelegate1(ByVal s_Name As Control, ByVal s_txt As String)
Public txtUpdate As New myDelegate1(AddressOf upd_ControlTextProperty)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = ""
End Sub
Private Sub upd_ControlTextProperty(ByVal ControlName As Control, ByVal txt As String)
If Me.InvokeRequired = True Then
Me.Invoke(txtUpdate, New Object() {ControlName, txt})
Else
ControlName.Text = txt
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thread1 As New Threading.Thread(AddressOf threadstart)
thread1.IsBackground = True
thread1.Start()
End Sub
Private Sub threadstart()
upd_ControlTextProperty(Label1, "This is Label 1")
End Sub
End Class

Copying multiple sources to single destination

I am new to VB.net and am trying to create a backup utility to copy a set of local folders to a single backup destination (on usb for example) with a progress bar. After googling for the last few hours I can only find examples of single folder to single destination.
Can anyone point me in the direction of an example?
UPDATE:
Based of #Werdna example, I have created a simple FOR EACH NEXT loop. However the next issue is that only the files within the source directories are being copied to the target directory, rather than the folders and all their contents. Can anyone see where I am going wrong?
Public Class Form1
Private Sub Start_Click(sender As Object, e As EventArgs) Handles Start.Click
Dim destination = "E:\Backup Folder"
Dim sources As New List(Of String)
sources.Add("D:\Profiles\Users\Desktop")
sources.Add("D:\Profiles\Users\Mail")
sources.Add("D:\Profiles\Users\Downloads")
For Each source As String In sources
My.Computer.FileSystem.CopyDirectory(source, destination)
Next
MessageBox.Show("Copy Completed")
End Sub
End Class
Also, what is the best method to use the FOR EACH NEXT loop to count the number of files to be copied? I would like to output the amount to a label as well as use it for a progressbar as the utility evolves.
here is something that i have quickly written up for you to give you some idea on how i went about completing your task. You will need to add 2 listboxes to your application, a folder open dialog and a few buttons, this might not be what you are looking for, unfortunately you cant select multiable folders with the other dialogs, however nevertheless, take a look below, the code isn't completely as there is always something to do, however this should lead you in the right direction hopefully!
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each item In IO.Directory.GetDirectories("C:\")
ListBox1.Items.Add(item)
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each item In ListBox1.SelectedItems
ListBox2.Items.Add(item & "\")
Next
ListBox1.SelectedItem = Nothing
End Sub
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
ListBox2.Items.Remove(ListBox2.SelectedItem)
ListBox2.SelectedItem = Nothing
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
For Each item In IO.Directory.GetDirectories(ListBox1.SelectedItem)
ListBox1.Items.Clear()
ListBox1.Items.Add(item)
Next
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
If FolderBrowserDialog1.ShowDialog = DialogResult.OK Then
My.Computer.FileSystem.CreateDirectory(FolderBrowserDialog1.SelectedPath)
For Each item In ListBox2.Items
My.Computer.FileSystem.CopyDirectory(item, FolderBrowserDialog1.SelectedPath)
Next
MessageBox.Show("Copy Completed.")
End If
End Sub
End Class
I wrote this in about 10 minutes, so i didnt get a change to do a progessbar, however if this is what you are looking for, then i am happy to help you add one to your program. Happy Coding!
UPDATE - BASED OFF YOUR NEW QUESTION
Public Class Form1
Dim NumberofFILEs As Integer = 0
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Dim Location1 = "C:\Backup Folder\TESTING\FOLDER"
Dim source As String = "C:\TESTING\FOLDER"
Dim source1 As String = "C:\TESTING1\FOLDER"
Dim Location2 = "C:\Backup Folder\TESTING1\FOLDER"
IO.Directory.CreateDirectory("C:\Backup Folder\TESTING\FOLDER")
IO.Directory.CreateDirectory("C:\Backup Folder\TESTING1\FOLDER")
For Each item In source
My.Computer.FileSystem.CopyDirectory(source, Location1, True)
Next
For Each item In source1
My.Computer.FileSystem.CopyDirectory(source1, Location2, True)
Next
MessageBox.Show("Completed")
For Each file In source1.Count & source.Count
NumberofFILEs += 1
Next
Label1.Text = NumberofFILEs
End Sub
End Class
Edit the locations and destinations to what you need. Also the True at the end of the copydirectory means that it will overwrite any files with the same name, eg it will update them pretty much
You can copy each folder to the destination, one at a time. Loop through the source folders and copy each one to the target folder.