How to stop an 'Object reference not set to an instance of an object' when creating textboxes at run time in VB - vb.net

For a project I require the creation of textboxes at run time, to this end I have used this code;
Dim AOP As Integer = GlobalVariables.AOP
Dim tb(11, 11) As TextBox
Dim LocationX As Integer
Dim LocationY As Integer
Dim count As Integer = 2
LocationX = 10
LocationY = 10
tb(1, 0).Name = "txtRoot"
tb(1, 0).Size = New Size(170, 20)
tb(1, 0).Location = New Point(LocationX, LocationY)
tb(1, 0).Visible = False
I am then able to loop it using this For loop;
For i = 1 To AOP
If count > AOP Then
Else
If i = AOP Then
LocationY = LocationY + 10
LocationX = 10
tb(count, 0).Name = "txtXValue" & 0 & "YValue" & count
tb(count, 0).Size = New Size(170, 20)
tb(count, 0).Location = New Point(LocationX, LocationY)
Controls.Add(tb(count, 0))
count = count + 1
i = 1
Else
LocationX = LocationX + 10
tb(count, i).Name = "txtXValue" & i & "YValue" & count
tb(count, i).Size = New Size(170, 20)
tb(count, i).Location = New Point(LocationX, LocationY)
Controls.Add(tb(count, i))
End If
End If
Next
This works in theory, however, when the code reaches the line;
tb(1, 0).Name = "txtRoot"
It returns the error 'Object reference not set to an instance of an object'
I am wondering if there is anyway around this? or if this way of creating textboxes isn't possible? Any help would be appreciated.

You have initialized the array but not added initialized TextBoxes, the array contains currently only Nothing. Also note that arrays are zero based, so the first TextBox is in tb(0, 0).
For i As Int32 = 0 To tb.GetLength(0) - 1
For ii As Int32 = 0 To tb.GetLength(1) - 1
tb(i, ii) = New TextBox()
tb(i, ii).Visible = False
' .... '
Next
Next
Now all are initialized.

Related

Positioning similar type objects on form programmatically in vb.net

I have a from which contains some PictureBoxes. They can be from one to many.
I create them at run-time depending on the existence of specific folders.
I create them and place them the one next to each other. This means that with a scrollable form I can view all of them easy.
My question is this: How do I position them in "rows"? For a specific form size, there can be 5 labels next to each other and infinite rows of them
How do I achieve this?
My (working) code:
Public allSeries As IEnumerable(Of String) = System.IO.Directory.EnumerateDirectories(root)
For i As Integer = 1 To allSeries.Count
Dim pb As New Windows.Forms.PictureBox With {
.Name = "pb" & i.ToString,
.Size = New Drawing.Size(500, 500),
.Location = New Point(5, 5),
.BorderStyle = BorderStyle.FixedSingle,
.SizeMode = PictureBoxSizeMode.Zoom,
.Image = Image.FromFile(allSeries(i - 1).ToString + "\sImage.jpg"),
.Tag = traveldestination, 'Store Directory path
.Cursor = Cursors.Hand}
Me.Controls.Add(pb)
For i As Integer = 2 To allSeries.Count
With Me
.Controls.Item("pb" + i.ToString).Left = .Controls.Item("pb" + (i - 1).ToString).Left + 520
End With
Next
My (bad) and (not workng) code:
Dim pbsOnForm As Integer = 13 'total PictureBoxes on Form /for this instance
Dim pbsOnRow As Integer = 5 'PictureBoxes that "fit" in a row /for this intance)
For i As Integer = 1 To pbsOnForm
If i <= pbsOnRow Then
Me.Controls.Item("pb" + i.ToString).Top = Me.Controls.Item("pb" + i.ToString).Top
End If
If i > pbsOnRow And i <= 10 Then
Me.Controls.Item("pb" + i.ToString).Top = Me.Controls.Item("pb" + (i - pbsOnRow).ToString).Top
End If
Works, but when the PcrureBoxes will be more than 10, I do not know......
While using the TableLayoutPanel would fulfill most cases for this and is probably the best way to achieve this, here is some code to align the PictureBox's in row / column.
First we want to setup a method to handle the positioning. We need some variables scoped to the Form.
Dim counter As Integer = 0
Dim xPos As Integer = 5
Dim yPos As Integer = 5
Now we use these variables in a method that sets the location.
Private Sub PositionPictureBox(pb As PictureBox, Optional imgPerRow As Integer = 5)
pb.Location = New Point(xPos, yPos)
counter += 1
If counter = imgPerRow Then
counter = 0
xPos = 5
yPos = pb.Location.Y + pb.Height + 5
Else
xPos = pb.Location.X + pb.Width + 5
End If
End Sub
Finally we call the method when the PictureBox is instantiated.
For i As Integer = 1 To allSeries.Count
Dim pb As New Windows.Forms.PictureBox
With pb
.Name = "pb" & i.ToString()
.Size = New Drawing.Size(50, 50)
.Location = New Point(5, 5)
.BorderStyle = BorderStyle.FixedSingle
.SizeMode = PictureBoxSizeMode.Zoom
.Image = Image.FromFile("...")
.Tag = allSeries(i)
.Cursor = Cursors.Hand
End With
PositionPictureBox(pb)
Me.Controls.Add(pb)
Next

How to dynamicallty create multiple controls at runtime

I am trying to add multiple labels to a userform at runtime
It's for the player names of a board game; and until the game starts the number of players are not known. I have managed to figure out for myself how to use the dynamic array function to create the list of players. I used a For.....Next loop to add the player names. I thought I could do that to add the labels to the form, but it only adds one. Depending on where the new control type is declared, it either adds the first player only, or the last player
This code produces one label only within the groupbox, the last player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
Dim newText As New Label
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
This one places only the first player
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Players_Num As Integer = InputBox("Enter the number of players")
Dim Players(Players_Num) As String
For i = 0 To Players_Num - 1
Players(i) = InputBox("Enter player name")
Next
'This piece of code was jsut for me to test that I was successfully using a For...Loop
'to add the players names, and will be deleted later on
For x = 0 To Players_Num - 1
MessageBox.Show(Players(x))
Next
For z = 0 To Players_Num - 1
Dim newText As New Label
newText.Name = "txt" & Players(z)
newText.Text = Players(z)
newText.Size = New Size(170, 20)
newText.Location = New Point(12 + 5, 12 + 5)
GroupBox1.Controls.Add(newText)
Next
End Sub
End Class
I've tried this in vs 2015 and 2019 Community
Where is it going wrong?
From the looks of the code, you are correctly creating the controls but their location is the same for all of them, essentially, they are being place one of top of the other, the first is hidden with the second, which is hidden with the third.
The line
newText.Location = New Point(12 + 5, 12 + 5)
needs to be modified to place the labels at different locations.
Perhaps, something like:
newText.Location = New Point(12 + 5, 12 + (z * 25))
This will vertically align the labels with a gap of 25 between them
You are placing them all in the same location
newText.Location = New Point(12 + 5, 12 + 5)
Use your 'z' index to place them at different locations in order to be able to see them
For me it is easier to contain controls in a TableLayoutPanel then add the TLP to what ever control collection, such as a GroupBox This way you can couple a Label with TextBox, for example. Here's an example how you can create controls from a DataTable. In your case you would only need 1 ColumnStyle for labels, I just thought I would show you a good practice for future shortcuts. (I rarely use the designer to place controls)
'Start test data
Dim DtTable As New DataTable
With DtTable
Dim NewDtRow As DataRow = .NewRow
For i As Integer = 0 To 25
Dim DtCol As New DataColumn With {.ColumnName = "Col" & i, .DataType = GetType(String)}
.Columns.Add(DtCol)
NewDtRow(DtCol.ColumnName) = "Test" & i
Next
.Rows.Add(NewDtRow)
End With
'End test data
Dim TLP1 As New TableLayoutPanel With {.Name = "TlpFields"}
With TLP1
.BorderStyle = BorderStyle.Fixed3D
.CellBorderStyle = TableLayoutPanelCellBorderStyle.Inset
.AutoScroll = True
.AutoSize = True
.RowStyles.Clear()
.ColumnStyles.Clear()
.Dock = DockStyle.Fill
.ColumnCount = 2
.ColumnStyles.Add(New ColumnStyle With {.SizeType = SizeType.AutoSize})
End With
For Each DtCol As DataColumn In DtTable.Columns
With TLP1
.RowCount += 1
.RowStyles.Add(New RowStyle With {.SizeType = SizeType.AutoSize})
'create labels
.Controls.Add(New Label With {
.Text = DtCol.ColumnName,
.Anchor = AnchorStyles.Right}, 0, .RowCount)
'create textboxs
Dim TxtBox As New TextBox With {
.Name = "TextBox" & DtCol.ColumnName,
.Size = New Size(170, 20),
.Anchor = AnchorStyles.Left}
'add binding
TxtBox.DataBindings.Add("Text", DtTable, DtCol.ColumnName)
.Controls.Add(TxtBox, 1, .RowCount)
End With
Next
Controls.Add(TLP1)

vb.net multithreading with multiple threads in a For..Next Loop

Hi folks I have the code below which i wanted to use all my processors cores. Originally, the code in the sub chekFileDupe was all within the For..Next loop in SearchForDupes sub and worked just fine on one thread. I'm a bit stuck as to create 4 threads that execute the checkFileDupe code so that i can check 4 items at a time, then wait till all 4 threads have completed. Then carry on with the next iteration of the loop.
This is the original code
Private Sub SearchForDupes()
Me.Refresh()
Dim totalrecords As Integer = dvSortedSearchResults.Count
Label1.Text = "Searching for duplicates"
Label1.Refresh()
dvSortedSearchResults.Sort = DataSortOrder
For i = 0 To totalrecords - 6 Step 4
Dim TrackDifference As Integer
Dim originalTrack, originalArtist, trackToCompare, artistToCompare As String
If i / 10 = CInt(i / 10) Then
Label2.Text = i.ToString + " of " + totalrecords.ToString
Label2.Refresh()
End If
originalArtist = mp3record(i, "Artist")
originalTrack = mp3record(i, "Track")
Dim ii As Integer = i + 1
While ii < totalrecords - 2
artistToCompare = mp3record(ii, "Artist")
trackToCompare = mp3record(ii, "Track")
TrackDifference = Difference(originalTrack, trackToCompare)
dvSortedSearchResults(ii).Item("Difference") = TrackDifference
'dgvSearchResults.Rows(ii).Cells("Difference").Value = trackdiff
If Difference(originalArtist, artistToCompare) < 6 Then
TrackDifference = Difference(originalTrack, trackToCompare)
If TrackDifference < 4 Then
dvSortedSearchResults(i).Item("Difference") = 999
dvSortedSearchResults(ii).Item("Difference") = TrackDifference
dvSortedSearchResults(ii).Item("chkdupe") = True
End If
Else
Exit While
End If
ii = ii + 1
End While
Next
Label2.Text = ""
Label1.Text = ""
End Sub
This is the first attempt at multithreading - probably naive but hey - everyone's new at something
Private Sub SearchForDupes()
'Dim query = (From record In dvSortedSearchResults Where record.Artist = "Abba" Select record).ToList
Me.Refresh()
Dim totalrecords As Integer = dvSortedSearchResults.Count
Label1.Text = "Searching for duplicates"
Label1.Refresh()
dvSortedSearchResults.Sort = DataSortOrder
Dim params(2) As Integer
For i = 0 To totalrecords - 6 Step 4
params(2) = totalrecords
params(1) = i
thread1 = New System.Threading.Thread(Sub() checkFileDupe(params))
thread1.Start()
params(1) = i + 1
thread2 = New System.Threading.Thread(Sub() checkFileDupe(params))
thread2.Start()
params(1) = i + 2
thread3 = New System.Threading.Thread(Sub() checkFileDupe(params))
thread3.Start()
params(1) = i + 3
thread4 = New System.Threading.Thread(Sub() checkFileDupe(params))
Next
Label2.Text = ""
Label1.Text = ""
End Sub
Private Sub checkFileDupe(params As Array)
Dim i As Integer = params(1)
Dim totalrecords As Integer = params(2)
Dim TrackDifference As Integer
Dim originalTrack, originalArtist, trackToCompare, artistToCompare As String
If i / 10 = CInt(i / 10) Then
Label2.Text = i.ToString + " of " + totalrecords.ToString
Label2.Refresh()
End If
originalArtist = mp3record(i, "Artist")
originalTrack = mp3record(i, "Track")
Dim ii As Integer = i + 1
While ii < totalrecords - 2
artistToCompare = mp3record(ii, "Artist")
trackToCompare = mp3record(ii, "Track")
TrackDifference = Difference(originalTrack, trackToCompare)
dvSortedSearchResults(ii).Item("Difference") = TrackDifference
'dgvSearchResults.Rows(ii).Cells("Difference").Value = trackdiff
If Difference(originalArtist, artistToCompare) < 6 Then
TrackDifference = Difference(originalTrack, trackToCompare)
If TrackDifference < 4 Then
dvSortedSearchResults(i).Item("Difference") = 999
dvSortedSearchResults(ii).Item("Difference") = TrackDifference
dvSortedSearchResults(ii).Item("chkdupe") = True
End If
ii = ii + 1
End While
End Sub Else
Exit While
End If
Actually, what I might advise you to do is forget about the code you have now and use a BackgroundWorker control. Start with just creating one thread to do the work and get that up and running. The advantage is that the background worker allows you to report progress back to the main UI thread. There should be plenty of tutorials out there.
I can try to tackle/indicate some of the problems in your current code, but to be honest there is a lot wrong with it.
You have a problem where you use the same underlying object, params(2), for each thread you create, so when you modify params you thereby modify the value that all the threads see. What you need to do is create a new array for each time you want to pass an argument to a new thread.
It's also extra confusing because you are using closures Sub () checkFileDupe(params), rather than using a correct signature for checkFileDupe and using Thread.Start to pass the argument.
I'd advise you to create a Structure to hold the arguments to your thread:
Private Structure FileDupeArguments
Public StartIndex As Integer
Public TotalRecords As Integer
End Structure
Then you can create a new thread via:
Dim params As FileDupeArguments
...
thread1 = New System.Threading.Thread(AddressOf checkFileDupe)
params = New FileDupeArguments With {.StartIndex = i, .TotalRecords = totalrecords}
thread1.Start(params)
And then declaring checkFileDupe as:
Sub checkFileDupe(argObj as Object)
Dim args As FileDupeArguments = CType(argObj, FileDupeArguments)
Dim i As Integer = args.StartIndex
Dim totalRecords As Integer = args.TotalRecords
...
End Sub
The important part here is that you send a new copy of FileDupeArguments to each thread. Also, no closures are needed.
There is an issue with you accessing controls from the threads you created.
For instance,
Label2.Text = i.ToString + " of " + totalrecords.ToString
Label2.Refresh()
will not work on a background thread and will give you errors. I'd advise you to not do direct progress reports from your worker threads. Like I mentioned, a BackgroundWorker will allow you to report back progress using events.
All the code that accesses dvSortedSearchResults suffers from the same or similar problems. If you access something from multiple threads, you need to apply locks. This is already more advanced and beyond the scope of this answer to explain.

Drawing an array of PictureBoxes in vb.net

I'm trying to draw an array of PictureBoxes, for testing I use the same picture for each picturebox.
But instead of showing the picture, it shows the color blue.
I would show you a picture, but I dont have 10 reputation..
Dim teren(120) As PictureBox
Dim x_locatie As Integer = 1, y_locatie As Integer = 0
For i = 0 To 10
x_locatie = 210
For j = 0 To 12
teren(i * j) = New PictureBox()
teren(i * j).Size = New Size(61, 61)
teren(i * j).Name = "x" + i.ToString + "y" + j.ToString
teren(i * j).Location = New Point(x_locatie, y_locatie)
Dim locatie As String = folder + "\harta\test.png"
teren(i * j).ImageLocation = locatie
teren(i * j).Show()
Next
y_locatie += 61
Next
I also tried another method , but same result.
Sub PictureBox1_Paint(sender1 As Object, er As PaintEventArgs)
If myImage IsNot Nothing Then
Dim r As New Rectangle(x, y, xlatime, ylungime)
er.Graphics.DrawImage(myImage, r)
End If
End Sub
Sub deseneaza(ByVal poza As String, ByRef x_perm As Integer, ByRef y_perm As Integer, ByRef lungime As Integer, ByRef latime As Integer)
myImage = Image.FromFile(poza)
x = x_perm
y = y_perm
xlatime = latime
ylungime = lungime
Refresh()
End Sub
'this part of code is in body of another function
Dim x_locatie As Integer = 1, y_locatie As Integer = 0
For i = 0 To 10
x_locatie = 210
For j = 0 To 12
Dim locatie As String = folder + "\harta\test.png"
deseneaza(locatie, x_locatie, y_locatie, 61, 61)
Next
y_locatie += 61
Next
I saw in other threads that their problem solution was something like that Dim teren() As PictureBox {teren1, teren2 , ... , teren n} But the problem in my case is that I need 120 PictureBoxes, and I think that it must be a way to do this without writing 120 pictureboxes.
Please try this...it will generate 16 picture box, size 20x20, in a row. I put it under "FormLoading" event.
Dim Shapes(16) As PictureBox
For i = 1 To 16
Shapes(i) = New PictureBox
With Shapes(i)
.BackColor = SystemColors.Control 'Color.Green
.BackgroundImage = New Bitmap(My.Resources.led_blk)
.BackgroundImageLayout = ImageLayout.Zoom
.Size = New Size(20, 20)
.Visible = True
.Location = New Point( 23 * i, 50)
End With
Me.Controls.Add(Shapes(i))
Next
I think GDI+ will be the way to go. I think you need a custom class that has a rectangle structure as a member with other properties that help you with further logic with the character intersecting with them. Paint should be done in the Paint event of the surface control you are using - PictureBox has the best rendering - IMO.
Public Class Tile
Public Property Bounds As New Rectangle
Public Property IsImpassable As Boolean
'others you think of
End Class
Dim iTop = 325
Dim pBox(48) As PictureBox
Dim pinColor = Color.SkyBlue
Dim leftStart = 50
For j = 0 To 3
For i = 0 To 11
pBox(i) = New PictureBox
'pBox(i).Image = Image.FromFile("\NoTest.bmp")
pBox(i).Visible = True
pBox(i).BackColor = pinColor
pBox(i).Top = iTop + (j * 40)
pBox(i).Width = 20
pBox(i).Height = 20
pBox(i).Left = leftStart + (35 * i)
If i > 9 Then
pBox(i).Left = leftStart + (35 * i) + 15
pBox(i).Width = 25
End If
pBox(i).BringToFront()
pBox(i).SizeMode = PictureBoxSizeMode.StretchImage
Controls.Add(pBox(i))
Next
Next

Error on inserting new line on gridview

I am trying to add a new row into a gridview but for some reason i'm having a problem in the for loop.
Directly goes to dtCurrentTable.Rows.Add(drCurrentRow) and of course, have an error "'row' argument cannot be null.Parameter name: row", because the dtcurrentTable.NewRow was not executed.
Why is this happening?
Private Sub AddNewRowToGrid()
Dim rowIndex As Integer = 0
If Not IsNothing(ViewState("CurrentTable")) Then
Dim dtCurrentTable As DataTable = CType(ViewState("CurrentTable"), DataTable)
Dim drCurrentRow As DataRow = Nothing
If dtCurrentTable.Rows.Count > 0 Then
For i as Integer = 1 To i <= dtCurrentTable.Rows.Count
' Extraem-se os valores das Textbox
Dim box1 As TextBox = Dados.Rows(rowIndex).Cells(0).FindControl("Artigo")
Dim box2 As TextBox = Dados.Rows(rowIndex).Cells(1).FindControl("Descricao")
Dim box3 As TextBox = Dados.Rows(rowIndex).Cells(2).FindControl("IVA")
Dim box4 As TextBox = Dados.Rows(rowIndex).Cells(3).FindControl("PU")
Dim box5 As TextBox = Dados.Rows(rowIndex).Cells(4).FindControl("Desconto")
Dim box6 As TextBox = Dados.Rows(rowIndex).Cells(5).FindControl("UN")
Dim box7 As TextBox = Dados.Rows(rowIndex).Cells(6).FindControl("Quantidade")
Dim box8 As TextBox = Dados.Rows(rowIndex).Cells(7).FindControl("TotalLiquido")
drCurrentRow = dtCurrentTable.NewRow
dtCurrentTable.Rows(i - 1)("Artigo") = box1.Text
dtCurrentTable.Rows(i - 1)("Descricao") = box2.Text
dtCurrentTable.Rows(i - 1)("IVA") = box3.Text
dtCurrentTable.Rows(i - 1)("PU") = box4.Text
dtCurrentTable.Rows(i - 1)("Desconto") = box5.Text
dtCurrentTable.Rows(i - 1)("UN") = box6.Text
dtCurrentTable.Rows(i - 1)("Quantidade") = box7.Text
dtCurrentTable.Rows(i - 1)("TotalLiquido") = box8.Text
rowIndex += 1
Next i
dtCurrentTable.Rows.Add(drCurrentRow)
ViewState("CurrentTable") = dtCurrentTable
Dados.DataSource = dtCurrentTable
Dados.DataBind()
End If
Else
Response.Write("ViewState null")
End If
SetPreviousData()
End Sub
Your For loop is defined wrong, thats why you're getting an error:
For i as Integer = 1 To i <= dtCurrentTable.Rows.Count
evaluates to
For i as Integer = 1 To True
(because i is always <= Rows.Count)
which VB translates as
For i as Integer = 1 To -1
which means your loop never runs.
It
should be
For i as Integer = 1 To dtCurrentTable.Rows.Count
Also, there's something odd about the way you use drCurrentRow = dtCurrentTable.NewRow. Why is that inside the loop when you don't do anything with it in the loop? It gets executed multiple times and then dtCurrentTable.Rows.Add(drCurrentRow) only gets called once.
Its hard for me to correct because I can't figure out what you're trying to do, but that bit looks dodgy to me.