Unable to start multiple thread - vb.net

I'm trying to run this code, but when running only the first n listbox looping , the others remain empty. I would like to run the subroutine code (an infinite loop) for each listbox in the form at the same time
This is the Main Form:
Public Class Form1
Private listSettings As New List(Of ListBox)
Private nameSettings As New List(Of Label)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Variabili Grafiche
Dim lSettings As ListBox
Dim i As Integer
i = 0
Dim n As Integer
Dim width As Integer
Dim height As Integer
'Variabili Lettura File
Dim path As New IO.DirectoryInfo("C:\BatchDomoLake\config\")
Dim diar1 As IO.FileInfo() = path.GetFiles()
Dim dra As IO.FileInfo
'Lettura File e Grafica
For Each dra In diar1
n = n + 1
Next
Dim T(n) As Thread
width = (Me.Size.Width() / n)
height = (Me.Size.Height() / 2)
For Each dra In diar1
lSettings = New ListBox
With lSettings
.Location = New Point(10 + (width * i), 40)
.Name = "lst" & dra.Name.Replace(".conf", "")
.Size = New Size(width - 35, height - 40)
.Visible = True
End With
Me.listSettings.Add(lSettings)
Me.Controls.Add(lSettings)
Dim reader As StreamReader = My.Computer.FileSystem.OpenTextFileReader(dra.FullName)
Dim a As String
Do
a = reader.ReadLine
If a IsNot Nothing Then
lSettings.Items.Add(a)
End If
Loop Until a Is Nothing
'Thread
T(i) = New Threading.Thread(AddressOf snmpThread)
T(i).Start(lSettings)
i = i + 1
Next
End Sub
This is the subroutine i would like to use for each created listbox:
Private Delegate Sub snmpThreadDelegate(ByVal list As ListBox)
Private Sub snmpThread(ByVal list As ListBox)
If list.InvokeRequired Then
list.Invoke(New snmpThreadDelegate(AddressOf snmpThread), New Object() {list})
Else
Dim count As Integer
Dim i As Integer
count = list.Items.Count - 1
Do
For i = 0 To count Step 1
list.SetSelected(i, True)
Thread.Sleep(100)
Next
Loop
End If
End Sub

You cannot access/update your UI elements from background threads. Only main UI thread can do operations on your UI objects. This applies to both WinForms and WPF.
For WinForms, you can use BeginInvoke method and pass in a delegate that would be invoked in your UI thread. For WPF, you can use Dispatcher object.

Related

Using listbox to display an excel file?

So after some changes, I have used this code:
Dim XlApp = New Microsoft.Office.Interop.Excel.Application
Dim oBook As Object = XlApp.Workbooks.Open("C:\file.xlsx")
Dim oSheet As Object = oBook.Worksheets(1)
Private Sub form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim StartedRow As Integer
Dim TotalRows As Integer
TotalRows = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
For StartedRow = 1 To TotalRows
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, 1).text)
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, 2).text)
Next
MessageBox.Show("Succesful")
This one works but only shows two rows which is not in proper order, I need to show the whole file.
Sorry, since I'm new to Stack Overflow I find it kinda confusing for now :)
You need to declare your variables as specific types so you can use the properties and methods of that type. In general, don't let variables flap about as Object unless absolutely necessary.
I used .UsedRange to get row count. It seemed a bit simpler.
I used an interpolated string, indicated by the $, to get the range. In versions of Visual Studio prior to 2015 you will have to use
String.Format("A{0}", RowNum)
Excel is hard to get rid of. Therefore, put the Excel stuff in a separate method and add the two GC calls when the method returns.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DoExcelThing()
GC.Collect()
GC.WaitForPendingFinalizers()
'Even doing this will still leave a Zoombie Excel in Task Manager when debugging. (Hopefully not release)
MessageBox.Show("Succesful")
End Sub
Private Sub DoExcelThing()
Dim XlApp As New Microsoft.Office.Interop.Excel.Application
Dim oBook As Workbook = XlApp.Workbooks.Open("C:\file.xlsx")
Dim oSheet As Worksheet = CType(oBook.Worksheets(1), Worksheet)
Dim TotalRows = oSheet.UsedRange.Rows.Count
For RowNum = 1 To TotalRows
ListBox1.Items.Add(oSheet.Range($"A{RowNum}").Value)
ListBox1.Items.Add(oSheet.Range($"B{RowNum}").Value)
Next
End Sub
#Wakashio1234
This Will loop through all the cells..
TotalRows = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
Totalcolumns = XlApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.columns.Count
For StartedRow = 1 To TotalRows
For Startedcolumn = 1 To Totalcolumns
Me.ListBox1.Items.Add(oSheet.Cells(StartedRow, Startedcolumn).text)
Next
Next
MessageBox.Show("Successful")
The following code shows how to take out the data in Excel and put it into 'List(Of String())', then bind listbox control to the List and draw aligned columns of data in listbox.
Dim XlApp = New Microsoft.Office.Interop.Excel.Application
Dim oBook As Excel.Workbook = XlApp.Workbooks.Open("your file path")
Dim oSheet As Excel.Worksheet = oBook.Worksheets(1)
Dim lst As List(Of String()) = New List(Of String())()
Private RowHeight, RowWidth As Single
Private ColWidths As Single() = Nothing
Private Const RowMargin As Single = 10
Private Const ColumnMargin As Single = 10
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ListBox1.DrawMode = DrawMode.OwnerDrawVariable
ListBox1.DataSource = GetList()
End Sub
Private Function GetList() As List(Of String())
Dim rows As Integer = oSheet.UsedRange.Rows.Count
'If you want to display only two columns, set the value of 'cols' to 2
Dim cols As Integer = oSheet.UsedRange.Columns.Count
For r As Integer = 1 To rows
Dim Value As String() = New String(cols - 1) {}
For c As Integer = 1 To cols
Value(c - 1) = oSheet.Cells(r, c).Text
Next
lst.Add(Value)
Next
oBook.Close()
XlApp.Quit()
Return lst
End Function
Private Sub ListBox1_MeasureItem(sender As Object, e As MeasureItemEventArgs) Handles ListBox1.MeasureItem
If ColWidths Is Nothing Then
GetRowColumnSizes(e.Graphics, ListBox1.Font, lst, RowHeight, ColWidths)
For i As Integer = 0 To ColWidths.Length - 1
ColWidths(i) += ColumnMargin
Next
RowHeight += RowMargin
RowWidth = ColWidths.Sum()
End If
e.ItemHeight = CInt(RowHeight)
e.ItemWidth = CInt(RowWidth)
End Sub
Private Sub ListBox1_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ListBox1.DrawItem
Dim values As String() = CType(ListBox1.Items(e.Index), String())
e.DrawBackground()
If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
DrawRow(e.Graphics, ListBox1.Font, SystemBrushes.HighlightText, Nothing, e.Bounds.X, e.Bounds.Y, RowHeight, ColWidths, values, False)
Else
DrawRow(e.Graphics, ListBox1.Font, Brushes.Black, Nothing, e.Bounds.X, e.Bounds.Y, RowHeight, ColWidths, values, False)
End If
End Sub
Private Sub GetRowColumnSizes(ByVal gr As Graphics, ByVal font As Font, ByVal values As List(Of String()), ByRef max_height As Single, ByRef col_widths As Single())
Dim num_cols As Integer = values(0).Length
col_widths = New Single(num_cols - 1) {}
max_height = 0
For Each row As String() In values
For col_num As Integer = 0 To num_cols - 1
Dim col_size As SizeF = gr.MeasureString(row(col_num), font)
If col_widths(col_num) < col_size.Width Then col_widths(col_num) = col_size.Width
If max_height < col_size.Height Then max_height = col_size.Height
Next
Next
End Sub
Private Sub DrawRow(ByVal gr As Graphics, ByVal font As Font, ByVal brush As Brush, ByVal box_pen As Pen, ByVal x0 As Single, ByVal y0 As Single, ByVal row_height As Single, ByVal col_widths As Single(), ByVal values As String(), ByVal draw_box As Boolean)
Dim rect As RectangleF = New RectangleF()
rect.Height = row_height
Using sf As StringFormat = New StringFormat()
Dim x As Single = x0
For col_num As Integer = 0 To values.Length - 1
sf.Alignment = StringAlignment.Near
sf.LineAlignment = StringAlignment.Center
rect.X = x
rect.Y = y0
rect.Width = col_widths(col_num)
gr.DrawString(values(col_num), font, brush, rect, sf)
If draw_box Then gr.DrawRectangle(box_pen, rect.X, rect.Y, rect.Width, rect.Height)
x += col_widths(col_num)
Next
End Using
End Sub
Result of my test:
For more details you can see: Make an owner-drawn ListBox that justifies columns in C#

index was outside the bounds

my code is not working please, help
Private Sub Form3_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
Dim lblControls() As Label = {lblName, lblID, lblGender, lblBirthDate, lblCity, lblContactNumber, lblAddress, lblHistory}
Dim sReader As New System.IO.StreamReader(Form1.pathStorage & "Patients\" & selectedPatient & "\patientInfo.txt")
Dim strValues(11) As String
Dim counter As Integer = 0
While sReader.Peek <> 1
strValues(counter) = sReader.ReadLine
counter += 1
End While
counter = 0
For Each lbl As Label In lblControls
lbl.Text = strValues(counter)
counter += 1
Next
sReader.Close()
I have changed to the File class to accomplish your task. You do not need to declare an instance of File. A StreamReader needs to be not closed but disposed. A Using...End Using block or Try...Finally...End Try to ensure the closing and disposing of a StreamReader so the File class is easier to use.
You do not need to declare a size for the strValues because it is initialize at declaration. I added a check that the two arrays are the same size.
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
Dim lblControls() As Label = {lblName, lblID, lblGender, lblBirthDate, lblCity, lblContactNumber, lblAddress, lblHistory}
Dim path As String = pathStorage & "Patients\" & selectedPatient & "\patientInfo.txt"
Dim strValues = File.ReadAllLines(path) 'Returns a string array
If lblControls.Length <> strValues.Length Then
MessageBox.Show("Labels do not equal data.")
Return
End If
Dim counter As Integer = 0
For Each lbl As Label In lblControls
lbl.Text = strValues(counter)
counter += 1
Next
End Sub

VB.net multithreading for loop, parallel threads

I have a simple form with 2 RichTextBoxes and 1 button, the code grabs the url address from RichTextBox1 and phrases the page for the title field using regex and appends it to RichTextBox2. I want to multithread everything in such way that none of the url's are skipped and the thread numbers can be set ( according to the system free resources ) For example, let's say 10 threads to run in parallel. I searched everything and the best that I managed to do is run everything in a background worker and keep the GUI from freezing while working. A short code sample will be of much help, I am a beginner in VB.net.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For i = 0 To RichTextBox1.Lines.Length - 1
If RichTextBox1.Lines(i).Contains("http://") Then
Dim html As String = New System.Net.WebClient() _
.DownloadString(RichTextBox1.Lines(i))
Dim pattern As String = "(?<=\<title\>)([^<]+?)(?=\</title\>)"
Dim match As System.Text.RegularExpressions.Match = _
System.Text.RegularExpressions.Regex.Match(html, pattern)
Dim title As String = match.Value
RichTextBox2.AppendText(title & vbCrLf)
End If
Next
End Sub
End Class
Updated code ( throwing "Index was outside the bounds of the array." errors. )
Imports System
Imports System.Threading
Public Class Form1
Public Sub test(ByVal val1 As String, ByVal val2 As String)
Dim zrow As String
zrow = RichTextBox1.Lines(val1)
If zrow.Contains("http://") Then
Dim html As String = New System.Net.WebClient().DownloadString(zrow)
Dim pattern As String = "(?<=\<title\>)([^<]+?)(?=\</title\>)"
Dim match As System.Text.RegularExpressions.Match = System.Text.RegularExpressions.Regex.Match(html, pattern)
Dim title As String = match.Value
RichTextBox2.AppendText(val2 & title & vbCrLf)
End If
End Sub
Public Sub lastfor(ByVal number)
Dim start As Integer = number - 100
For x = start To number - 1
Try
test(x, x)
RichTextBox2.AppendText(x & RichTextBox1.Lines(x).Trim & vbCrLf)
Catch ex As Exception
'MsgBox(ex.Message)
RichTextBox3.AppendText(ex.Message & vbCrLf & vbCrLf)
End Try
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim TotalLines As String = RichTextBox1.Lines.Length - 1
Dim TotalThreads As Integer = 10
Dim LinesPerThread As Integer = TotalLines / TotalThreads
Dim increment As String = LinesPerThread
Dim zdata(TotalThreads) As String
For i = 0 To TotalThreads - 1
zdata(i) = increment
increment = increment + LinesPerThread
Next
Dim lst As New List(Of Threading.Thread)
For Each bump As String In zdata
Dim t As New Threading.Thread(Function(l As String)
'Do something with l
'Update GUI like this:
If bump = String.Empty Or bump Is Nothing Then
Else
lastfor(l)
'MsgBox(l)
End If
End Function)
lst.Add(t)
t.Start(bump)
Next
'test(1)
End Sub
End Class
There are two ways two achieve this:
First, if you are using .NET 4.0, you could use a Parallel.ForEach loop:
Parallel.ForEach(RichTextBox1.Lines, Function(line As String)
' Do something here
' To update the GUI use:
Me.Invoke(Sub()
' Update GUI like this...
End Sub)
Return Nothing
End Function)
The other way is to do this manually (and you will have slightly more control):
Dim lst As New List(Of Threading.Thread)
For Each line In RichTextBox1.Lines
Dim t As New Threading.Thread(Function(l As String)
'Do something with l
'Update GUI like this:
Me.Invoke(Sub()
'Update Gui...
End Sub)
End Function)
lst.Add(t)
t.Start(line)
Next
Both of these are very crude, but will get the job done.
EDIT:
Here is a sample code that will control the number of threads:
Dim lst As New List(Of Threading.Thread)
Dim n As Integer = 1 ' Number of threads.
Dim npl As Integer = RichTextBox1.Lines / n
Dim seg As New List(Of String)
For Each line In RichTextBox1.Lines
For i = npl - n To npl
seg.Add(RichTextBox1.Lines.Item(i))
Next
Dim t As New Threading.Thread(Function(l As String())
For Each lin In l
' TO-DO...
Next
'Do something with l
'Update GUI like this:
Me.Invoke(Sub()
'Update Gui...
End Sub)
End Function)
lst.Add(t)
t.Start(seg.ToArray())
Next
*The above code might have bugs.

VB Tab control in draw.mode

I am trying to make the tabs show horizontally on the right side of my form. I can't use tabcontrol from tool box because of how the text displays.
I am using a code that I found to help me. But after exhausting all of my resources I can't seem to get the code to point to the tabPages collection. I have entries in there but the tabs show up blank.
Public Sub New()
tabControl1 = New TabControl()
Dim tabPage1 As New TabPage()
' Sets the tabs to be drawn by the parent window Form1.
' OwnerDrawFixed allows access to DrawItem.
tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed
tabControl1.Controls.Add(tabPage1)
tabControl1.Location = New Point(25, 25)
tabControl1.Size = New Size(250, 250)
tabPage1.TabIndex = 0
myTabRect = tabControl1.GetTabRect(0)
ClientSize = New Size(300, 300)
Controls.Add(tabControl1)
AddHandler tabControl1.DrawItem, AddressOf OnDrawItem
End Sub!
tab Example
You can set the .Alignment property of the TabControl to Left to use horizontal tabs.
If you don't like that, try a FlowLayoutPanel with a separate TabControl for each tab, e.g.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Dim flp As New FlowLayoutPanel
flp.Dock = DockStyle.Left
flp.AutoSize = True
flp.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowOnly
Me.Controls.Add(flp)
For i As Integer = 0 To 5
Dim tbc As New TabControl
Dim tbp As New TabPage("Tab" & i.ToString)
tbc.TabPages.Add(tbp)
flp.Controls.Add(tbc)
Next i
End Sub
End Class
I ended up compiling this code from different sources to get this working,
Private Sub TabControl1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles TabControl1.DrawItem
Dim g As Graphics
Dim sText As String
Dim iX As Integer
Dim iY As Integer
Dim sizeText As SizeF
Dim ctlTab As TabControl
Dim r As New RectangleF(e.Bounds.X, e.Bounds.Y + 2, e.Bounds.Width, e.Bounds.Height - 2)
ctlTab = CType(sender, TabControl)
g = e.Graphics
sText = ctlTab.TabPages(e.Index).Text
sizeText = g.MeasureString(sText, ctlTab.Font)
iX = e.Bounds.Left + 6
iY = e.Bounds.Top + (e.Bounds.Height - sizeText.Height) / 2
g.DrawString(sText, ctlTab.Font, Brushes.Black, iX, iY)
End Sub
The text doesn't show up in the RAD but it does when I debug/run it.
Many thanks to LUC001 # http://www.dreamincode.net/forums/topic/125792-how-to-make-vertical-tabs/

How to find and update a matching control once a file exists

I have written a WinForm project which displays a ListBox containing a list of file names. When the user clicks a submit button, the application dynamically loads and displays one PictureBox control for each file and then waits while they are processed. As PDF files are generated for each one, the matching PictureBox for that file needs to be updated to display an image.
Here's what I have so far:
Private Sub ButtonSubmit_Click(sender As System.Object, e As System.EventArgs) Handles ButtonSubmit.Click
Dim x As Integer = 790
Dim y As Integer = 91
For i As Integer = 0 To ListBox1.Items.Count - 1
Dim key As String = ListBox1.Items(i).ToString()
'adds picturebox for as many listbox items added
Dim MyPictureBox As New PictureBox()
MyPictureBox.Name = "pic" + key
MyPictureBox.Location = New Point(x, y)
MyPictureBox.Size = New Size(12, 12)
MyPictureBox.SizeMode = PictureBoxSizeMode.StretchImage
Me.Controls.Add(MyPictureBox)
MyPictureBox.Image = My.Resources.Warning1
ToolTipSpooling.SetToolTip(MyPictureBox, "Creating PDF...")
x += 0
y += 13
Next i
Call CheckPDFs()
End Sub
Public Sub CheckPDFs()
Dim ListboxTicketIDs = (From i In ListBox1.Items).ToArray()
For Each Item In ListboxTicketIDs
Dim ID = Item.ToString
Dim Watcher As New FileSystemWatcher()
Watcher.Path = "C:\Temp\"
Watcher.NotifyFilter = (NotifyFilters.Attributes)
Watcher.Filter = ID + ".pdf"
AddHandler Watcher.Changed, AddressOf OnChanged
Watcher.EnableRaisingEvents = True
Next
End Sub
Private Sub OnChanged(source As Object, e As FileSystemEventArgs)
Dim p As PictureBox = CType(Me.Controls("pic" + ListBox1.Items.ToString()), PictureBox)
p.Image = My.Resources.Ok1
End Sub
I'm having trouble changing the PictureBox to a different picture once the item(s) listed in the listbox are present, based on the FileSystemWatcher. For instance, the files are not always created in the same order as they exist in the ListBox.
EDIT
Working code below.
Public Class Form1
Private WithEvents Watcher As FileSystemWatcher
Public Sub CheckPDFs()
For i As Integer = 0 To ListBox1.Items.Count - 1
Watcher = New FileSystemWatcher()
Watcher.SynchronizingObject = Me
Watcher.Path = "C:\Temp\"
Watcher.NotifyFilter = NotifyFilters.Attributes
Watcher.Filter = "*.pdf"
Watcher.EnableRaisingEvents = True
Next
End Sub
Private Sub Watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs) Handles Watcher.Changed
Dim key As String = Path.GetFileNameWithoutExtension(e.Name)
Dim p As PictureBox = CType(Me.Controls("pic" + key), PictureBox)
p.Image = My.Resources.Ok
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ListBox1.Items.Add(TextBox1.Text)
TextBox1.Text = ""
Dim x As Integer = 5
Dim y As Integer = 5
For i As Integer = 0 To ListBox1.Items.Count - 1
Dim key As String = ListBox1.Items(i).ToString()
'adds picturebox for as many listbox items added
Dim MyPictureBox As New PictureBox()
MyPictureBox.Name = "pic" + key
MyPictureBox.Location = New Point(x, y)
MyPictureBox.Size = New Size(15, 15)
MyPictureBox.SizeMode = PictureBoxSizeMode.StretchImage
Me.Controls.Add(MyPictureBox)
MyPictureBox.Image = My.Resources.Info
x += 0
y += 18
Next i
Call CheckPDFs()
End Sub
First of all, you don't need to create multiple file watchers. You just need a single file watcher to watch for any changes to the folder. I would recommend declaring it as a private field at the top of your form using the WithEvents keyword so you don't have to worry about adding and removing event handlers.
Next, when the watcher raises the changed event, you can get the file name of the file that changed by looking at the properties of the event args object. You need to get the name of the file that changed and then use the file name as the key to finding the matching picture box control.
Public Class Form1
Private WithEvents Watcher As FileSystemWatcher
Public Sub CheckPDFs()
Watcher = New FileSystemWatcher()
Watcher.Path = "C:\Temp\"
Watcher.NotifyFilter = NotifyFilters.Attributes
Watcher.Filter = "*.pdf"
End Sub
Private Sub Watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs) Handles Watcher.Changed
Dim key As String = Path.GetFileNameWithoutExtension(e.Name)
Dim p As PictureBox = CType(Me.Controls("pic" + key), PictureBox)
p.Image = My.Resources.Ok1
End Sub
End Class
However, since you say in a comment below that the file name will not be the same as the text in the listbox, but that it will merely start with that text, you could do something like this, instead:
Private Sub Watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs) Handles Watcher.Changed
Dim p As PictureBox = Nothing
For Each item As Object In ListBox1.Items
If e.Name.StartsWith(item.ToString()) Then
p = CType(Me.Controls("pic" + item.ToString()), PictureBox)
Exit For
End If
Next
If p IsNot Nothing Then
p.Image = My.Resources.Ok1
End If
End Sub