Change label text in foreach loop - vb.net

i want to update a label while a foreach-loop.
The problem is: the program waits until the loop is done and then updates the label.
Is it possible to update the label during the foreach-loop?
Code:
Dim count as Integer = 0
For Each sFile as String in Files
'ftp-code here, works well
count = count+1
progressbar1.value = count
label1.text = "File " & count & " of 10 uploaded."
next
Thanks in advance

Label is not updated because UI thread is blocked while executing your foreach loop.
You can use async-await approach
Private Async Sub Button_Click(sender As Object, e As EventArgs)
Dim count as Integer = 0
For Each sFile as String in Files
'ftp-code here, works well
count = count+1
progressbar1.value = count
label1.text = "File " & count & " of 10 uploaded."
Await Task.Delay(100)
Next
End Sub
Because you will work with Ftp connections, which is perfect candidate for using async-await.
The Await line will release UI thread which will update label with new value, and continue from that line after 100 milliseconds.
If you will use asynchronous code for ftp connection , then you don't need Task.Delay

You've already accepted an answer but just as an alternative a BackgroundWorker can also be used for something like this. In my case the FTP to get the original files happens very quickly so this snippet from the DoWork event is for downloading those files to a printer.
Dim cnt As Integer = docs.Count
Dim i As Integer = 1
For Each d As String In docs
bgwTest.ReportProgress(BGW_State.S2_UpdStat, "Downloading file " & i.ToString & " of " & cnt.ToString)
Dim fs As New IO.FileStream(My.Application.Info.DirectoryPath & "\labels\" & d, IO.FileMode.Open)
Dim br As New IO.BinaryReader(fs)
Dim bytes() As Byte = br.ReadBytes(CInt(br.BaseStream.Length))
br.Close()
fs.Close()
For x = 0 To numPorts - 1
If Port(x).IsOpen = True Then
Port(x).Write(bytes, 0, bytes.Length)
End If
Next
If bytes.Length > 2400 Then
'these sleeps are because it is only 1-way comm to printer so I have no idea when printer is ready for next file
System.Threading.Thread.Sleep(20000)
Else
System.Threading.Thread.Sleep(5000)
End If
i = i + 1
Next
In the ReportProgress event... (of course, you need to set WorkerReportsProgress property to True)
Private Sub bgwTest_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwTest.ProgressChanged
Select Case e.ProgressPercentage
'BGW_State is just a simple enum for the state,
'which determines which UI controls I need to use.
'Clearly I copy/pasted from a program that had 15 "states" :)
Case BGW_State.S2_UpdStat
Dim s As String = CType(e.UserState, String)
lblStatus.Text = s
lblStatus.Refresh()
Case BGW_State.S15_ShowMessage
Dim s As String = CType(e.UserState, String)
MessageBox.Show(s)
End Select
End Sub

Is it not enough to use Application.DoEvents()? This clears the build up and you should be able to see the text fields being updated very quickly.

Related

How to use a loop to print a statement a number of times defined by user inputs on visual basic

I would like to make a simple program that does the following
Takes a text input of a string and the number of times it needs to be looped by the program user and produces that loop to output that string the number of times stated. My loop isn't doing that. (New to visual basic loops)
Kindly correct advise where i have gone wrong below:
Public Class Form1
Private Sub cmdLoop_Click(sender As Object, e As EventArgs) Handles cmdLoop.Click
Dim newput As String
Dim numberr As Integer
Dim counter As Integer
newput = txtStatement.Text
numberr = TxtRepeatNum.Text
For counter = 1 To numberr
lbloutput.Text = newput & vbCrLf
Next
End Sub
Your loop is writing numberr times the same string into the text box, with every iteration it overwrites the previous content. Change it to
lbloutput.Text = ""
For counter = 1 To numberr
lbloutput.Text = lbloutput.Text & newput & vbCrLf
Next

How to check if Variable value is increaseing vb.net?

Hi I have listview and lable ,the lable is showing how many items in the listview
I want if the lable is increased will show msgbox
* may be it will increased alot such as 100 or 1 whatever
* It will execute the command just one if the value is increased
* it must be execute the msgbox any time if the value is increased
I'm sorry for my bad language
thank you
the code :
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim b As String = New WebClient().DownloadString(TextBox2.Text + "/Getinfo.php")
Dim mc As MatchCollection = Regex.Matches(b, "(\w+):")
For Each x As Match In mc
Dim infoName As String = x.Groups(1).Value
Try
Dim download As String = New WebClient().DownloadString(TextBox2.Text + "/sender/info/" & infoName & "/info.txt")
Dim f As String() = download.Split(":")
Dim ls As New ListViewItem
ls.Text = infoName
For Each _x As String In f
ls.SubItems.Add(_x)
Next
Dim hTable As Hashtable = New Hashtable()
Dim duplicateList As ArrayList = New ArrayList()
Dim itm As ListViewItem
For Each itm In ListView1.Items
If hTable.ContainsKey(itm.Text) Then 'duplicate
duplicateList.Add(itm)
Else
hTable.Add(itm.Text, String.Empty)
End If
Next
'remove duplicates
For Each itm In duplicateList
itm.Remove()
Next
ListView1.Items.Add(ls)
'here I want to excute the command if the value is increased but it's timer and the code will be execute every time
Catch
End Try
Next
End Sub
At the top of your function do
Dim oldlength = ListView1.Items.Count
At the bottom do
If oldlength < ListView1.Items.Count Then
'here I want to excute the command if the value is increased
End If
I'm pretty sure that your MessageBox isn't really what you want but a debugging example because you think if you can get the MessageBox at the right time you've solved one of your problems (which is true).

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

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

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.