Reference an object using string (the object is under the tab control) - vb.net

I have a working code here that can put test to all textboxes from 1 to 10. The name of the textboxes are Tb_Hour_1, Tb_Hour_2, Tb_Hour_3, Tb_Hour_4, etc. up to 10.
Dim textboxes As TextBox
For i As Integer = 1 To 10
textboxes = Me.Controls("TB_Hour_" & i)
textboxes.Text = "Test" & i
Next
But the problem comes in when I put my text boxes under the tab control, by searching I've found out that by adding in I can specify the place of the object that I want to call. Here's the working code.
For Each c As TextBox In TabPage1.Controls.OfType(Of TextBox)()
If c.Text = String.Empty Then c.Text = "a"
Next
My question is how can I integrate the 2 codes so I can have something like this
Dim textboxes As TextBox In TabPage1.Controls.OfType(Of TextBox)()
For i As Integer = 1 To 10
textboxes = Me.Controls("TB_Hour_" & i)
textboxes.Text = "Test" & i
Next

My VB is a little rusty, but it should be something like this. You can loop through all of the controls on the tab, look for ones that starts with "TB_Hour_", cast it to a textbox and do whatever it is you want to do.
For Each c As Control In TabPage1.Controls
If c.Name.StartsWith("TB_Hour_") Then
' it's a textbox
Dim tb as Textbox = DirectCast(c, Textbox)
' do whatever you're going to do
End If
Next

Now that you tucked the TextBoxes into a TabContol, don't use Me.Controls(). Instead try:
Dim textboxes As TextBox
For i As Integer = 1 To 10
textboxes = TabPage1.Controls("TB_Hour_" & i)
textboxes.Text = "Test" & i
Next
Otherwise, you can do something like:
Dim textboxes As TextBox
For i As Integer = 1 To 10
textboxes = Me.TabControl1.Controls("TabPage1").Controls("TextBox" & i)
textboxes.Text = "Test" & i
Next

Related

Get focus on unbound textbox when form returns no records

I'm a little stumped.
I've got an MS Access front end application for an SQL Server back end. I have an orders form with a list box that, when selected and a "Notes" button is clicked will open another form of notes. This is a continuous form and has a data source (linked table - a view) from the back end database.
When the notes button is clicked in the main orders form, it passes a filter and an OpenArgs string to the Notes form in this code:
Private Sub cmdItemNotes_Click()
Dim i As Integer
Dim ordLine As Boolean
Dim line As Integer
Dim args As String
If Me.lstOrders.ItemsSelected.count = 1 Then
ordLine = False
With Me.lstOrders
For i = 0 To .ListCount - 1
If .selected(i) Then
If .Column(16, i) = "Orders" Then
ordLine = True
line = .Column(0, i)
End If
End If
Next i
End With
If ordLine Then
args = "txtLineID|" & line & "|txtCurrentUser|" & DLookup("[User]", "tblUsers", "[Current] = -1") & "|txtSortNum|" & _
Nz(DMax("[SortNum]", "dbo_vwInvoiceItemNotesAll", "[LineID] = " & line), 0) + 1 & "|"
DoCmd.OpenForm "frmInvoiceItemNotes", , , "LineID = " & line, , , args
Else
'Potting order notes
End If
Else: MsgBox "Please select one item for notes."
End If
Here is my On Load code for the Notes form:
Private Sub Form_Load()
Dim numPipes As Integer
Dim ArgStr As String
Dim ctl As control
Dim ctlNam As String
Dim val As String
Dim i As Integer
ArgStr = Me.OpenArgs
numPipes = Len(ArgStr) - Len(Replace(ArgStr, "|", ""))
For i = 1 To (numPipes / 2)
ctlNam = Left(ArgStr, InStr(ArgStr, "|") - 1)
Set ctl = Me.Controls(ctlNam)
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(ctlNam) + 1))
val = Left(ArgStr, InStr(ArgStr, "|") - 1)
ctl.Value = val
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(val) + 1))
Next i
End Sub
This code executes fine. The form gets filtered to only see the records (notes) for the line selected back in the orders form.
Because this is editing a table in the back end, I use stored procedures in a pass through query to update the table, not bound controls. The bound controls in the continuous form are for displaying current records only. So... I have an unbound textbox (txtNewNote) in the footer of the form to type a new note, edit an existing note, or post a reply to an existing note.
As stated above, the form filters on load. Everything works great when records show. But when it filters to no records, the txtNewNote textbox behaves quite differently. For instance, I have a combo box to mention other users. Here is the code after update for the combo box:
Private Sub cmbMention_AfterUpdate()
Dim ment As String
If Me.txtNewNote = Mid(Me.txtNewNote.DefaultValue, 2, Len(Me.txtNewNote.DefaultValue) - 2) Then
Me.txtNewNote.Value = ""
End If
If Not IsNull(Me.cmbMention) Then
ment = " #" & Me.cmbMention & " "
If Not InStr(Me.txtNewNote, ment) > 0 Then
Me.txtNewNote = Me.txtNewNote & ment
End If
End If
With Me.txtNewNote
.SetFocus
.SelStart = Len(Nz(Me.txtNewNote, ""))
End With
End Sub
The problem occurs with the line
.SelStart = Len(Nz(Me.txtNewNote, ""))
When there are records to display, it works. When there are no records to display, it throws the Run-time error 2185 "You can't reference a property or method for a control unless the control has the focus."
Ironically, if I omit this line and make the .SetFocus the last line of code in the sub, the control is in focus with the entire text highlighted.
Why would an unbound textbox behave this way just because the filter does not show records?
Thanks!

vb.net find textbox name that begins with specified string in a TabControl

I have a TabControl in a form with 4 tabpages. Each tabpage has multiple GroupBox. Each GroupBox has tableLayoutPanel. Each tableLayoutPanel has pragmatically generated array of textbox. if checkbox in a tableLayoutPanel is checked by the user then textboxes in the respective row will be generated. Suppose, the name of one of my textbox array is txtMax(0), txtMax(1).......upto txtMax(42). I need to know how many txtMax(?) (along with their index array) has been generated and become visible. I have tried the the following:
Dim coutGene as integer = 0
Dim coutParameter as integer = 0
Dim indx As Integer
Dim cntl1, cntl2, cntl3 As Control
For Each cnn As TabPage In tabParameters.TabPages
cntl1 = DirectCast(cnn, TabPage)
For Each c2 As Control In cntl1.Controls
If TypeOf (c2) Is GroupBox Then
cntl2 = DirectCast(c2, GroupBox)
For Each c3 As Control In cntl2.Controls
If TypeOf (c3) Is TableLayoutPanel Then
cntl3 = DirectCast(c3, TableLayoutPanel)
For Each c4 As Control In cntl3.Controls
If TypeOf (c4) Is TextBox Then
Dim txt As TextBox = DirectCast(c4, TextBox)
If txt.Name.StartsWith("txtMax") Then
If txt.Visible = True Then
indx = CInt(Between(txt.Name, "(", ")"))
countGene = CInt(countGene + Val(txtGene(indx).Text))
countParameter = countParameter + 1
txtMax(indx).Tag = ""
End If
End If
End If
Next
End If
Next
End If
Next
Next
Function Between(value As String, a As String, b As String) As String
' Get positions for both string arguments.
Dim posA As Integer = value.IndexOf(a)
Dim posB As Integer = value.LastIndexOf(b)
If posA = -1 Then
Return ""
End If
If posB = -1 Then
Return ""
End If
Dim adjustedPosA As Integer = posA + a.Length
If adjustedPosA >= posB Then
Return ""
End If
' Get the substring between the two positions.
Return value.Substring(adjustedPosA, posB - adjustedPosA)
End Function
But the every time code is not getting inside the loop for this condition If txt.Name.StartsWith("txtMax") Then
I am stuck up here. Any assistance will be highly appreciated. Regards. Tariq
You should simplify that code to this:
For Each tp As TabPage In tabParameters.TabPages
For Each gb In tp.Controls.OfType(Of GroupBox)()
For Each tlp In gb.Controls.OfType(Of TableLayoutPanel)()
For Each tb In tlp.Controls.OfType(Of TextBox)().Where(Function(c) c.Visible AndAlso c.Name.StartsWith("txtMax"))
'If you get here, tb is definitely a visible TextBox with a Name starting with "txtMax".
Next
Next
Next
Next
That code will find every visible TextBox with a Name that starts with ""txtMax" inside a TableLayoutPanel, inside a GroupBox, inside a TabPage, inside tabParameters, guaranteed. If that code doesn;t find any such controls, it's because there are no such controls, so that's what you need to investigate.
Thank you for all yours guidelines and suggestion. I tried to implement all,,, but the code could not find the control even though the control exists and visible. I tried this simple code
For k = 1 To 42
If txtMax(k).Visible = True Then
countGene = CInt(countGene + Val(txtGene(k).Text))
countParameter = countParameter + 1
txtMax(k).Tag = ""
End If
Next
But i find that, it search and count the control that exists only in the present tabpage of the TabControl. So i modified the code, though not efficient code, to solve my problem.
I have 4 tabpages in the TabControl. So i tried to select each tabpage by its index and searched 4 times.
For tp As Integer = 0 To 3
tabParameters.SelectedIndex = tp
For k = 1 To 42
If txtMax(k).Visible = True Then
countGene = CInt(countGene + Val(txtGene(k).Text))
countParameter = countParameter + 1
txtMax(k).Tag = ""
End If
Next
Next
Though i solved my problem, but still i seek your advise for efficient code.

VB.Net Loop controls by names using variables

I need to set text of some controls.
I have a Form with some CheckBoxes an some TextBoxes.
In VBA (If I have 5 TextBoxes named "TextBox1", "TextBox2", ... "TextBox5") I can use something like this:
For n = 1 To 5
Me("TextBox" & n).Text = NeededValue
Next n
I know that something like this is also possible in VB.Net but I wasn't able to find the right syntax (and I didn't find similar codes on SO).
I've tryed using
Me.Controls()
But I can't insert control name this way
Me.Controls.Find("TextBox" & n, True)
would be the similar approach to your VBA Style.
Use For Each and then test with TypeOf to find all TextBoxes in your form like :
For Each myObject1 As [Object] In Me.Controls
If TypeOf myObject1 Is TextBox Then
TryCast(myObject1, TextBox).Text = "NeededValue"
End If
Next
Also :
Dim myText = {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5}
For Each btn In myText
btn.Text = "NeededValue"
Next
For i As Int32 = 1 To 5
Dim Txt = Me.Controls.Find("TextBox" & i, True)
If Txt.Length > 0 Then
Txt(0).Text = "blah"
End If
Next
Or :
For i As Int32 = 1 To 5
Dim Txt = Me.Controls.Find("TextBox" & i, True)
If Txt.Length > 0 Then
Txt(0).Text = "NeededValue"
End If
Next

How can I loop through the systems.windows.forms.controls that has the control names in the sequential order?

I have radio buttons named in the sequential order like "Jbtn1' to "Jbtn20". I am trying to name those buttons using set toolTip by using for loop.
Dim toolTip1 As New ToolTip()
For j As Integer = 1 To 20
Dim pinInfo As String = "J2-" & j
'Tried to convert the buttonName as Control , but got an error as
'Value of string cannot be converted to systems.windows.forms.control
Dim buttonName As Control = "Jbtn" & j
toolTip1.SetToolTip(buttonName, pinInfo)
Next
Any suggestions are appreciated.
You can do something like this:
Dim toolTip1 As New ToolTip()
For j As Integer = 1 To 20
Dim pinInfo As String = "J2-" & j
Dim control As Control = Me.Controls.Item("Jbtn" & j)
toolTip1.SetToolTip(control, pinInfo)
Next
This code uses the form's Controls property to access controls on the form. You can lookup individual controls by their names using the indexer on Controls.
I would consider creating and adding the button dynamically rather then relying on the hard-coded name.
That said, you can use the ControlCollection(controlNameString) to find them by the name.
You can also use LINQ to Objects:
Dim radioButtons = From control In Me.Controls.OfType(Of RadioButton)
Where control.Name.StartsWith("Jbtn")
Dim toolTip = New ToolTip()
For Each radioButton In radioButtons
toolTip.SetToolTip(radioButton, radioButton.Name.Replace("Jbtn", "J2-"))
Next

Why can't I get textbox.text values from code generated textboxes

I have a project where I have a form "userform1" which only has a "GO" button and an "EXIT" button on it to begin with. I solicit user input via an inputbox for a number. I use code to then populate the form with that number of labels and textboxes and display the form to allow editting of the created textboxes. Everything works fine up to this point.
I then want the "GO" button to populate an array with the textbox.text values for use elsewhere. This is where I am having a problem.
I tried retrieving the text like I normally do (something like; Xstring = UserForm1.Box1.Text), that didn't work. So then I cycled thru the form controls; UserForm1.Controls.index(), looking for my Textbox .names to confirm they existed. I found them, so I singled one out, index(3), .name = "Box1". See below:
Dim tText As String
tText = UserForm1.Box1.Text
MsgBox tText
returns an ERROR, 'method or data member not found'
But if I change it to this:
Dim x As Object
Dim tText As String
Set x = UserForm1.Controls.Item(3)
tText = x.Text
MsgBox tText
The msgbox returns the .text value
So, QUESTION is, why can't I simply address it normally? I don't want to have to go through all the extra steps to figure out index numbers vs names to populate my array.
for reference, below is partial code of my sub for creating labels/textboxes:
For i = Data(x, 1) To z
Lab = "forms.label.1"
Box = "forms.textbox.1"
Set newlabel = UserForm1.Controls.Add(Lab)
Set newtextbox = UserForm1.Controls.Add(Box)
lbl1 = ("Label" + CStr(i))
With newlabel
.Name = lbl1
.Caption = "No. " + CStr(i)
.Left = Data(x, 3)
.top = top
.Height = 20
.Width = 30
End With
lbl2 = ("Box" + CStr(i))
With newtextbox
.Name = lbl2
.Text = CStr(i)
.Left = (Data(x, 3) + 35)
.top = top
.Height = 20
.Width = 36
End With
top = top + 25
Next i
UserForm1 has no method or data member of that name before run-time. Therefore the VBA interpreter cannot interpret what is meant.
You can nevertheless access the item via through its collection of child controls indexed on either index number or control name, like:
Dim myTxtBox As MSForms.TextBox
Me.Controls.Add bstrProgId:="Forms.Textbox.1", Name:="My Runtime Textbox"
Set myTxtBox = Me.Controls("My Runtime Textbox")
myTxtBox.Text = "Hello"
After reading the comments from Cor_Blimey, this is what I now have working in the code for retrieving my program generated textbox.text data:
Dim i As Integer, num As Integer, ctr As Integer
Dim oCntl As Object
Dim tText() as String
num = UserForm1.Controls.Count
ReDim tText(num)
For Each Control In UserForm1.Controls 'loops thru each control
If TypeOf Control Is TextBox Then
ctr = ctr + 1
tText(ctr) = Control.Text
End If
Next Control
ReDim Preserve tText(ctr)
The .text is being written to the array sequentially (by index#, the order textboxes were created)
In the future, I might consider 2 values in the array, 1 for .name and 1 for .text, just to be sure.