Creating a control array in VB.NET - vb.net

How can I make a control array? Or the equivalent.
I am used to Visual Basic 6 which presents the option of whether to create a control array when I copy and paste and control.

Control arrays are long gone. Object-oriented methods are now the law of the land for VB.
You need to use a collection of some sort, such as a list.

Another implicit feature of the control arrays that is easily overlooked is the association of numeric indexes with each element of the array. The indexes can be any positive int as long as they areunique in the collection; they do not have to be sequential. Consequently the array is more like a dictionary indexed by integers with each item value being an instance of a specific type of control.
Logic in the VB6 event handlers for the control array get the value of the index along with the attributes of the event to be handled. Logic in the handler typically uses the index to determine which instance of the control was raising the event.
.NET event handlers are quite different. You will typically get a reference to a specific control instance and an instance of a specific event object with the attributes of the event to be handled. You will NOT get the index.
Also, VB6 apps sometimes have logic that iterates/manipulates the control array as an array.
In our default translations, we try to support legacy VB6 logic that depends explicitly on the control array and its indexes. We rewrite control arrays as a group of individual control instances and then we add them to a generic OrderDictionary<int, controlType> during form initialization. The individual controls subscribe to the events and we can use the collection to lookup the index given a control instance or to iterate/manipulate the items in the "array". If you do not explicitly need the index or the array, you can get rid of the collection.
Dynamically adding controls is more work now -- it was conceptually like adding an item to the control array and supported with a single statement in VB6 (Load control). As far as I know, in .NET you need to clone a control, deep copy the properties explicitly, and hook up the event handlers explicitly. It can be generalized using reflection and other moderately advanced techniques -- but it sure is not simply calling "load control". If there is an easier way to do this in .NET I would love to hear about it. FWIW: we translate to a helper function.

A control array in VB6 solely existed to allow a single event handler for multiple controls of same type.
You do this in VB.NET by putting the controls into the Handles clause:
private sub Button_Click(...) Handles Command1.Click, Command2.Click, Command3.Click
end sub

You can act only through code. For example:
Dim c() As TextBox
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim j As Integer
For j = 0 To 10
ReDim Preserve c(j)
c(j) = New TextBox
c(j).Name = "txt" & j
c(j).Parent = Me
c(j).Top = j * c(j).PreferredHeight + 2
c(j).Tag = j
c(j).Visible = True
AddHandler c(j).KeyPress, AddressOf TestKeyPress
Next
End Sub
Public Sub TestKeyPress(source As Object, e As KeyPressEventArgs)
Dim index As Integer
index = CInt(source.tag)
If index >= 5 Then
If e.KeyChar > "9" Or e.KeyChar < "0" Then
e.Handled = True
End If
Else
If e.KeyChar <= "9" And e.KeyChar >= "0" Then
e.Handled = True
End If
End If
End Sub
This will create eleven text boxes assigning to all the same event handler.
The TAG property is used to store and retrieve the idex of the text box.

there are three ways to create control array.
assign the same name to more then one control.
copy an evangelizing control and past it into the form.
set the index properly to a value that is not null.

Related

GetType.GetProperties for PictureBox

I am trying to run the code,in windows form using vb.net where I have multiple pictureboxes with properties like backgroudimage and location.
So I want to eliminate these repeated initilisations,and hence I am trying to initialise all the controls using GetType.Getproperties command. But I am getting an exception error
Private Sub Read_csv_file_itextbox_Load_1(sender As Object, e As EventArgs) Handles MyBase.Load
For Each c As Control In Me.Controls
If c.GetType().Name = "TextBox" Then
c.GetType().GetProperty("BackColor").SetValue(c, "Transparent", Nothing)
End If
Next
End Sub
And I also need to explicitly set properties for some textboxes That have naming TextBox1,TextBox2, Textbox3 so on.
And I want to access these Textbox in their even number indexing and perform my code.
The proper way to do what you're trying to do in the code you posted would be this:
For Each tb In Controls.OfType(Of TextBox)()
tb.BackColor = Color.Transparent
Next
Don't use Reflection without good reason.
Note that setting the BackColor of a TextBox to Transparent doesn't necessarily make sense, but you can use this same principle on any type of control and any property and any value.
If you want to filter any list using LINQ then you call the Where method. It's up to you to determine what the condition(s) is that you want to filter by and write the appropriate Boolean expression, e.g.
For Each tb In Controls.OfType(Of TextBox)().
Where(Function(tb) Enumerable.Range(1, 10).
Where(Function(n) n Mod 2 = 0).
Select(Function(n) $"TextBox{n}").
Contains(tb.Name))
tb.BackColor = Color.Transparent
Next
That will first generate a list of TextBoxes and then filter them on Name. It will include only those with a Name of TextBoxN where N is an even number in the range 1 to 10. This is a perfect example of determining the logic first, i.e. working out what the code needs to do, and then writing code to do that specifically.

How to update a group of combo boxes using Loops

I have a form with combo boxes (cmbPort#) to select up to 8 serial ports. I want to first clear the item list for each, populate them with currently available system ports, and then add the option "Off" to each list. Finally, to set each combo box according to defaults saved in string spName(). I created a GroupBox (gBox1) and dragged each cmbPort onto it but I'm not sure how to reference the controls on it. I'm using VB 2015.
Can you help with VB.NET code to use loops ("For Each" or similar) to do this more efficiently?
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
cmbPort1.Items.Clear()
...
cmbPort8.Items.Clear()
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort1.Items.Add(sp)
...
cmbPort8.Items.Add(sp)
Next
cmbPort1.Items.Add("Off")
...
cmbPort8.Items.Add("Off")
cmbPort1.Text = spName(1)
...
cmbPort8.Text = spName(8)
End Sub
Loops are an incredibly useful tool to master. I pitched here some code so you can get the idea, but I was working out of IDE so you might have to apply some fixes to this code to make it work as you want it to.
The main idea is that you shouldn't have to write a line more than once. If you multiply the same operation on several lines of code, you create potential problems for the future. Loops and subs are really helpful to prevent this kind of issues.
Private Sub frmProp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'This is for later
Dim cmbIndex As Integer = 1
'You basically do the same operations for every Combobox in your list, son only one loop needed
For Each cmbPort As ComboBox In {cmbPort1, cmbPort2, cmbPort3 [...] cmbPort8} 'this is a way to declare an array
'Clear
cmbPort.Items.Clear()
'Add SerialPortNames
For Each sp As String In My.Computer.Ports.SerialPortNames
cmbPort.Items.Add(sp)
Next
'Add "Off"
cmbPort.Items.Add("Off")
'This last one is a little bit trickier because you want to match numbers
'This is the place where you get errors when something doesn't go as planned
'If you need to keep it inside the loop here's a way to achieve that, but honestly I would't do that
'Instead I would suggest that you take this part out of the loop and do it manually
If spName(cmbIndex) IsNot Nothing Then cmbPort.Text = spName(cmbIndex)
cmbIndex += 1
Next
End Sub
You shouldn't consider efficiency into this equation, as this operation will not be called all the time, only on load. I mean: you should always do things in the best way you can, but optimization is sometimes the enemy of good, readable code.

How to set a listview FocusedItem programatically

How can I set the FocusedItem property programatically?
I've tried this so far with no success:
If lvw.FocusedItem Is Nothing Then
If lvw.Items.Count > 0 Then
lvw.Focus()
lvw.HideSelection = False
lvw.Items(0).Selected = True
lvw.Items(0).Focused = True
lvw.FocusedItem = lvw.Items(0)
lvw.Select()
End If
End If
btw the form where the listview is has not called the ShowDialog method yet.
Can this be the reason for this not to work?
You have to understand that each control on the Form and the Form itself is a window and for the window to have focus it must first be created and assigned a handle.
For a basic description of this, I refer you to: All About Handles in Windows Forms The following excerpts are from the referenced article.
What is a Handle?
A handle (HWND) is the return value from CreateWindowEx which the Windows Operating System uses to identify a window. A "window" in win32 is a much broader concept than you may think - each individual button, combobox, listbox etc comprises a window. (For more information see About Window Classes ) NOTE: there are other things known as "Handles" in the Framework - e.g. GDI Handles from a Bitmap or Handles to Device Contexts (HDCs) - this article discusses HWNDs only.
...
When does a Control create its handle? (When does a control call CreateWindowEx?)
A control tries as much as possible to defer creating its handle. This is because setting properties forces chatty interop between the CLR and user32.
Typically the handles for all the controls are created before the
Form.Load event is called. Handles can also be created if the "Handle"
property is called and the handle has not yet been created, or
CreateControl() is called.
So a window's handle is not immediately created when you instantiate a control. However, you can force a control to create its handle by referencing its Handle property.
So if you first get the ListView to create its handle, then when you set those properties that you wanted.
Dim f2 As New Form2
' you do not need this condition, it is here only for demonstration purposes
' so that you can step through the code in the debugger and observe the
' code execution.
If Not f2.ListView1.IsHandleCreated Then
' retrieval of the Handle will cause a handle to be created
' if it has not yet been created
' if you delete the If-Then block, you will need to retain the
' following statement
Dim h As IntPtr = f2.ListView1.Handle
End If
f2.ListView1.FocusedItem = f2.ListView1.Items(2)
f2.ListView1.Items(2).Selected = True
f2.ListView1.Items(2).Focused = True
f2.ActiveControl = f2.ListView1
f2.ShowDialog()
As others have commented, your code should work as written. If all you need is to programmatically access the focused item in your code, you shouldn't be experiencing any difficulties. (If you are, please describe them.)
If you are looking for a visual effect (the row being highlighted), my guess is that your code is in another control's event and the focus is being set back to that control automatically the instant after your code runs. More than likely your code needs to be where it is and trying to move it elsewhere to prevent this issue would be a waste of time.
However, there are other ways to set a row apart visually. When a list view isn't likely to stay focused, my preferred method is to distinguish the selected item with a different fore/back color. (You can use the focused item if you prefer, but I find the selected item more useful. Your call.)
Here is an example which visually highlights the selected row, regardless of focus:
Private Sub lvw_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lvw.SelectedIndexChanged
If lvw.Items Is Nothing Then Exit Sub
For Each lvi As ListViewItem In lvw.Items
If lvi.Selected = True Then
lvi.ForeColor = Color.DarkGray
lvi.BackColor = Color.LightCyan
Else
lvi.ForeColor = Color.Black
lvi.BackColor = Color.White
End If
Next
End Sub
EDIT:
In response to the added information that this form is being displayed using ShowDialog, yes, that is likely the source of your problem.
ShowDialog creates a new instance of the form. Therefore, if you have set any properties of a form or its controls, and later call ShowDialog to display that form, the form displayed is a new copy of the original form and will not reflect the changes you made programatically.
Imagine you sit down at a computer where a blank Word document is already open. You type something in it and then open a new document. The text you typed in the first document is not copied to the second. I think this is the root of your troubles here.

vb2010 Express - Operator '=' is not defined

I'm building the retro arcade game 'Asteroids' and have been having some trouble with the collision detection of a 'shot' hitting an asteroid and splitting it into pieces.
Using the following code, I get an error in the If statement at the start of the second subroutine:
Operator '=' is not defined for types 'System.Windows.Forms.PictureBox' and 'System.Windows.Forms.PictureBox'.
Private Sub CollisionDetection_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CollisionDetection.Tick
Dim Asteroids() = {picLrgAsteroid100, picLrgAsteroid200, picLrgAsteroid300, picLrgAsteroid400, picLrgAsteroid500}
'Stores all asteroids, used for testing collision for all asteroids
For Each PictureBox In Asteroids
'For all asteroids
If PictureBox.Bounds.IntersectsWith(picBullet1.Bounds) Then
Me.Controls.Remove(picBullet1)
AsteroidDestroyer(PictureBox)
ElseIf PictureBox.Bounds.IntersectsWith(picBullet2.Bounds) Then
Me.Controls.Remove(picBullet2)
ElseIf PictureBox.Bounds.IntersectsWith(picBullet3.Bounds) Then
Me.Controls.Remove(picBullet3)
End If
'If it intersects with a bullet
'Remove the bullet, and transmit to break apart the asteroid
Next
End Sub
Public Sub AsteroidDestroyer(ByVal Asteroid As System.Windows.Forms.PictureBox)
If Asteroid = picLrgAsteroid100 Then
*
*
*
End if
As I'm using a for...each statement, how can I transmit which PictureBox is currently being run through "For each PictureBox in Asteroids", to the subroutine 'AsteroidDestroyer' and have it receive, then use it in the if statement?
Example (Pseudo-code):
sub CollisionDetection
If PictureBox (picLrgAsteroid100) intersects with picBullet1 then
Remove picBullet1
Transmit to AsteroidDestroyer(picLrgAsteroid100)
End if
End Sub
Sub AsteroidDestroyer(ByVal Asteroid as PictureBox)
If Asteroid = picLrgAsteroid100 then
End If
End Sub
And if you can see any way to improve, to avoid this problem, there's no reason I have it as I currently do, so feel free to suggest!
In lay terms, you are trying to compare two variables which contain references, and in this case, to the same object (or potentially the same object). Whereas the number one can "equal" the number one, or a variable containing the number one can "be equal to" another variable containing the number one, in contrast an object can not equal itself. Equality is a characteristic of the properties, or values of an object.
In the case of comparing references to an object vb.net provides the Is Operator. Where you might be accustomed to comparing values or properties with the equal sign,
If var1 = var2 Then
to perform a comparison of object references you would instead use ‘Is‘.
If objRef1 Is objRef2 Then
Both = and Is return a Boolean indicating a success or failure (equality/true, or inequality/false), but where the equal sign compares a value, Is compares a reference without regard to the reference's properties.
In your specific case, this translates to,
If Asteroid Is picLrgasteroid100 Then

How to read input from a barcode scanner in vb.net without using a textbox?

My program is already working fine, I use a TextBox to capture the barcode scanner input. The purpose of my program is for time and attendance monitoring, the problem is I want to prevent users from using the keyboard to type in their ID's as it would render the barcode scanner and their ID's with barcodes useless.
*I already tried removing the keyboard from the computer and it did work, but the keyboard must not be removed as a requirement...
Option 1:
Get a barcode-scanner that is connected to a serial-port (raw serial device read by a COM port). As most barcode-scanners emulate keyboard strokes there is no way to directly distinguish a barcode scanner input from a keyboard input (see next option) without going low-level (see last update).
One connected to a serial port (or emulated one via USB as serial-ports are not so common anymore) gives you full control on where the input comes from.
Option 2:
Count number of chars typed by time. Barcode-scanners inject a sequence (line) pretty fast compared to typing. Measuring the time used in the textbox by counting key-presses (use CR+LF as a measure point as these are sent by the scanner as well) can give you one method to distinguish if a human is typing (unless there is one typing fast as f) or the content was injected. If timed-out just reject/clear the input.
In addition the checksum of the barcode (if you use one that contains that) can be used to do an extra validation in addition to time measurement.
(you can detect pasting by overriding the ctrl + v as in the next option).
Option 3:
Combine option 2 but instead of measure in the textbox tap into the ProcessCmdKey() function (by overriding it) and measure there if textbox has focus. This way you can first buffer input, measure time and if within a set time-out value, inject the line into the textbox.
Update:
Option 4: a non-technical approach -
Usability improvements: make it visually very clear that bar-codes must be entered with a scanner and not typed. I am including as an option as it is simple and if made correct also effective (there's no right answer of what is correct unfortunately).
Approached could include f.ex. a watermark in the textbox ("Don't type, scan!" or something in that order). Give it a different color, border, size etc. to distinguish it from normal textboxes, and have a help text associated and available at all time that improves clarity.
I had the same issue and I did the following:
I set an int variable digitsPrevTyped = 0
In the "TextChanged" event of my textbox I added this (the textbox has a maxsize of 17 chars):
Private Sub tbxScannedText_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles tbxScannedText.TextChanged
If tbxScannedText.Text.Length >= 17 Then
SearchFunction(False)
Else
digitsPrevTyped = tbxScannedText.Text.Length
End If
End Sub
Then in my "SearchFunction" I check the following:
Dim inputMethod As Char
If tbxScannedText.TextLength = 17 And digitsPrevTyped = 0 Then
inputMethod = TEXT_SCANNED
Else
inputMethod = TEXT_MANUALLY_ENTERED
End If
If the textbox initially had a length of 0 chars and now has a length of 17 chars it means that the text was scanned. If the length of the previously typed text is less than 17 chars, then the text was typed.
It is very basic but it works for me.
The other possible workaround is to handle keypress event to restrict user input. Do not allow direct input from keyboard and leave the readonly false.
Set following in KeyPress event handler
Private Sub Textbox1_KeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Textbox1.KeyPress
e.Handled = True
End Sub
Just disable the keyboard anyway.. when using barcode you can disable the keyboard without using readonly on the textbox..
on keypress event put some code i.e
if e.keychar <> chrw(0) then
e.keychar = chrw(0)
end if
that condition will automatically be trigged when user type anything.. you will forcibly disable any input from user but not from barcode
why not use an "alias" in the bar code like "123##$!" (but make it stupid long) is "JSMITH" and set the font color to the same as the background color in the textbox. The user can't see what they're typing or what the bar code value is when it's scanned.
Super simplistic approach that doesn't really require anything added aside from another field in the the user table.
This is an old post, but it took me some time to figure out a relatively clean way to use a barcode scanner and combobox so this is for future users.
Barcode scanners can often be configured to append carriage return and line feed to the end of the scan. I have a form that can take user input or barcode scanner input into a bound combobox using the _PreviewKeyDown property and trapping on the value "Keys.Enter".
Example:
If ((e.KeyCode = Keys.Enter) Then
'do stuff
Else
'do other stuff
End if
Verifying the data exists in the datasource is a bit trickier because the SelectedValue property of the combobox doesn't update so that event doesn't fire. I used a custom method to verify that the value scanned exists in the datasource. This method uses the .Text property of the combo box. It uses:
Me.combobox.findexactstring(Me.combobox.Text)
If e.KeyCode = Keys.Enter And txt.Text.Length > 0 Then
'To Do
Else
'To Do
End if
All of my scanner input goes into a "hidden" textbox, which then fills the visible ones as needed depending on the input. This, of course, means you need to keep track of where the focus is. Any type of control that can get focus will then make a call in those events to return focus to whatever the "active" textbox is at that time, which is normally the hidden one. For example...
Private Sub buttons_gotFocus(sender As System.Object, e As System.EventArgs) Handles btnPrint.GotFocus, btnInMVPageDown.GotFocus, btnAdv.GotFocus, btnManual.GotFocus, btnResend.GotFocus, dgvInbound.GotFocus, dgvOutbound.GotFocus, TCRole.GotFocus
Try
activeTextbox.Focus()
Catch ex As Exception
'ignore any errors
End Try
End Sub
Most other textboxes are disabled by default, and only enabled under certain conditions. Once that entry is done they are disabled and the hidden one will get focus again. Works like a charm.
There's no need to record previous typed characters.
Here's my solution:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
If TextBox1.Text.Length >= 17 Then '17 or the number of characters your scanner gets.
MsgBox("scanned")
TextBox1.Clear()
Else
If TextBox1.Text.Length <> 0 Then TextBox1.Clear()
End If
End Sub
This answer will handle any fast typing.
Dim scanner_input As Boolean = False
Dim start_typing As DateTime
Private Sub TextBox_part_number_TextChanged(sender As Object, e As EventArgs) Handles
TextBox_part_number.TextChanged
If (TextBox_part_number.Text.Length = 1) Then
start_typing = DateTime.Now
scanner_input = False
'' MsgBox(start_typing.ToString)
ElseIf (TextBox_part_number.Text.Length > 7) Then
If (calc_typing_time(start_typing) < 500) Then
scanner_input = True
Else
scanner_input = False
End If
End If
End Sub
Function calc_typing_time(time_started As DateTime)
Dim time_finished As DateTime
time_finished = DateTime.Now
Dim duration As TimeSpan = time_finished - time_started
Dim time_diff As String = duration.TotalMilliseconds
Return time_diff
End Function
Most of the scanners has a driver to communicate with (Opos) it has functions to open the scanner port and listen to the scanning , so you take the result and decode it in the background and then display the result in the Textbox... what you need to do it to check your barcode scanner's brand go to it's website and download the driver and its manual.
You should just mark your textbox as readonly.