Is there a best practice way to validate user input?
Actual Problem:
A user gives certain inputs in a window. When he is done with those inputs, he can click 'create'. Now, a pop up message should be shown with all invalid input given. If no invalid input, then just continue.
I could easily do this in the Form class. But I remember some best practice way of validating the input in the set properties. Problem is that I already created an instance of that class (or otherwise, can't set properties ;) ) if I validate this way. That should not happen, no instance of the class may be created unless input is valid.
I was planning to create a ErrorMessages class that contains a list where I can put all errorMessages. Every time an invalid input is given, a new message is added to the errorMessages list. So if user click's 'create' button, all messages in the list are shown. Is this a good way of handling things?
So is there a best practice way? Any design patterns that provide such solution?
Edit: This is a school task. So with illogical requirements. I HAVE to show all invalid inputs when I click 'create'. I would like to do this out of Form class. (So validation works even without GUI, I did't even create the GUI yet at this point). First making sure my functionality works correctly ;). I want to keep my code clean, abstract and OOP. So how should I show my error messages?
This is a simple requirement but sometimes being debated. This is my "current" approach how to deal with validation. I have not yet used this approach, and this is just a concept. This approach need to be developed more
First, create a custom validation attributes
Second, create a custom error handler / message
Then create a validator
To use it, then you can do like this:
To call the validator
What is the advantage:
The disadvantage:
See the
ErrorProvider
class (documentation here). It provides a set of standard visual indicators that can be attached to most of the standard WinForms controls.Subjectively, I think it would be better to provide instant feedback that the value the user entered is invalid. That way, they can immediately go back and fix it.
I mean, think about it. The approach you propose would literally give them a giant list of problems at the end, which is not very user-friendly. Besides, how are they going to remember all of those problems to be able to go back and fix them one at a time? (Hint: they're not.)
Instead, I recommend using the
ErrorProvider
class to display any errors right next to the appropriate control. I talked a little bit more about this approach in my answer here and here.Of course, you'll still need to make sure upon final submission (clicking the OK/Submit button) that all the input is valid, but then that's just a simple case of checking for the presence of any errors.
Yes, the idea here is encapsulation. The Form class should only know about form stuff. It shouldn't be required to know what kind of input is/is not valid for all of your different controls.
Instead, this validation logic should be placed elsewhere, such as in a class that stores your data. That class would expose public properties to get and set the data, and inside of the setter method, it would verify the data.
That means that all your Form has to do is call a setter method on your data class. The Form needs to know nothing about how to validate the data, or even what the data means, because the data class handles all of that.
If this is indeed the case, you will need to provide a constructor for the class that accepts as parameters all of the data it needs. The body of the constructor will then validate the specified data and throw an exception if any of it is invalid. The exception will prevent the class from being created, ensuring that no instance of a class that contains invalid data ever exists.
Such a class would probably not have setter methods at all—only getters.
However, this is kind of an unusual requirement in the world of C# (however common it may be in C++). Generally, placing your validation code inside of the setters works just fine.
Why would that change anything? You still handle the validation inside of the private setters. If validation fails, you throw an exception. Because the constructor doesn't handle the exception, it continues bubbling up out of that method to the code that attempted to instantiate the object. If that code wants to handle the exception (e.g., to display an error message to the user), it can do so.
Granted, throwing an exception in the case of invalid input is not necessarily a "best practice". The reason is that exceptions should generally be reserved for unexpected conditions, and users screwing up and providing you with invalid data is, well, to be expected. However:
There are several possible approaches:
When user enters value it is checked during input (
TextChanged
) and validated right away. Create instance of a new class, call property/method what should acceptstring
and returnbool
(or throwException
in case of property), onfalse
- draw special error condition (red label next to text box, something blinking,ErrorProvider
or whatever you can do what should tell user "wrong!").This one I like to use, but a bit differently, usually I only check
Type
and then just trying to parse it straight away in the form. It is possible to abstract more if form operate with thestring
's and all formattings and validation occurs in the class (property setters). Or you can supply form with additional information (by using query methods or attributes) so it can do instant validation without need to instantiate class or using setters. As example,double factor
property can be identified in the form (or even control) to perform 'double.Parseand you can have attribute
DefaultValuewhich can be used to display to the user value in the different way when it's different from default (like it is done by
PropertyGrid`).When user finished input, validate (by trying to set value and catching exception), if wrong - user can't "leave" or "progress" until he press ESC (to cancel changes) or correct his input to pass validation.
This one I dislike. Idea of holding user annoy me (and user ofc). Also it is hard to implement cross checks (like if you have
Min
andMax
values, then user will be pushed to increase "right" one first, otherwise invalidation will fail).That just means let user to enter everything and only validate when he clicks "Ok" button.
I think combining "Ok" button and interactive instant validation is the best for the user. As user knows where he made a mistake through input, but still is free to browse and only will get a "slap" from validation after clicking "Ok" button (at which step you can simply show him first of errors he did, not necessary to show them all).
Error messages can be provided by setters in the old-fashion
LastError
way or as a text in theException
.