Search code examples
c#winformsvalidationerrorprovider

How can I set an errorProvider against ALL invalid form controls when using this.validateChildren()?


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...


Solution

  • 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
                // ...                
            }
        }