Apologies if I'm overlooking something obvious here.
Problem Context
I have an application which allows the user to fill out a form, the contents of which will be uploaded to a database. When they hit the 'Upload' button, the form must be validated. The user will be alerted if any fields are not valid.
Current Solution
When the user hits the button to upload the transaction, validFormSelections()
is called.
if (!this.validateChildren())
{
// Display an error
return false;
}
else
{
return true;
}
For each combobox, the validating method is called, e.g.
cmbName_Validating(object sender, CancelEventArgs e)
{
if (/* not valid for whatever reason */)
{
errorProvider.Clear();
e.Cancel = true;
errorProvider.setError((Control)sender, /* suitable error message */)
}
else
{
errorProvider.SetError((Control)sender, "");
}
}
Problem
What I'd like to do is show the errorProvider
next to all fields which are not valid, however at the moment since e.Cancel = true;
in an individual validation method, then (if my understanding is correct) it does not continue validating other fields once the first invalid field is found.
How can I make it so that I can call this.validateChildren()
and still show an errorProvider
next to all invalid fields?
Attempts at researching a solution so far
As far as I understand it, my problem is needing to set the CancelEventArgs
' Cancel
property to true, because it 'cuts off' the rest of the validation. So, it would be a good idea to find a way to avoid setting this property until later or find a way around this. But I'm not sure how to go about doing that.
One of my initial thoughts was that I could solve this fairly easily if only I could return a boolean
from each validating event handler (however I suspected, and have since learned, that I cannot do that). I see an alternative to return values in event handlers is to pass in a mutable value (such as, for example, CancelEventArgs
itself). This seems like it could perhaps be a suitable solution, however I'm not sure how I can do this myself...
Setting e.Cancel = true
is the problem of course. So just don't do that. It is in general a fairly unpleasant feature, making data entry very modal. A much friendlier approach is to give the user free reign of the data entry task, entering data in an order that isn't necessarily the one you envisioned when you set the tab order.
Just write a little helper method that makes it easy. For example:
private Control firstInvalidControl;
private void SetValidation(object sender, bool ok, string errMsg) {
var ctl = (Control)sender;
if (ok) errorProvider1.SetError(ctl, "");
else {
if (firstInvalidControl == null) firstInvalidControl = ctl;
errorProvider1.SetError(ctl, errMsg);
}
}
Call SetValidation() in the Validating event handlers of your controls. Now you have a very easy time to get all the appropriate warnings displayed, as well help the user navigate to a control that he probably needs to work on first. For example:
private void OKButton_Click(object sender, EventArgs e) {
firstInvalidControl = null;
this.ValidateChildren();
if (firstInvalidControl != null) firstInvalidControl.Focus();
else {
// Data is good, use it
// ...
}
}