|
-
Dec 17th, 2019, 01:30 PM
#1
Thread Starter
Addicted Member
Proper way to use Validating event
I was working on old VB6 apps, and yes there were validating events which couldn't even allow the user to close the application using the "X" button at the top of the window.
Though VB.NET also has a "validating" event I am still not sure how is to be used properly because I don't want to confuse the user of my application by locking the application at just the currently selected field if the validation fails 
Can you recommend me some good examples or tutorials for using validating event in VB.NET?
-
Dec 17th, 2019, 05:14 PM
#2
Re: Proper way to use Validating event
It's simple stuff. Handle the Validating event and set e.Cancel to True if validation fails to prevent the control losing focus. Before using the data, call ValidateChildren to ensure that even controls that haven't received focus are validated. Provide a Cancel Button and set its CausesValidation property to False so that clicking it will not cause the previous control to be validated. That allows you to close the form while it contains invalid data.
-
Dec 19th, 2019, 02:42 PM
#3
Thread Starter
Addicted Member
Re: Proper way to use Validating event
What do you mean by Cancel button? Show message box with Ok/Cancel buttons?
Also validating event doesn't get called if I set CausesValidation to false. Do I miss something?
Made a sample code but still the form gets stuck if CausesValidation is true
Code:
Private Sub denMTB_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles denMTB.Validating
Dim den As Integer = Convert.ToInt32(denMTB.Text)
If den < 1 Or den > 31 Then
e.Cancel = True
MsgBox("Внеси ден од 1 до 31!", MsgBoxStyle.OkCancel)
Else
ValidateChildren()
SendKeys.Send("{TAB}")
End If
End Sub
By the way, ValidateChildren() throw an exception
Code:
Process is terminated due to StackOverflowException.
EDIT:
I modified some of the code like so:
Code:
Private Sub denMTB_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles denMTB.Validating
Dim den As Integer = Convert.ToInt32(denMTB.Text)
If den < 1 Or den > 31 Then
e.Cancel = True
If MsgBox("Внеси ден од 1 до 31!", MsgBoxStyle.OkOnly) = MsgBoxResult.Ok Then
e.Cancel = False
End If
Else
'ValidateChildren()
SendKeys.Send("{TAB}")
End If
End Sub
Last edited by kutlesh; Dec 19th, 2019 at 02:50 PM.
-
Dec 19th, 2019, 08:01 PM
#4
Re: Proper way to use Validating event
 Originally Posted by kutlesh
I modified some of the code like so:
Code:
Private Sub denMTB_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles denMTB.Validating
Dim den As Integer = Convert.ToInt32(denMTB.Text)
If den < 1 Or den > 31 Then
e.Cancel = True
If MsgBox("Внеси ден од 1 до 31!", MsgBoxStyle.OkOnly) = MsgBoxResult.Ok Then
e.Cancel = False
End If
Else
'ValidateChildren()
SendKeys.Send("{TAB}")
End If
End Sub
Should prob look like this:
VB.Net Code:
Private Sub denMTB_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles denMTB.Validating Dim den As Integer = Convert.ToInt32(denMTB.Text) If den < 1 Or den > 31 Then e.Cancel = True MsgBox("Внеси ден од 1 до 31!", MsgBoxStyle.OkOnly) End If End Sub
The e.cancel = True is pointless... since it will be set to false after... but judging by the message it should be cancel = True
... and SendKeys is just pure evil ... please expalin what you were trying to achieve with this
... also have you considered using a NumericUpDown?
Kris
-
Dec 19th, 2019, 09:08 PM
#5
Re: Proper way to use Validating event
 Originally Posted by kutlesh
What do you mean by Cancel button? Show message box with Ok/Cancel buttons?
Yes, that's what I mean. The OK Button means that the user is happy that all required data is present and all provided data is valid so you can go ahead and use it. The Cancel Button means that the user has decided against entering data so you can discard anything that has been entered and close the form.
 Originally Posted by kutlesh
Also validating event doesn't get called if I set CausesValidation to false. Do I miss something?
Of course it doesn't. That's the whole point. Did you bother reading the documentation for that property or doing any additional research? I'd wager not.
The CausesValidation property is True by default for all WinForms controls. What that means is that, when any of those controls receive focus, the control that previously had focus will raise its Validating and, optionally, Validated events. If you set CausesValidation to False for a control, that means that the previous control will not be validated when that control receives focus. That's why you set that property to False on a Cancel Button specifically. It allows you to click the Button, thus focusing it, without validating the previous control. That means that you can close the form with that Button even if the previous control contains invalid data.
 Originally Posted by kutlesh
By the way, ValidateChildren() throw an exception
Code:
Process is terminated due to StackOverflowException.
Of course it did. The point of ValidateChildren is to raise the Validating and, optionally, Validated events on ALL child controls. If you do that in the Validating event handler then of course you're going to overflow the stack, because you raise the Validating event every time the Validating event is raised. You are supposed to call ValidateChildren when you're ready to use ALL the data, e.g. save it to a database. The point is to ensure that even controls that haven't received focus are validated. That would be primarily to ensure that all required data has been entered. If there's no chance of a control being invalid if it hasn't received focus then there's no need to call ValidateChildren. Again, if you read some documentation and did some research, you'd have a better understanding of how things are supposed to work.
 Originally Posted by kutlesh
Code:
Private Sub denMTB_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles denMTB.Validating
Dim den As Integer = Convert.ToInt32(denMTB.Text)
If den < 1 Or den > 31 Then
e.Cancel = True
If MsgBox("Внеси ден од 1 до 31!", MsgBoxStyle.OkOnly) = MsgBoxResult.Ok Then
e.Cancel = False
End If
Else
'ValidateChildren()
SendKeys.Send("{TAB}")
End If
End Sub
What is that SendKeys call for? It looks like you're trying to shift focus to the next control in the Tab order but the current control is already losing focus if the Validating event has been raised, so another control is already receiving focus. Apart from that, if you actually did want to shift focus to the next control in the Tab order, the way to do that is by calling SelectNextControl.
Here's what a well-implemented Validating event handler would look like:
vb.net Code:
Private Sub numberTextBox_Validating(sender As Object, e As CancelEventArgs) Handles numberTextBox.Validating Dim number As Integer If Not Integer.TryParse(numberTextBox.Text, Nothing) OrElse number < 1 OrElse number > 31 Then 'Highlight all text and show highlight when not focused. numberTextBox.SelectAll() numberTextBox.HideSelection = False MessageBox.Show("Please enter a number in the range 1 to 31.", "invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) numberTextBox.HideSelection = True 'Don't let the control lose focus. e.Cancel = True End If End Sub Private Sub okButton_Click(sender As Object, e As EventArgs) Handles okButton.Click 'Ensure that all controls have been validated. If ValidateChildren() Then 'All data is valid so it can be used safely here and the form closed. '... DialogResult = DialogResult.OK End If End Sub 'The Button is named "cancelOperationButton" so as not to clash with the form's "CancelButton" property. 'The Button's "CausesValidation" property would be set to "False" so it can be clicked without validating the previous control. Private Sub cancelOperationButton_Click(sender As Object, e As EventArgs) Handles cancelOperationButton.Click 'This whole event handler is not required if the Button's "DialogResult" property is set to "Cancel". DialogResult = DialogResult.Cancel End Sub
I'm suspicious about that numeric range though. It looks like you might be trying to select a day of the month. As suggested by i00, a NumericUpDown would like be better for entering numeric data, as it allows you to set a minimum and a maximum and it handles all the validation automatically. That said, mybe you should be using a DateTimePicker if you're working with a date, as it also handles validation and thus will not let you select a day that doesn't exist in a month, e.g. you can't select September 31 and you can't select February 29 except in a leap year.
-
Dec 19th, 2019, 09:09 PM
#6
Re: Proper way to use Validating event
This doesn't seem right either,
Code:
Dim den As Integer = Convert.ToInt32(denMTB.Text)
This will throw an error is someone enters non numeric value by mistake (or on purpose ).
Use Integer.TryParse method,
Code:
Dim var As Integer
If Integer.TryParse(TextBox1.Text, var) Then
MessageBox.Show("It is an Integer")
End If
EDIT - jmc beat me to the punch.
-
Dec 21st, 2019, 12:53 PM
#7
Thread Starter
Addicted Member
Re: Proper way to use Validating event
Yes, I don't use Convert.ToInt32() since it throws exception in my actual application. I have built my own function that returns -1 if the conversion to positive integer fails or the actual number.
I used Convert.ToInt32() because its on my home PC so that I don't think too much on conversion. It is an exercise application. Can change it anytime.
@jmc yes its a day field. My exercise application consists of several fields, day, month, year of birth and the 6 left numbers of the person identification number. Then comes a combo box for choosing sex and then a field for place of living identification number.
EDIT:
The application is for fast data entry. For example we have a table with codes for different places in our country, so the application should search for a given code entered in a given text box and if it exists, jump to the next field in the form. That is why I use SendKeys.Send("{TAB}").
There is a better way?
Last edited by kutlesh; Dec 21st, 2019 at 01:40 PM.
-
Dec 21st, 2019, 01:47 PM
#8
Thread Starter
Addicted Member
Re: Proper way to use Validating event
Coded a form like this:

The day text field has CausesValidation set to true and here is the code behind:
Code:
Public Class Form4
Private Sub MaskedTextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MaskedTextBox1.Validating
Dim number As Integer
If Not Integer.TryParse(MaskedTextBox1.Text, number) OrElse
number < 1 OrElse
number > 31 Then
'Highlight all text and show highlight when not focused.
MaskedTextBox1.SelectAll()
MaskedTextBox1.HideSelection = False
MessageBox.Show("Please enter a number in the range 1 to 31.",
"invalid Data",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation)
MaskedTextBox1.HideSelection = True
'Don't let the control lose focus.
e.Cancel = True
End If
End Sub
Private Sub okButton_Click(sender As Object, e As EventArgs) Handles okButton.Click
'Ensure that all controls have been validated.
If ValidateChildren() Then
'All data is valid so it can be used safely here and the form closed.
'...
DialogResult = DialogResult.OK
End If
End Sub
Private Sub cancelOperationButton_Click(sender As Object, e As EventArgs) Handles cancelOperationButton.Click
'This whole event handler is not required if the Button's "DialogResult" property is set to "Cancel".
DialogResult = DialogResult.Cancel
End Sub
End Class
Cannot close the form. Its stuck. And if I set CausesValidation to false on the day text field, nothing happens, not validation at all... hmm 
ValidateChildren() doesn't makes a call to that MaskedTextBox1_Validating() event...
Last edited by kutlesh; Dec 21st, 2019 at 01:54 PM.
-
Dec 21st, 2019, 02:39 PM
#9
Re: Proper way to use Validating event
Are you talking about the form not closing when you click the okButton or the cancelOperationButton. What is the purpose of the cancelOperationButton? Also is there a valid number in the MTB.
-
Dec 21st, 2019, 03:02 PM
#10
Thread Starter
Addicted Member
Re: Proper way to use Validating event
Yes, pressing cancel or OK won't allow closing the form. Also form "X" button fires validating and doesn't allow me to close the form.
Only when I input value in the required range, I can close the form 
That DialogResult thing doesn't change much 
Should the MessageBox be of type OkCancel and then add some custom logic to those two buttons, Ok and Cancel?
Last edited by kutlesh; Dec 21st, 2019 at 03:14 PM.
-
Dec 21st, 2019, 03:14 PM
#11
Re: Proper way to use Validating event
If you want to exit without a valid entry then change to this,
Code:
If MessageBox.Show("Please enter a number in the range 1 to 31.",
"invalid Data",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Exclamation) <> DialogResult.Cancel Then
'Don't let the control lose focus.
e.Cancel = True
End If
-
Dec 21st, 2019, 03:21 PM
#12
Thread Starter
Addicted Member
Re: Proper way to use Validating event
Yes that does make a difference. And yes I guessed it right, the MessageBox should be of type OKCancel 
Now the bottom left "Cancel" button on the form seems unnecessary. Or is it?
-
Dec 21st, 2019, 03:28 PM
#13
Re: Proper way to use Validating event
Neither button is necessary,
Code:
Private Sub Form6_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If ValidateChildren() Then
DialogResult = DialogResult.Cancel
Else
e.Cancel = True
End If
End Sub
But how you design it is up to you.
-
Dec 21st, 2019, 03:36 PM
#14
Thread Starter
Addicted Member
Re: Proper way to use Validating event
Lol, so tricky to make it work... why it shows the same MessageBox twice, one after the other?
First validate event called when the field looses focus, and the second when called ValidateChildren() right?
Yes that's true...
Not sure why but, ValidateChildren() doesn't call the validate event when CausesValidation is set to false...
Current code:
Code:
Public Class Form4
Private Sub MaskedTextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MaskedTextBox1.Validating
Dim number As Integer
If Not Integer.TryParse(MaskedTextBox1.Text, number) OrElse
number < 1 OrElse
number > 31 Then
'Highlight all text and show highlight when not focused.
MaskedTextBox1.SelectAll()
MaskedTextBox1.HideSelection = False
If MessageBox.Show("Please enter a number in the range 1 to 31.",
"invalid Data",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Exclamation) <> DialogResult.Cancel Then
MaskedTextBox1.HideSelection = True
'Don't let the control lose focus.
e.Cancel = True
End If
End If
End Sub
Private Sub Form4_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
If ValidateChildren() Then
DialogResult = DialogResult.Cancel
Else
e.Cancel = True
End If
End Sub
End Class
Last edited by kutlesh; Dec 21st, 2019 at 03:47 PM.
-
Dec 21st, 2019, 03:48 PM
#15
Re: Proper way to use Validating event
Not sure why but, ValidateChildren() doesn't call the validate event when CausesValidation is set to false...
Why would it validate? You told it not to.
-
Dec 21st, 2019, 03:51 PM
#16
Thread Starter
Addicted Member
Re: Proper way to use Validating event
So how can I set the damn thing not to show the messagebox twice one after the other???
Last edited by kutlesh; Dec 21st, 2019 at 03:55 PM.
-
Dec 21st, 2019, 04:18 PM
#17
Re: Proper way to use Validating event
Code:
Private Sub MaskedTextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MaskedTextBox1.Validating
Dim number As Integer
If Not Integer.TryParse(MaskedTextBox1.Text, number) OrElse
number < 1 OrElse
number > 31 Then
'Highlight all text and show highlight when not focused.
MaskedTextBox1.SelectAll()
MaskedTextBox1.HideSelection = False
If MessageBox.Show("Please enter a number in the range 1 to 31.",
"invalid Data",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Exclamation) <> DialogResult.Cancel Then
'Don't let the control lose focus.
e.Cancel = True
Else
Me.MaskedTextBox1.CausesValidation = False
End If
End If
End Sub
If you want the focus set to a specific control if you press Cancel then just Select that control,
Code:
Private Sub MaskedTextBox1_Validating(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles MaskedTextBox1.Validating
Dim number As Integer
If Not Integer.TryParse(MaskedTextBox1.Text, number) OrElse
number < 1 OrElse
number > 31 Then
'Highlight all text and show highlight when not focused.
MaskedTextBox1.SelectAll()
MaskedTextBox1.HideSelection = False
If MessageBox.Show("Please enter a number in the range 1 to 31.",
"invalid Data",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Exclamation) <> DialogResult.Cancel Then
'Don't let the control lose focus.
e.Cancel = True
Else
Me.MaskedTextBox1.CausesValidation = False
Me.Button1.Select()
End If
End If
End Sub
Last edited by wes4dbt; Dec 21st, 2019 at 04:24 PM.
-
Dec 21st, 2019, 11:27 PM
#18
Re: Proper way to use Validating event
I posted the following in another thread but, because the topics - or my answers, at least - overlap somewhat, I will duplicate it here, for everyone's benefit. A major point to note is that CausesValidation actually works as I said but closing a form that was displayed using Show will still cause the Validating events to be raised. This doesn't happen when using ShowDialog, presumably because the form is not disposed when it is closed. That means that you need to detach event handlers manually in conjunction with using CausesValidation in order to display a form with Show and then close it while it contains invalid data.
I just did some testing and it seems that CausesValidation works as I expected when the form is displayed by calling ShowDialog but not when calling Show. It must have something to do with when the form is disposed. Presumably you are calling Show. Is Show actually the better option or should it actually be ShowDialog? If you do need to use Show, you can still make it work if you attach and detach the Validating event handlers dynamically. Try creating a new WinForms project with two forms and have the first form create and display the second form. Add three TextBoxes and two Buttons to the second form and add this code:
vb.net Code:
Imports System.ComponentModel Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'Allow Button2 to be clicked without performing validation. Button2.CausesValidation = False End Sub Private Sub TextBox1_Validating(sender As Object, e As CancelEventArgs) Handles TextBox1.Validating Dim number As Integer If Not Integer.TryParse(TextBox1.Text, number) OrElse number < 1 OrElse number > 100 Then TextBox1.SelectAll() TextBox1.HideSelection = False MessageBox.Show("TextBox1 must contain a whole number in the range 1 to 100", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox1.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBox2_Validating(sender As Object, e As CancelEventArgs) Handles TextBox2.Validating Dim [date] As Date If Not Date.TryParse(TextBox2.Text, [date]) OrElse [date] < #1/1/2000# OrElse [date].TimeOfDay <> TimeSpan.Zero Then TextBox2.SelectAll() TextBox2.HideSelection = False MessageBox.Show("TextBox2 must contain a round date no earlier than January 1, 2000", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox2.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBox3_Validating(sender As Object, e As CancelEventArgs) Handles TextBox3.Validating If TextBox3.TextLength = 0 OrElse Not TextBox3.Text.All(Function(ch) Char.IsLetter(ch)) Then TextBox3.SelectAll() TextBox3.HideSelection = False MessageBox.Show("TextBox3 must contain text that consists only of letters", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox3.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBoxes_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown, TextBox2.KeyDown, TextBox3.KeyDown Select Case e.KeyData Case Keys.Enter 'Move forward in the Tab order on Enter. SelectNextControl(DirectCast(sender, Control), True, True, True, True) e.SuppressKeyPress = True Case Keys.Shift Or Keys.Enter 'Move backward in the Tab order on Shift+Enter. SelectNextControl(DirectCast(sender, Control), False, True, True, True) e.SuppressKeyPress = True End Select End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click If ValidateChildren() Then 'Close the form when all data is valid. MessageBox.Show("All data is valid. Closing form.") Close() Else MessageBox.Show("Unable to close form with invalid data", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Error) End If End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'Close the form whether data is valid or not. Close() End Sub End Class
If you display that second form using ShowDialog, you'll find that you can close the form by clicking Button2 even if the data is not valid. If you use Show rather than ShowDialog though, clicking Button2 won't close the form. If you change the code to this:
vb.net Code:
Imports System.ComponentModel Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'Allow Button2 to be clicked without performing validation. Button2.CausesValidation = False 'Attach Validating event handlers. AddHandler TextBox1.Validating, AddressOf TextBox1_Validating AddHandler TextBox2.Validating, AddressOf TextBox2_Validating AddHandler TextBox3.Validating, AddressOf TextBox3_Validating End Sub Private Sub TextBox1_Validating(sender As Object, e As CancelEventArgs) Dim number As Integer If Not Integer.TryParse(TextBox1.Text, number) OrElse number < 1 OrElse number > 100 Then TextBox1.SelectAll() TextBox1.HideSelection = False MessageBox.Show("TextBox1 must contain a whole number in the range 1 to 100", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox1.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBox2_Validating(sender As Object, e As CancelEventArgs) Dim [date] As Date If Not Date.TryParse(TextBox2.Text, [date]) OrElse [date] < #1/1/2000# OrElse [date].TimeOfDay <> TimeSpan.Zero Then TextBox2.SelectAll() TextBox2.HideSelection = False MessageBox.Show("TextBox2 must contain a round date no earlier than January 1, 2000", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox2.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBox3_Validating(sender As Object, e As CancelEventArgs) If TextBox3.TextLength = 0 OrElse Not TextBox3.Text.All(Function(ch) Char.IsLetter(ch)) Then TextBox3.SelectAll() TextBox3.HideSelection = False MessageBox.Show("TextBox3 must contain text that consists only of letters", "Invalid Data", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) TextBox3.HideSelection = False e.Cancel = True End If End Sub Private Sub TextBoxes_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown, TextBox2.KeyDown, TextBox3.KeyDown Select Case e.KeyData Case Keys.Enter 'Move forward in the Tab order on Enter. SelectNextControl(DirectCast(sender, Control), True, True, True, True) e.SuppressKeyPress = True Case Keys.Shift Or Keys.Enter 'Move backward in the Tab order on Shift+Enter. SelectNextControl(DirectCast(sender, Control), False, True, True, True) e.SuppressKeyPress = True End Select End Sub Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click If ValidateChildren() Then 'Close the form when all data is valid. MessageBox.Show("All data is valid. Closing form.") Close() Else MessageBox.Show("Unable to close form with invalid data", "Invalid Input", MessageBoxButtons.OK, MessageBoxIcon.Error) End If End Sub Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'Detach Validating event handlers. RemoveHandler TextBox1.Validating, AddressOf TextBox1_Validating RemoveHandler TextBox2.Validating, AddressOf TextBox2_Validating RemoveHandler TextBox3.Validating, AddressOf TextBox3_Validating 'Close the form whether data is valid or not. Close() End Sub End Class
then you'll be able to close the form containing invalid data by clicking Button2, even if the form was displayed by calling Show.
Note that this code also answers the question you asked here about advancing through the controls using the Enter key.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|