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

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

Related

KeyPress handler being applied to wrong control

I have a .KeyPress event handler which is supposed to limit/control the keys which can be entered in a specific TextBox (more precisely, any of the textboxes in a specific DataGridViewTextBoxColumn)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index And Not e.Control Is Nothing Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
Private Sub controlKeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
Dim charEnum As Integer = CUInt(Microsoft.VisualBasic.Asc(e.KeyChar))
Dim tb As TextBox = DirectCast(sender, TextBox)
Select Case charEnum
Case 8
' Always permit the keying of backspace (no suppression)
Case 42
' Permit the keying of asterisk (42) but only if it is the first character (otherwise, suppress the key press)
If Not tb.SelectionStart = 0 Then e.KeyChar = ""
Case 46
' Permit the keying of period (46) but only if it is not the first character and the first character is not an asterisk (otherwise, suppress the key press)
If tb.SelectionStart = 0 OrElse tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case 65 To 90, 97 To 122
' Permit the keying of upper-case alpha (65-90) and lower-case alpha (97-122) as long as the first character is not an asterisk (otherwise, suppress the key press)
If tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case Else
' All other characters, suppress the key press (set the KeyChar to nothing)
e.KeyChar = ""
End Select
End Sub
What's weird is, the same handler seems to be getting applied to other TextBox controls in the DataGridView, but in a different column (i.e. not in myColumn) Which is strange because I have a specific condition in the EditingControlShowing event that specifies that the handler should only be applied if the .ColumnIndex of the control matches that of the column to which it should apply (i.e. If dgv.CurrentCell.ColumnIndex = myColumn.Index) So I'm not sure why the same handler is being applied to a TextBox that's not in myColumn?
Also, it doesn't appear to be consistent - when I initially load the DGV, the other textboxes have no restrictions on them (as expected); when I go to edit a row, and the handler is applied to myColumn (as expected), the same handler also seems to be applied immediately to any other textboxes in the same row (but in debugging, I can't seem to trap where this happens, I can only trap the application of the event handler to the correct control)
I'm not sure if I should have a RemoveHandler call somewhere - and if so, where, because I can't find the point at which the handler is being applied erroneously in the first place?
I tried this but it doesn't seem to have any effect (again, while debugging, when I click in a TextBox in myOtherColumn, it does hit that line, but the restriction is still imposed anyway?)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
ElseIf dgv.CurrentCell.ColumnIndex = myOtherColumn.Index Then
RemoveHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
All suggestions welcome!
The DataGridView control will reuse an editing control if it can to improve performance. You should keep a reference to the editing control from the EditingControlShowing event handler and use a RemoveHandler statement in the CellEndEdit event handler.
Actually, you may not need to keep the reference. You may be able to use the EditingControl property of the grid. Try that first.
EDIT:
I have just tested for myself and the EditingControl property of the grid is Nothing when the CellEndEdit event is raised, so my second suggestion above is out. That means that you need to retain a reference to the editing control from the EditingControlShowing event handler. If you're going to do that though, you may as well not use AddHandler and RemoveHandler. It's simpler to declare the field WithEvents and then use a Handles clause on the event handler, e.g.
Private WithEvents editingControl As TextBox
Private Sub DataGridView1_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
If DataGridView1.CurrentCell.ColumnIndex = 0 Then
editingControl = DirectCast(e.Control, TextBox)
End If
End Sub
Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
editingControl = Nothing
End Sub
Private Sub EditingControl_KeyPress(sender As Object, e As KeyPressEventArgs) Handles editingControl.KeyPress
Console.WriteLine(e.KeyChar)
End Sub
That code will assign the editing control to the field if and only if the cell being edited is in the first column. Any control assigned to that field will have its events handled and the field is always reset when an editing session ends.

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

Implementing a Text change event from an Array

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.

Crash of application while debugging when Textbox is empty

I'd like to create a Textbox, whose text changes color dynamically from black to red when a value larger than a number is typed, and vice versa. I managed to do this but when I erase all the content of the textbox, my debugging application crashes with the error System.InvalidCastException: 'Conversion from string "" to type 'Double' is not valid.' Here is the code I'm using:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
If CDbl(TextBox1.Text) > CDbl(Label2.Text) Then
TextBox1.ForeColor = Color.Red
End If
If CDbl(TextBox1.Text) = CDbl(Label2.Text) Then
TextBox1.ForeColor = Color.Black
End If
If CDbl(TextBox1.Text) < CDbl(Label2.Text) Then
TextBox1.ForeColor = Color.Black
End If
If TextBox1.Text = "" Then
TextBox1.ForeColor = Color.Black
End If
End Sub
Which feature do I have to add in order to prevent the crashing of my application? Thanks in advance. Best regards.
Your code has multiple issues that may require attention.
The VB casting functions like CInt, CDbl etc. will throw an exception if casting fails. An empty value does not represent a numerical value, so it will fail. A solution might be to check for empty string values first.
You also might want to use an If ... ElseIf ... ElseIf ... ElseIf ... EndIf construct to avoid execution of subsequent if-blocks once you executed a matching if-block.
But then the code would crash if you would type a non-numeric value, like "A"...
So I would propose to use the Double.TryParse method instead of CDbl here.
Also notice that you normally want to use black as the default foreground color, but only want to switch to red if the value of the textbox is larger than the value of the label. Why not just express that literally in code?
So here are my two cents, using a variable NewForeColor that is initially set to Color.Black. It is then only set to Color.Red if both the textbox and the label hold a numerical value and the textbox value is larger than the label value. Eventually the textbox's foreground color is set to the value of the NewForeColor variable:
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
Dim NewForeColor As Color = Color.Black
Dim TextBox1Value As Double
Dim Label2Value As Double
If Double.TryParse(TextBox1.Text, TextBox1Value) AndAlso Double.TryParse(Label2.Text, Label2Value) Then
If TextBox1Value > Label2Value Then
NewForeColor = Color.Red
End If
End If
TextBox1.ForeColor = NewForeColor
End Sub

VB.NET 2010 DataGridView Handling Keypress via EditingControlShowing Event

I am working with a DataGridView for the first time and while I have MANY questions, this latest issue is vexing me.
Summary of issue:
I have a DataGridView (dgv) which I have a set of columns defined. Some readonly some editable.
For the editable columns I need four things to occur.
1) Allow Numeric entry
2) Allow maximum of 2 digits
3) Zero Pad any entries <2 digits
4) My ISSUE:
If the user types in a two digit number, I want to detect that and TAB to the next column. I cannot get this to work.
Sample code (with some known working items left out):
Private Sub dgvDiary_EditingControlShowing(sender As Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvDiary.EditingControlShowing
Dim txtEdit As TextBox = e.Control
txtEdit.MaxLength = 2
'remove any existing handler
RemoveHandler txtEdit.KeyPress, AddressOf txtdgvDiaryEdit_Keypress
AddHandler txtEdit.KeyPress, AddressOf txtdgvDiaryEdit_Keypress
End Sub
Private Sub txtdgvDiaryEdit_Keypress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
'Test for numeric value or backspace
If IsNumeric(e.KeyChar.ToString()) _
Or e.KeyChar = ChrW(Keys.Back) Then
e.Handled = False 'if numeric
Else
e.Handled = True 'if non numeric
End If
'If user typed in 2 characters, move on!
'Don't work!
If Strings.Len(Me.dgvDiary.Rows(Me.dgvDiary.CurrentRow.Index).Cells(Me.dgvDiary.CurrentCell.ColumnIndex).Value) = 2 Then
SendKeys.Send("{TAB}")
End If
End Sub
Basically during this event I'm not able to see what the value of the cell "will be" when entered.
I tried adding a ".RefreshEdit" and a ".Commit" but they didn't work.
Any way to test the code within this event OR is there an event that would fire IMMEDIATELY afterward that I can use?
You are looking in the wrong place. You need to examine the text in the TextBox, not the grid, to see how many characters are currently being typed. Try using the TextChanged event for that:
Private Sub txtdgvDiaryEdit_TextChanged(sender As Object, e As EventArgs)
If DirectCast(sender, TextBox).Text.Length = 2 Then
SendKeys.Send("{TAB}")
End If
End Sub
Like your other code, add the handlers:
'remove any existing handler
RemoveHandler txtEdit.TextChanged, AddressOf txtdgvDiaryEdit_TextChanged
AddHandler txtEdit.TextChanged, AddressOf txtdgvDiaryEdit_TextChanged
RemoveHandler txtEdit.KeyPress, AddressOf txtdgvDiaryEdit_KeyPress
AddHandler txtEdit.KeyPress, AddressOf txtdgvDiaryEdit_KeyPress
Alternatively, you can check to see if the TextBox only has one character, and if the KeyPress is passing in another number, send your Tab key then. You would remove the TextChanged event code in this case:
Private Sub txtdgvDiaryEdit_KeyPress(sender As Object, e As KeyPressEventArgs)
'Test for numeric value or backspace
If IsNumeric(e.KeyChar.ToString()) _
Or e.KeyChar = ChrW(Keys.Back) Then
e.Handled = False 'if numeric
Else
e.Handled = True 'if non numeric
End If
If DirectCast(sender, TextBox).Text.Length = 1 AndAlso Char.IsNumber(e.KeyChar) Then
SendKeys.Send("{TAB}")
End If
End Sub