Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WinForm UI Validation

I need to implement input validation throughout my winform app. There are many different forms where data can be entered and I would like to not go control by control by form and create isValid etc per item. How have others dealt with this?

I see that most related posts deal with Web Apps and/or mention Enterprise Library Validation Application Block. Now I admit I haven't thoroughly researched ELVAB but it seems like overkill for what I need. My current thought is to write a class library with the various requirements and pass it a control as a parameter. I already have a Library of RegEx functions for things like isValidZipCode and such so that may be a place for me to start.

What I would like to have is a Validate button that onClick cycles through all the controls on that Form Page and performs the needed validation. How can I accomplish this?

like image 590
Refracted Paladin Avatar asked Apr 20 '09 17:04

Refracted Paladin


People also ask

How to add validation in c# Form?

Step 1: Create a Windows form application. Step 2: Choose “ErrorProvider” form toolbox. Step 3: Select the Text box and go to its properties. In properties choose “Events” and under focus double click on “validating”.

What is Validating event in c#?

Validating is fired just before the validation process starts, this is the place to put the actual validation code. Validated is fired after the validation process has finished and is designed to be the place to something based on the validation result.

What is the importance of the validating event C#?

The Validating event is the ideal place for storing the field-level validation logic for a control. The event handler for validating the event receives an argument of type CancelEventArgs. Its only property, Cancel, cancels the event when it is set to true.


2 Answers

Validation is already built into the WinForms library.

Each Control-derived object has two events named Validating and Validated. Also it has a property called CausesValidation. When this is set to true (it is true by default) then the control participates in validation. Otherwise, it does not.

Validation occurs as part of focus. When you focus off of a control, its validation events are fired. In fact the focus events are fired in a specific order. From MSDN:

When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl..::.ActiveControl property to the current form, focus events occur in the following order:

  1. Enter
  2. GotFocus
  3. Leave
  4. Validating
  5. Validated
  6. LostFocus

When you change the focus by using the mouse or by calling the Focus method, focus events occur in the following order:

  1. Enter
  2. GotFocus
  3. LostFocus
  4. Leave
  5. Validating
  6. Validated

If the CausesValidation property is set to false, the Validating and Validated events are suppressed.

If the Cancel property of the CancelEventArgs is set to true in the Validating event delegate, all events that would usually occur after the Validating event are suppressed.

Also a ContainerControl has a method called ValidateChildren() which will loop through contained controls, and validate them.

like image 80
Matt Brunell Avatar answered Sep 20 '22 12:09

Matt Brunell


I realize this thread is pretty old but I thought I'd post the solution I came up with.

The biggest problem with validation on WinForms is the validation is only executed when the control has "lost focus". So the user has to actually click inside a text box then click somewhere else for the validation routine to execute. This is fine if your only concerned about the data that is entered being correct. But this doesn't work well if you're trying to make sure a user didn't leave a textbox empty by skipping over it.

In my solution, when the user clicks the submit button for a form, I check each control on the form (or whatever container is specified) and use reflection to determine if a validating method is defined for the control. If it is, the validation method is executed. If any of the validations fail, the routine returns a failure and allows the process to stop. This solution works well especially if you have several forms to validate.

1) Just copy and paste this section of code to your project. We're using Reflection so you need to add System.Reflection to your using statements

class Validation {     public static bool hasValidationErrors(System.Windows.Forms.Control.ControlCollection controls)     {         bool hasError = false;          // Now we need to loop through the controls and deterime if any of them have errors         foreach (Control control in controls)         {             // check the control and see what it returns             bool validControl = IsValid(control);             // If it's not valid then set the flag and keep going.  We want to get through all             // the validators so they will display on the screen if errorProviders were used.             if (!validControl)                 hasError = true;              // If its a container control then it may have children that need to be checked             if (control.HasChildren)             {                 if (hasValidationErrors(control.Controls))                     hasError = true;             }         }         return hasError;     }      // Here, let's determine if the control has a validating method attached to it     // and if it does, let's execute it and return the result     private static bool IsValid(object eventSource)     {         string name = "EventValidating";          Type targetType = eventSource.GetType();          do         {             FieldInfo[] fields = targetType.GetFields(                  BindingFlags.Static |                  BindingFlags.Instance |                  BindingFlags.NonPublic);              foreach (FieldInfo field in fields)             {                 if (field.Name == name)                 {                     EventHandlerList eventHandlers = ((EventHandlerList)(eventSource.GetType().GetProperty("Events",                         (BindingFlags.FlattenHierarchy |                         (BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(eventSource, null)));                      Delegate d = eventHandlers[field.GetValue(eventSource)];                      if ((!(d == null)))                     {                         Delegate[] subscribers = d.GetInvocationList();                          // ok we found the validation event,  let's get the event method and call it                         foreach (Delegate d1 in subscribers)                         {                             // create the parameters                             object sender = eventSource;                             CancelEventArgs eventArgs = new CancelEventArgs();                             eventArgs.Cancel = false;                             object[] parameters = new object[2];                             parameters[0] = sender;                             parameters[1] = eventArgs;                             // call the method                             d1.DynamicInvoke(parameters);                             // if the validation failed we need to return that failure                             if (eventArgs.Cancel)                                 return false;                             else                                 return true;                         }                     }                 }             }              targetType = targetType.BaseType;          } while (targetType != null);          return true;     }  } 

2) Use the standard Validating event on any control you want to validate. Be Sure to use e.Cancel when the validation fails!

private void txtLastName_Validating(object sender, CancelEventArgs e)     {         if (txtLastName.Text.Trim() == String.Empty)         {             errorProvider1.SetError(txtLastName, "Last Name is Required");             e.Cancel = true;         }         else             errorProvider1.SetError(txtLastName, "");     } 

3) Don't skip this step! Set the AutoValidate property on the form to EnableAllowFocusChange. This will allow tabbing to another control even when the validation fails.

4) Finally, in your Submit Button method, call the Validation method and specify what container you want to check. It can be the whole form, or just a container on the form like a Panel or a Group.

private void btnSubmit_Click(object sender, EventArgs e)     {         // the controls collection can be the whole form or just a panel or group         if (Validation.hasValidationErrors(frmMain.Controls))             return;          // if we get here the validation passed         this.close();     } 

Happy Coding!

like image 34
Bruce Avatar answered Sep 22 '22 12:09

Bruce