Implementing a Text change event from an Array - vb.net

I am looking to change textbox fore and back colors of multiple textboxes based on a value if any one of the textboxes change it's value either by user input or reading from the DB.
I am not sure how to implement the code once an individual textbox has a change. The below code is as far as I got as I do not know how to implement it to work. Can someone assist?
Private Sub DiffCalcColor_TextChanged(sender As Object, e As EventArgs) Handles tbPMDiffCalc.TextChanged, tbLHDiffCalc.TextChanged, tbRFDiffCalc.TextChanged, tbFSDiffCalc.TextChanged, tbFSADiffCalc.TextChanged
Dim tb = DirectCast(sender, TextBox)
Dim text = tb.Text.Replace("$", "")
Dim number As Decimal
Decimal.TryParse(text, number)
Select Case number
Case < 0D : tb.ForeColor = Color.DarkRed
tb.BackColor = Color.White
Case > 0D : tb.ForeColor = Color.Green
tb.BackColor = Color.White
Case = 0D : tb.ForeColor = Color.DimGray
tb.BackColor = Color.Gainsboro
Case Else
Exit Select
End Select
End Sub

If you want to handle the same event for multiple controls with a single method then you simply include all those controls in the Handles clause, e.g.
Private Sub TextBoxes_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged,
TextBox2.TextChanged
Dim eventRaiser = DirectCast(sender, TextBox)
'Get the text that just changed.
Dim text = eventRaiser.Text
Dim number As Decimal
'Try to convert it to a number.
Decimal.TryParse(text, number)
'Use the number to decide how to format.
If number = Decimal.Zero Then
'...
Else
'...
End If
End Sub
You can do that manually or you can let the designer do it for you. To do the latter, start by selecting the multiple controls in the designer, then open the Properties window, click the Events button on the toolbar, then double-click the appropriate event. That will generate an event handler, much like double-clicking on a single control does, but it will add all the selected controls to the Handles clause. It also allows you to generate a handler for any event, rather than just the default event. To add a control to that Handles clause, you can select one or more controls, select the event and then select an existing event handler from the drop-down.

Related

DataGridView + TextBox How to colorate more and different values

I have a problem, with DataGridView's CellFormatting. The cells are colored by the search result from a TextBox. When I search for 2 numbers together, they are no longer colored. What should I do?
I state that I am using CONCAT_WS to load the table in DataGridView. What can I do?
Private Sub DataGridView1_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs) Handles DataGridView1.CellFormatting
Try
If e.ColumnIndex = 3 And e.Value IsNot Nothing Or e.ColumnIndex = 4 And e.Value IsNot Nothing Or e.ColumnIndex = 5 And e.Value IsNot Nothing Or e.ColumnIndex = 6 And e.Value IsNot Nothing Or e.ColumnIndex = 7 And e.Value IsNot Nothing Then
If String.IsNullOrEmpty(txtRefreshFiltra.Text) Then
txtRefreshFiltra.Text = ""
End If
Dim sum6 As String = Convert.ToInt32(e.Value)
If sum6 = txtRefreshFiltra.Text Then
e.CellStyle.BackColor = Color.Gold
e.CellStyle.ForeColor = Color.Black
End If
End If
Catch ex As Exception
MsgBox(ex.Message) 'show error msg'
End Try
End Sub
My connection
Public Sub FilterData(ValueToSearch As String)
Try
Dim SearchQyery As String = "SELECT * FROM LottoDeaBendata WHERE CONCAT_WS([Estratto1],[Estratto2],[Estratto3],[Estratto4],[Estratto5])LIKE'%" & ValueToSearch & "%'"
Dim command As New SqlCommand(SearchQyery, connection)
connection.Open()
Dim table As New DataTable()
Dim adapter As New SqlDataAdapter(command)
adapter.Fill(table)
DataGridView1.DataSource = table
connection.Close()
Catch ex As Exception
MsgBox(ex.Message) 'show error msg'
End Try
End Sub
Upload by button
Private Sub btnFiltraDati_Click(sender As Object, e As EventArgs) Handles btnFiltraDati.Click
FilterData(txtRefreshFiltra.Text)
End Sub
There are a few things you may want to consider to color the cells as you describe. First, using the grids CellFormatting event may not necessarily be the best choice. This event will fire once for each cell when the data is loaded into the grid and this is fine and colors the cells as we want when the data is loaded, however, it also may fire if the user simply moves the cursor over a cell or the user scrolls the grid.
In both the cases of the user moving the cursor over a cell or scrolling the grid, clearly demonstrates that the cells will get re-colored unnecessarily. In other words, if the text in the text box has not changed or a cells value has not changed, then, re-coloring the cell(s) is superfluous.
Given this, the only drawback to NOT using the grids CellFormatting event is that our code will have to color the cells AFTER the grid is loaded with data. This means we will need a method to loop through all the rows of the grid to check and color the cells. This method to color all the cells is also going to be needed if the text in the text box changes. So, making this method makes sense so we can call it when the data is loaded and also when the text box text changes.
So given all this, to simplify things, I suggest you create a method that takes a single DataGridViewCell. The method will get the comma separated values from the text box and compare the cells value to the values in the text box and if one matches, then we simply color the cell, otherwise do not color the cell.
This method is below. First, we check if the cell is not null and actually has some value. Then, we take the string in the text box and split it on commas. Then we start a loop through all the values in the split string from the text box and if a match is found, then we simply color the cell and exit the for each loop.
Private Sub ColorCell(cell As DataGridViewCell)
If (cell.Value IsNot Nothing) Then
Dim target = cell.Value.ToString()
If (Not String.IsNullOrEmpty(target)) Then
cell.Style.BackColor = Color.White
cell.Style.ForeColor = Color.Black
Dim split = txtRefreshFiltra.Text.Trim().Split(",")
For Each s As String In split
If (target = s.Trim()) Then
cell.Style.BackColor = Color.Gold
cell.Style.ForeColor = Color.Black
Exit For
End If
Next
End If
End If
End Sub
The method above should simplify looping through all the rows in the grid to color the proper cells. This method may look something like below and we would call this method once after the data is loaded into the grid and also when the text in the text box changes.
Private Sub ColorAllCells()
For Each row As DataGridViewRow In DataGridView1.Rows
ColorCell(row.Cells(3))
ColorCell(row.Cells(4))
ColorCell(row.Cells(5))
ColorCell(row.Cells(6))
ColorCell(row.Cells(7))
Next
End Sub
Lastly, the two event methods that we need to capture when the user changes a cells value in the grid in addition to when the user changes the text in the text box.
Private Sub txtRefreshFiltra_TextChanged(sender As Object, e As EventArgs) Handles txtRefreshFiltra.TextChanged
ColorAllCells()
End Sub
Private Sub DataGridView1_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellValueChanged
If (e.RowIndex >= 0) Then
If (e.ColumnIndex = 3 Or e.ColumnIndex = 4 Or e.ColumnIndex = 5 Or e.ColumnIndex = 6 Or e.ColumnIndex = 7) Then
ColorCell(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex))
End If
End If
End Sub
Edit per OP comment…
There are definitely a couple of things we can do to speed up the current code above, like take out the call to ColorAllCells in the text boxes TextChanged event.
The TextChanged event will fire when the user types a single character. Example, if the user wants to color the cells that are “55”, then, when the user types the first “5” into the text box… then the TextChanged event will fire and the code will color all the cells with “5”. Then when the user types the second “5”, the cells will be un-colored/colored again.
So, one way we can prevent the unnecessary coloring as described above is to NOT call the ColorAllCells method in the text boxes TextChanged event and simply put the ColorAllCells method into a button click. In other words, the user types what they want into the text box… THEN clicks a button to color the cells.
In addition, if you look at the ColorCell method, you may note that each time the method is called, the code is splitting the same string over and over with … Dim split = txtRefreshFiltra.Text.Trim().Split(",") … this is potentially redundant in a sense that the text … txtRefreshFiltra.Text may not have changed.
Therefore, to remedy this and only split the txtRefreshFiltra.Text when needed, we will use a global variable called something like currentSplit that holds the current split of the text box. Then we would “update” the currentSplit variable only when needed… like in its TextChanged event.
This should somewhat speed things up. In my small tests, it took approximately 10 seconds to color the cells the FIRST time. Subsequent coloring of the cells when the text box value was changed took less than 1 second.
First make a global variable to hold the current text boxes split values…
Dim currentSplit As String()
Then change the other methods as shown below…
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim dt = GetDT()
DataGridView1.DataSource = dt
currentSplit = txtRefreshFiltra.Text.Trim().Split(",")
End Sub
Private Sub ColorCell(cell As DataGridViewCell)
If (cell.Value IsNot Nothing) Then
Dim target = cell.Value.ToString()
If (Not String.IsNullOrEmpty(target)) Then
cell.Style.BackColor = Color.White
cell.Style.ForeColor = Color.Black
For Each s As String In currentSplit
If (target = s.Trim()) Then
cell.Style.BackColor = Color.Gold
cell.Style.ForeColor = Color.Black
Exit For
End If
Next
End If
End If
End Sub
Private Sub txtRefreshFiltra_TextChanged(sender As Object, e As EventArgs) Handles txtRefreshFiltra.TextChanged
currentSplit = txtRefreshFiltra.Text.Trim().Split(",")
End Sub
And finally, a button click event to color the cells. I added a stop watch to time how long it takes to color the cells.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim sw As Stopwatch = New Stopwatch()
Debug.WriteLine("Starting coloring of cells -------")
sw.Start()
ColorAllCells()
sw.Stop()
Debug.WriteLine("It took: " + sw.Elapsed.TotalSeconds.ToString() + " to color the cells of 100,000 rows with 10 columns")
End Sub

Validating textbox when enter button is pushed

I'm creating a AddIn for Autodesk Inventor (3D drawing software), and for the moment I am playing with positional constraints.
I created a custom user menu for quick editing certain values, in this case the elevation and orientation value.
First I used the textbox.textchanged event to change my constraint value. But this was working not 100%. Example when pressing elevation 1000 would change the elevation 4 times ( on per digit ).
Now I went to using the validated event. This works better, BUT I would like to have the textbox to initiate validation when the Enter button is pressed. For this I whipped up this, but it's not correct i'm sure of it. How should I write this correctly?
The code below works but, I would like to have a proper way to achieve the result.
Private Sub tbElevationValue_TextChanged(sender As Object, e As EventArgs) _
Handles tbElevation.Validated
' If the elevation parameter and textbox value are the same
' The sub must be aborted
If CDbl(tbElevation.Text) = oElevationParameter.Value * 10 Then Exit Sub
' Check the entered value
Dim oValue As Double
If tbElevation.Text = "" Then
oValue = 0
tbElevation.Text = 0
Else
oValue = tbElevation.Text
End If
' Set the parameter value
oElevationParameter.Value = oValue / 10
' Update the document
EM_AddIn.StandardAddInServer.m_inventorApplication.ActiveDocument.Update()
End Sub
Private Sub tbOrientation_TextChanged(sender As Object, e As EventArgs) _
Handles tbOrientation.Validated
' If the orientation parameter and textbox value are the same
' The sub must be aborted
If CDbl(tbOrientation.Text) = cRandiansToDegrees(oOrientationParameter.Value) Then Exit Sub
' Check the entered value
Dim oValue As Double
If tbOrientation.Text = "" Then
oValue = 0
tbOrientation.Text = 0
Else
oValue = tbOrientation.Text
End If
' Set the parameter value
oOrientationParameter.Value = cDegreesToRandians(oValue)
' Update the document
EM_AddIn.StandardAddInServer.m_inventorApplication.ActiveDocument.Update()
End Sub
Private Sub OrientationElevationEnterKey_Pressed(sender As Object, e As Windows.Forms.KeyEventArgs) Handles tbElevation.KeyUp, tbOrientation.KeyUp
If e.KeyCode = Windows.Forms.Keys.Enter Then
CType(sender, Windows.Forms.TextBox).Parent.Focus()
CType(sender, Windows.Forms.TextBox).Focus()
End If
End Sub
I think you're on the right way. It can be a pain to register a key_down event for each TextBox object, but your idea is good.
You can set a key_down event listener to your form instead.
Try something like this:
Private Sub Main_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.KeyCode.Enter Then
MyBase.Focus() ' this will cause the currently focused control to validate
End If
End Sub

VB - TextChanged not triggering properly after the backspace key is hit?

My problem boils down to this:
I have six TextBoxes which expect a value that is between 0 and a given number.
What I am trying to achieve is:
If the number entered is between 0 and the specified number (as a label), the text will remain black
If the number entered exceeds the specified number, the text will turn red
The problem here is that if the specified number is "10", and the user enters 11, it turns red, as it should, HOWEVER, if they hit the backspace key (the number entered is now 1) the number remains red, which is not the intended functionality - the number 1 should be black since it's between 0 and the specified number.
All of the specified numbers are hard-coded for now (I'm in a beginner course and this is just something I'm doing for fun to increase the functionality of the program and I haven't gotten to adding classes for each "Assignment" yet) and you can technically input negative numbers, I don't care about that at the moment.
This is the Subroutine which gets added as the handler for all of the textboxes within a certain GroupBox
' Handler which gets added to all TextBoxes in "grpGrades" GroupBox
Private Sub txtGradePoints_TextChanged(sender As Object, e As EventArgs)
' Take in generic sender (Textbox) and convert to TextBox (necessary due to Strict mode)
Dim textBox = CType(sender, TextBox)
Try
' the value of the current TextBox being checked
Dim val = Decimal.Parse(textBox.Text)
Select Case textBox.Name
Case "txtPostPoints"
If val > 10 Then textBox.ForeColor = Color.Red
Case "txtCh1TestPoints", "txtCh2TestPoints", "txtCh3TestPoints"
If val > 50 Then textBox.ForeColor = Color.Red
Case "txtCh2TutPoints", "txtCh3TutPoints"
If val > 25 Then textBox.ForeColor = Color.Red
Case Else
textBox.ForeColor = Color.Black
End Select
Catch
textBox.ForeColor = SystemColors.ControlText
End Try
End Sub
This is the onLoad Handler which gets the appropriate TextBox controls from the "grpGrades" GroupBox and adds the aforementioned TextChanged handler to each one.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Get array of TextBox Controls from the "grpGrades" GroupBox
Dim textBoxes = grpGrades.Controls.OfType(Of TextBox)()
' Go through the array of TextBoxes and add the TextChanged handler to each TextChanged event
For Each txt In textBoxes
AddHandler txt.TextChanged, AddressOf txtGradePoints_TextChanged
Next
'AddHandler txtPostPoints.TextChanged, AddressOf txtGradePoints_TextChanged
'AddHandler txtCh1TestPoints.TextChanged, AddressOf txtGradePoints_TextChanged
'AddHandler txtCh2TestPoints.TextChanged, AddressOf txtGradePoints_TextChanged
'AddHandler txtCh3TestPoints.TextChanged, AddressOf txtGradePoints_TextChanged
'AddHandler txtCh2TutPoints.TextChanged, AddressOf txtGradePoints_TextChanged
'AddHandler txtCh3TutPoints.TextChanged, AddressOf txtGradePoints_TextChanged
End Sub
The last part of the Subroutine is just the commented out code and is how I originally had the Handlers added, just in case something went wrong with my new method.
EDIT: Was it seriously necessary to downvote? For what reason?
Your code never test for a valid value. The Case Else that sets the current textbox to Black is never hit when the current textbox returns to a valid value. This cannot happen because the Select Case will match the current Name of the textbox, will test again for the invalid value and then exits the Select Case block. You need to set the color for the valid value in the appropriate Case for the current text box name. The IF conditional operator could reduce everything to a single line
Private Sub txtGradePoints_TextChanged(sender As Object, e As EventArgs)
' Take in generic sender (Textbox) and convert to TextBox (necessary due to Strict mode)
Dim textBox = CType(sender, TextBox)
Try
' the value of the current TextBox being checked
Dim val = Decimal.Parse(textBox.Text)
Select Case textBox.Name
Case "txtPostPoints"
textBox.ForeColor = IF(val > 10, Color.Red, Color.Black)
Case "txtCh1TestPoints", "txtCh2TestPoints", "txtCh3TestPoints"
textBox.ForeColor = IF(val > 50, Color.Red, Color.Black)
Case "txtCh2TutPoints", "txtCh3TutPoints"
textBox.ForeColor = IF(val > 25, Color.Red, Color.Black)
Case Else
' Not sure if it is needed for other textboxes....
textBox.ForeColor = Color.Black
End Select
Catch
textBox.ForeColor = SystemColors.ControlText
End Try
End Sub
So, as others have said, you're not handling both sides of the If for each text box. You're setting the colour if it meets the condition, but not reversing it.
Here's my way of handling this. It's a bit different, but it keeps all of your code together and doesn't require any of that Case code to work out the name of the calling text box.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim check As Func(Of TextBox, Decimal, Boolean) =
Function(tb, d)
Dim value As Decimal
If (Decimal.TryParse(tb.Text, value)) Then
Return value > d
End If
Return False
End Function
Dim add As Action(Of TextBox, Decimal) =
Sub(tb, d)
AddHandler tb.TextChanged,
Sub(s, e2)
tb.ForeColor = If(check(tb, d), Color.Red, Color.Black)
End Sub
End Sub
add(txtPostPoints, 10)
add(txtCh1TestPoints, 50)
add(txtCh2TestPoints, 50)
add(txtCh3TestPoints, 50)
add(txtCh2TutPoints, 25)
add(txtCh3TutPoints, 25)
End Sub
So, check is a Func(Of TextBox, Decimal, Boolean) that takes a TextBox and safely parses its text to see if it is greater than the Decimal value and returns True if it is and False otherwise.
And add is an Action(Of TextBox, Decimal) that takes a TextBox and the Decimal and adds the handler to call check set the colour to red or black based on the result of check.
It then is very simple to call add for each TextBox.
All hard-coded with no magic string checks and all nicely encapsulated in the form load method.
You are using the select case based on the name and the only condition is that if it's > 10 then turn the forecolor red. But you're never turning it back. Now before you bring up the case else, let me inform you that the case else would only be based upon the textbox.name and since the name is found case else doesn't come in to play here.
so when a name is found you check the value > 10 and set red but you don't tell it what to do if it isn't > 10

Backcolor change of radiobutton, with button appearance

I'm creating a laboratory application in VS2013, where the user enters a rack number, and gets back a visual grid of which samples he/she has to weigh for a certain analysis, for that specific rack with samples.
I'm getting the information from an Oracle DB, based on the requested/send SQL string.
I've created radio buttons as a means of selecting/filter the analysis type.
I've given them the appearance of regular buttons, in the object properties.
When a certain radio buttons is clicked, I want to give them a specific color, depending on the type of analysis.
The same color will be used to mark the samples that are to be weighed in the visual grid.
When I test the program, the radio buttons do what they need to do (meaning: getting the correct info from Oracle DB), but I can't seem to manage the backcolor change of the radio buttons.
The backcolor change of the regular buttons (on visual grid) is working correctly.
I trigger the function behind all this, by Function Rbanalysistype (sender as object, e as EventArgs) Handles Rbanalysistype1.Click, Rbanalysistype2.Click, ...
Public Function RbAnaTypeClick(sender As Object, e As EventArgs) Handles RbAnaTypeAcIn.CheckedChanged
Dim SenderName As String = ""
Dim TitrType As String = ""
SenderName = CType(sender, RadioButton).Name
Select Case SenderName
Case Is = "RbAnaTypeAcIn"
TitrType = "AcIn"
'put correct Radiobuttion in GbAnaType in color
If RbAnaTypeAcIn.Checked = True Then
RbAnaTypeAcIn.BackColor = Color.Orange
End If
End Select
End Function
You're handling the wrong event for a start. Here's the sort of thing you should be doing:
Private Sub RadioButtons_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged,
RadioButton2.CheckedChanged
Dim rb = DirectCast(sender, RadioButton)
If rb.Checked Then
rb.BackColor = Color.Red
Else
rb.BackColor = Color.Green
End If
End Sub

Data doesn't display when working with multiple forms

I'm new to VB.NET and have been struggling all afternoon with something. I've found similar questions on the forum but none of them seemed to describe my problem exactly. I'm fairly sure that I'm missing something very basic.
I have made a main form which currently holds only one button which purpose is to open up a second form and close the main form. Based on the settings the user will select on the 2nd form the first form might have to be adapted to match with the new settings. But the problem occurs even before that.
The 'settings' form has 15 textboxes which I drew onto the form in development mode. They are called ID1, ID2,..,ID15. The values which I want to display in there are saved in an array:
Dim ids(15) as integer
Next, I created a module to simulate what you could call a control array as I used to use them in VB6.
Public sources() As TextBox = [frmSettings.ID1, frmSettings.ID2, //and so on
I did this to be able to iterate through all the 15 textboxes:
For i = 0 To 14
Sources(i).Text = ids(i + 1)
Next
Then I added on the main form this code to the Button1_Click() event:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
frmSettings.Show()
Me.Close()
End Sub
I did the same thing for the 'exit ' button on the frmSettings form.
This seems to work, but only once. I launch the application, push the button and frmSettings pops up and shows all the values from the array in the textboxes. When I push the 'close' button, I return to the main page.
So far so good, but if I try to return to frmSettings a second time, all the textboxes remain blank as if the code I added to the form never gets executed.
Any help would be greatly appreciated!
First, make sure the array that holds your data is accessible to both forms:
Module Module1
Public ids(15) As Integer
End Module
There should not be a declaration for "ids" in either form.
Next, make frmSettings itself responsible for loading and saving the data:
Public Class frmSettings
Private Sub frmSettings_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim matches() As Control
For i As Integer = 0 To 14
matches = Me.Controls.Find("ID" & (i + 1), True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is TextBox Then
Dim TB As TextBox = DirectCast(matches(0), TextBox)
TB.Text = ids(i)
End If
Next
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim valid As Boolean = True
Dim matches() As Control
For i As Integer = 0 To 14
matches = Me.Controls.Find("ID" & (i + 1), True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is TextBox Then
Dim TB As TextBox = DirectCast(matches(0), TextBox)
Dim value As Integer
If Integer.TryParse(TB.Text, value) Then
ids(i) = value
Else
MessageBox.Show(TB.Name & ": " & TB.Text, "Invalid Value", MessageBoxButtons.OK, MessageBoxIcon.Warning)
valid = False
End If
End If
Next
If valid Then
Me.Close()
End If
End Sub
End Class