how to compare control name to text value - vb.net

I am trying to loop through all controls and assign a text_changed handler on all textboxes aside from two but the following code does not work it keeps getting inside the if and assigning the handler to these textboxes. Using IsNot in replacement of <> does not work either
Dim textboxes = Me.GetAllControls(Me).OfType(Of TextBox)().Where(Function(x) x.Enabled = True).ToList()
For Each item As TextBox In textboxes
If item.Name <> "txtSearchName" AndAlso item.Name <> "txtSearchEmpNum" Then
Dim TextBox As TextBox = DirectCast(item, TextBox)
AddHandler TextBox.TextChanged, AddressOf TextBoxTextChanged_Handler
MsgBox(item.Name & " Has textchanged event handler")
End If
Next
Dim textboxes = Me.GetAllControls(Me).OfType(Of TextBox)().Where(Function(x) x.Enabled = True).ToList()
Private Function GetAllControls(control As Control) As IEnumerable(Of Control)
'This function gets all controls of specified type
Dim controls = control.Controls.Cast(Of Control)()
Return controls.SelectMany(Function(ctrl) GetAllControls(ctrl)).Concat(controls)
End Function

Related

How to check empty controls within tabcontrol pages?

Some of my codes are from the answers I read here in stack-overflow, one difficulty I am facing is how to check empty controls inside Tab-control with 3 pages? I have created a function that validate if the input is empty:
VB.Net Function:
Public Shared Function ValidateInput(parent As Control)
Dim ctl As Control = Nothing
For Each ctl In parent.Controls
If TypeOf ctl Is TextBox And ctl.Text.Length = 0 Or TypeOf ctl Is ComboBox And ctl.Text.Length = 0 Then
Return ctl.Name
Exit For
'--------------------------------------------------------------------
'(1.) I want this statement to check if the control is type of Tabcontrol ang check for empty input. (I have 3 pages)
ElseIf TypeOf ctl Is TabControl Then
For Each tp As TabPage In frmClientInfo.TabControl1.TabPages
For Each ctr As Control In tp.Controls
If TypeOf ctr Is TextBox OrElse TypeOf ctr Is ComboBox Then
ctl.Name = ctr.Name
Return ctl.Name
Exit For
End If
Next
Next
'--------------------------------------------------------------------
Else
ctl = Nothing
End If
Next
Return ctl
End Function
Usage:
Dim x = Functions.ValidateInput(Me)
Dim ctrlinput As Object = Me.Controls.Find(x, True).FirstOrDefault()
If TypeOf ctrlinput Is TextBox Then
ctrlinput.focus()
ElseIf TypeOf ctrlinput Is ComboBox Then
ctrlinput.DroppedDown = True
ElseIf TypeOf ctrlinput Is TabControl Then 'If control is Tab-control
Dim y = Functions.ValidateInput(Me)
Dim xinput As Object = TabControl1.Find(y, True).FirstOrDefault()
If TypeOf xinput Is TextBox Then
xinput.focus()
ElseIf TypeOf xinput Is ComboBox Then
xinput.DroppedDown = True
End If
Else
MessageBox.Show("saveeee")
Call SaveTransaction()
End If
My condition that deals with the controls outside the tab-control works. But I cant get the controls inside the tab-control pages. I want to focus the control when the function detects an empty control inside the tab-control. Let say the empty control was detected on page 3 the system will automatically jump from page 1 to page 3 and focus the empty control. Any solution? Thanks.
A problem here is that a tab can have another container inside it, just like your tabcontainer contains... tabs. You can bypass this by going recursive, but then your function will return everything it finds, wherever it finds it.
Here's a recursive function which will return you a list of every textbox or combobox without text it finds where you call it:
Private Function GetEmptyControls(current As Control) As List(Of Control)
Dim list As New List(Of Control)
For Each ctrl As Control In current.Controls
If TypeOf ctrl Is ContainerControl Then
'if this control is a container, add every control this function might find inside it to your list
'this is what makes this function recursive
list.AddRange(GetEmptyControls(ctrl))
Else
'add this control if it's a textbox or combobox and there's no text
If ctrl.Text = "" AndAlso (TypeOf ctrl Is TextBox OrElse TypeOf ctrl Is ComboBox) Then
list.Add(ctrl)
End If
End If
Next
Return list
End Function
You can call it on your form or on a container inside your form, or adapt it to suit your needs. Have fun!

how i can create selected_index_changed event of combobox in datagridview

my datagridview name is "DG" and i add combobox column programatically named item as shown in code below.i want to create the event which call on itemchanged of combobox.i use DG_CellLeave event but it not call after the item selection immediatly but call when we leave the cell.i want to create event which immediatly call on selection change event of combobox.
Dim item As New DataGridViewComboBoxColumn
item.DataSource = dset.Tables("tab")
item.HeaderText = "item"
item.Name = "item"
item.DisplayMember = "p_name"
item.DataPropertyName = "item"
DG.Columns.Add(item)
which event should i choose for this purpose...
You should take a look at: DataGridView.EditingControlShowing Event. This event is raised whenever an edit control is shown in the DataGridView. It can be handled/used like below:
Dim gridComboBox As ComboBox
Private Sub DG_EditControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs)
' Check to see if the ColumnIndex is where we expect to have the DropDown
If (DG.CurrentCell.ColumnIndex = 1) Then
' Get the ComboBox that is being shown
gridComboBox = TryCast(e.Control, ComboBox)
If Not (gridComboBox Is Nothing) Then
' Always remove the Event Handler before Adding, when setting them at runtime
RemoveHandler gridComboBox.SelectedIndexChanged, AddressOf gridComboBox_SelectedIndexChanged
AddHandler gridComboBox.SelectedIndexChanged, AddressOf gridComboBox_SelectedIndexChanged
End If
End If
End Sub
Private Sub gridComboBox_SelectedIndexChanged(sender As Object, e As EventArgs)
Dim dropDown As ComboBox = TryCast(sender, ComboBox)
if not(dropDown is nothing) then
Dim drv as DataRowView = dropDown.SelectedItem
if Not (drv is nothing) then
Debug.Print(drv("item"))
end if
end if
End Sub
The SelectedIndexChanged event is raised as per a ComboBox used outside of a DataGridView.

Deleting controls each time ShowDialog is called

I have a form that I display exclusively with the ShowDialog method. In the Form_Shown event, I dynamically create a set of labels and text boxes based on a public variable set in the form making the call.
I understand that the form is not closed or destroyed but simply hidden between calls, and as such I added code at the top of my Form_Shown event to clear out any controls from a previous call, but the controls aren't being removed. I have tried ctrl.Dispose (as in the code below) and Me.Controls.Remove(ctrl). Neither produces an error, but the Textboxes are not removed and new ones are created over them. (For some reason,
This is the first time I've dynamically created/removed controls in .NET, so it's possible my yearning for VB6's control arrays have something to do with the error.
The form builds itself based on the calling form's public ListView variable. The calling form makes certain this variable is not nothing and that items are selected if and only if the user is editing an existing row.
Public Class frmTableEdit
Private isNew As Boolean
Private inputText() As TextBox
Private Sub FormTableEdit_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
For Each ctrl As Control In Me.Controls
If TypeOf (ctrl) Is TextBox Or TypeOf (ctrl) Is Label Then
ctrl.Dispose()
End If
Next
With frmKioskData.TBLobj
Dim fieldCount As Integer = .Columns.Count - 1
isNew = .SelectedIndices.Count = 0
'(code setting size of form and location of OK/Cancel buttons is here)
ReDim inputText(fieldCount)
For i As Integer = 0 To fieldCount
Dim lbl As New Label, txt As New TextBox
inputText(i) = txt
Me.Controls.Add(lbl)
Me.Controls.Add(txt)
'(code setting size and and position of lbl & txt is here)
'lbl.Tag = i (I commented these lines out because I never used the
'txt.Tag = i Tag property, but can if a solution calls for it.)
lbl.Text = .Columns(i).Text
If isNew Then
txt.Text = ""
Else
txt.Text = .Items(.SelectedIndices(0)).SubItems(i).Text
End If
Next
End With
End Sub
End Class
If #Plutonix's suggestion in the comment above does not quite work for you, I think it would be easiest and make more sense to dispose of the form after calling ShowDialog, and then when you need to show that form you create a new instance with a parameter that tells it what dynamic controls to load.
So you'd have a New method in frmTableEdit that uses this parameter:
Public Sub New(ByVal fieldCount As Integer)
InitializeComponent()
fieldCount = fieldCount 'Where fieldCount is a class variable
End Sub
And when you call this form from frmKioskData, you can do so like this:
Dim newTableEdit As New frmTableEdit(Me.TBLobj.Columns.Count - 1)
newTableEdit.ShowDialog()
newTableEdit.Dispose()
Then the code in your frmEditTable's Shown event just simply has to add the controls accordingly, without the need to remove old ones:
ReDim inputText(fieldCount)
For i As Integer = 0 To fieldCount
Dim lbl As New Label, txt As New TextBox
inputText(i) = txt
Me.Controls.Add(lbl)
Me.Controls.Add(txt)
'(code setting size and and position of lbl & txt is here)
'lbl.Tag = i (I commented these lines out because I never used the
'txt.Tag = i Tag property, but can if a solution calls for it.)
lbl.Text = .Columns(i).Text
If isNew Then
txt.Text = ""
Else
txt.Text = .Items(.SelectedIndices(0)).SubItems(i).Text
End If
Next
How you get the isNew value is up to you - you can add that as a second parameter to the form's New, or get it the same way you are now...w/e. If it were me I'd normally add that as another New parameter.

Loop controls in form and tabpages

I have form with tabcontrol and few tab pages which contain many settings in textboxes and checkboxes.
When user press Exit from this form I have to check if data was changed.
For that I thought to make a string on Enter of all values on form and compare it with string of all values on exit:
Private Function getsetupstring() As String
Dim setupstring As String = ""
For Each oControl As Control In Me.Controls
If TypeOf oControl Is CheckBox Then
Dim chk As CheckBox = CType(oControl, CheckBox)
setupstring &= chk.Checked.ToString
End If
If TypeOf oControl Is TextBox Then
setupstring &= oControl.Text.Trim.ToString
End If
Next
Return setupstring
End Function
But that code don't loop through controls which are on tab pages, only TabControl and few buttons which are on top of form.
What to do to get all controls listed so I can pick values?
Controls only contains the parent controls, not the respective children. If you want to get all the controls (parents and respective children), you can rely on a code on these lines:
Dim allControls As List(Of Control) = New List(Of Control)
For Each ctr As Control In Me.Controls
allControls = getAllControls(ctr, allControls)
Next
Where getAllControls is defined by:
Private Function getAllControls(mainControl As Control, allControls As List(Of Control)) As List(Of Control)
If (Not allControls.Contains(mainControl)) Then allControls.Add(mainControl)
If mainControl.HasChildren Then
For Each child In mainControl.Controls
If (Not allControls.Contains(DirectCast(child, Control))) Then allControls.Add(DirectCast(child, Control))
If DirectCast(child, Control).HasChildren Then getAllControls(DirectCast(child, Control), allControls)
Next
End If
Return allControls
End Function
Other alternative you have is relying on the Controls.Find method with the searchAllChildren property set to True.

Cross-thread operation not valid

I was doing the following to try out threadings but get the 'Cross-Threading' error in the line txtBox.Text = DataGridView2.Rows.Item(iloop).Cells(3).Value, anyone can point out what's wrong, thanks:
Dim lm As New Thread(AddressOf load_movie)
Dim lt As New Thread(AddressOf load_timings)
lm.Start()
lt.Start()
Private Sub load_movie()
For iloop As Integer = 0 To DataGridView1.Rows.Count - 2
Dim Cstring As String = "txt_Movie_0" & iloop.ToString
For Each cCtrl As Control In Panel1.Controls
If TypeOf cCtrl Is TextBox Then
Dim txtBox As TextBox
txtBox = cCtrl
If txtBox.Name = Cstring Then
txtBox.Text = DataGridView1.Rows.Item(iloop).Cells(1).Value
End If
End If
Next
Next
End Sub
Private Sub load_timings()
For iloop As Integer = 0 To DataGridView2.Rows.Count - 2
For Each cCtrl As Control In Panel2.Controls
If TypeOf cCtrl Is TextBox Then
Dim txtBox As TextBox
txtBox = cCtrl
If (txtBox.Name.Substring(9, 6)) = (DataGridView2.Rows.Item(iloop).Cells(0).Value.substring(0, 6)) Then
txtBox.Text = DataGridView2.Rows.Item(iloop).Cells(3).Value 'This is the part that says "Cross-thread operation not valid: Control 'txt_Time_00_000' accessed from a thread other than the thread it was created on."
End If
End If
Next
Next
End Sub
It's not legal to access a UI element from anything other than UI thread in .Net code. Hence when you try and use the DataGridView2 instance from a background thread it rightfully throws an exception.
In order to read or write to a UI component you need to use Invoke or BeginInvoke method to get back on the UI thread and make the update. For example
If TypeOf cCtrl Is TextBox Then
Dim txtBox As TextBox
txtBox = cCtrl
txtBox.Invoke(AddressOf UpdateTextBox, txtBox, iloop)
End If
Private Sub UpdateTextBox(txtBox as TextBox, iloop as Integer)
If (txtBox.Name.Substring(9, 6)) = (DataGridView2.Rows.Item(iloop).Cells(0).Value.substring(0, 6)) Then
txtBox.Text = DataGridView2.Rows.Item(iloop).Cells(3).Value 'This is the part that says "Cross-thread operation not valid: Control 'txt_Time_00_000' accessed from a thread other than the thread it was created on."
End If
End Sub
#JaredPar you have the basic idea but that code itself won't compile (unless I'm missing something). For VB9 or less you need to declare an actual delegate and invoke that:
''//The delegate is only needed for the VB 9 or less version
Private Delegate Sub UpdateTextBoxDelegate(ByVal txtBox As TextBox, ByVal value As String)
If TypeOf cCtrl Is TextBox Then
Dim txtBox As TextBox
txtBox = cCtrl
''//Perform validation logic here
If (txtBox.Name.Substring(9, 6)) = (DataGridView2.Rows.Item(iloop).Cells(0).Value.ToString().Substring(0, 6)) Then
''//Call the update method with our textbox and value
UpdateTextBox(txtBox, DataGridView2.Rows.Item(iloop).Cells(3).Value.ToString())
End If
End If
Private Sub UpdateTextBox(ByVal txtBox As TextBox, ByVal value As String)
''//Basically ask the textbox if we need to invoke
If txtBox.InvokeRequired Then
''//For VB 9 or less you need a delegate
txtBox.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), txtBox, value)
Else
txtBox.Text = value
End If
End Sub
For VB 10 we can finally use anonymous subs so we can completely get rid of the delegate:
If TypeOf cCtrl Is TextBox Then
Dim txtBox As TextBox
txtBox = cCtrl
''//Perform validation logic here
If (txtBox.Name.Substring(9, 6)) = (DataGridView2.Rows.Item(iloop).Cells(0).Value.ToString().Substring(0, 6)) Then
''//Call the update method with our textbox and value
UpdateTextBox(txtBox, DataGridView2.Rows.Item(iloop).Cells(3).Value.ToString())
End If
End If
Private Sub UpdateTextBox(ByVal txtBox As TextBox, ByVal value As String)
If txtBox.InvokeRequired Then
''//For VB 10 you can use an anonymous sub
txtBox.Invoke(Sub() UpdateTextBox(txtBox, value))
Else
txtBox.Text = value
End If
End Sub