45 Stimmen

WinForm UI-Validierung

Ich brauche, um Eingabevalidierung in meinem Winform app zu implementieren. Es gibt viele verschiedene Formulare, wo Daten eingegeben werden können, und ich möchte nicht gehen Kontrolle durch Kontrolle durch Form und erstellen isValid usw. pro Element. Wie haben andere mit diesem behandelt?

Ich sehe, dass die meisten verwandten Beiträge sich mit Web Apps befassen und/oder erwähnen Enterprise Library Validation Anwendungsblock . Ich gebe zu, dass ich ELVAB nicht gründlich recherchiert habe, aber es parece wie Overkill für das, was ich brauche. Mein derzeitiger Gedanke ist, eine Klassenbibliothek mit den verschiedenen Anforderungen zu schreiben und übergeben Sie es ein Steuerelement als Parameter. Ich habe bereits eine Bibliothek von RegEx-Funktionen für Dinge wie isValidZipCode und so weiter, also könnte das ein guter Anfang für mich sein.

Was ich haben möchte, ist eine Schaltfläche Validieren, die onClick Zyklen durch alle Steuerelemente auf dieser Formularseite und führt die erforderliche Validierung. Wie kann ich das erreichen?

2voto

Steven Evers Punkte 15926

In allen meinen Formularen implementiere ich das isValidating-Ereignis für das jeweilige Steuerelement in Frage, und wenn die Daten nicht validieren, habe ich einen errorProvider auf dem Formular und ich verwende seine SetError(...)-Methode, um den Fehler auf das Steuerelement in Frage mit relevanten Informationen zu setzen, warum es falsch ist.

Bearbeiten> Ich sollte beachten, dass ich in der Regel das Mvc-Muster verwenden, wenn dies zu tun, so dass die spezifische Validierung für dieses Steuerelement/Mitglied des Modells am Modell geschieht, so dass die isValidating sieht irgendwie wie diese:

private uicontrol_isValidating(...)
{
    if(!m_Model.MemberNameIsValid())
    {
        errorProvider.SetError(...);
    }
}

2voto

danish Punkte 5426

Entweder auf diese Weise. Oder Sie können ein einziges Validierungsereignis mit allen Steuerelementen verknüpfen, die ähnliche Validierungen benötigen. Dadurch wird die Schleifenbildung aus dem Code entfernt. Nehmen wir an, Sie haben vier Textfelder, die nur Ganzzahlen enthalten können. Was Sie tun können, ist ein einzelnes Ereignis für jedes von ihnen. Ich bin nicht mit jeder IDE so Code unten ist das Beste, was ich mit kommen kann.

this.textbox1.Validated += <ValidatedEvent>
this.textbox2.Validated += <ValidatedEvent>
this.textbox3.Validated += <ValidatedEvent>
this.textbox4.Validated += <ValidatedEvent>

In dem Fall:

  1. Cast Absender als Textbox.
  2. Prüfen, ob der Wert im Textfeld numerisch ist.

Und so geht es weiter mit den Veranstaltungen.

Ich hoffe, das hilft.

2voto

Rey Punkte 29

Wenn Sie die obigen Ideen mit diesem generischen Validierungs-Ereignishandler kombinieren, erhalten Sie ein gutes Validierungsfehler-"Framework" mit allen Validierungsmethoden in Ihren Geschäftsklassen. Ich habe gerade Bruce Code mit der dänischen Idee erweitert. Das wurde für Entity Framework und Dev Express Komponenten gemacht, aber diese Abhängigkeiten können leicht entfernt werden. Viel Spaß damit!

public class ValidationManager
{
    /// <summary>
    /// Call this method to validate all controls of the given control list 
    /// Validating event will be called on each one
    /// </summary>
    /// <param name="controls"></param>
    /// <returns></returns>
    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;
    }

    /// <summary>
    /// Attach all youe Validating events to this event handler (if the controls requieres validation)
    /// A method with name Validate + PropertyName will be searched on the binded business entity, and if found called
    /// Throw an exception with the desired message if a validation error is detected in your method logic
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public static void ValidationHandler(object sender, CancelEventArgs e)
    {
        BaseEdit control = sender as BaseEdit;

        if (control.DataBindings.Count > 0) //control is binded
        {
            string bindedFieldName = control.DataBindings[0].BindingMemberInfo.BindingField;

            object bindedObject = control.BindingManager.Current;

            if (bindedObject != null) //control is binded to an object instance
            {
                //find and call method with name = Validate + PropertyName

                MethodInfo validationMethod = (from method in bindedObject.GetType().GetMethods()
                                               where method.IsPublic &&
                                                     method.Name == String.Format("Validate{0}",bindedFieldName) &&
                                                     method.GetParameters().Count() == 0
                                               select method).FirstOrDefault();

                if (validationMethod != null) //has validation method
                {
                    try
                    {
                        validationMethod.Invoke(bindedObject, null);

                        control.ErrorText = String.Empty; //property value is valid
                    }
                    catch (Exception exp)
                    {
                        control.ErrorText = exp.InnerException.Message;
                        e.Cancel = true;
                    }
                }
            }
        }
    }

    // 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;
    }

}

Methode der Probenvalidierung:

partial class ClientName
{
    public void ValidateFirstName()
    {
        if (String.IsNullOrWhiteSpace(this.FirstName))
            throw new Exception("First Name is required.");
    }

    public void ValidateLastName()
    {
        if (String.IsNullOrWhiteSpace(this.LastName))
            throw new Exception("Last Name is required.");
    }
}

1voto

Kenneth Cochran Punkte 11762

Das Durchlaufen der Kontrollen kann funktionieren, ist aber fehleranfällig. Ich habe an einem Projekt gearbeitet, das diese Technik verwendet hat (zugegeben, es war ein Delphi-Projekt und nicht C#), und es hat wie erwartet funktioniert, aber es war sehr schwierig zu aktualisieren, wenn ein Steuerelement hinzugefügt oder geändert wurde. Vielleicht war das korrigierbar. Ich bin mir aber nicht sicher.

Jedenfalls funktionierte es, indem ein einzelner Event-Handler erstellt wurde, der dann an jedes Steuerelement angehängt wurde. Der Handler würde dann RTTI verwenden, um den Typ des Steuerelements zu bestimmen. Dann würde er die Eigenschaft name des Steuerelements in einer großen Select-Anweisung verwenden, um den auszuführenden Validierungscode zu finden. Schlug die Überprüfung fehl, wurde eine Fehlermeldung an den Benutzer gesendet und das Steuerelement erhielt den Fokus. Um die Sache noch komplexer zu machen, war das Formular in mehrere Registerkarten unterteilt, und die richtige Registerkarte musste sichtbar sein, damit das untergeordnete Steuerelement den Fokus erhielt.

Das sind also meine Erfahrungen.

Ich würde viel lieber ein Passive View Design Pattern verwenden, um alle Geschäftsregeln aus dem Formular zu entfernen und sie in eine Presenter-Klasse zu verschieben. Je nach Zustand Ihres Formulars kann das mehr Arbeit sein, als Sie bereit sind zu investieren.

1voto

Will Eddins Punkte 13220

Nur eine grobe Idee:

void btnValidate_Click(object sender, EventArgs e)
{
  foreach( Control c in this.Controls )
  {
    if( c is TextBox )
    {
      TextBox tbToValidate = (TextBox)c;
      Validate(tbToValidate.Text);
    }
  }
}

Sie könnten die Textfelder in ein Panel einfügen und nur die darin enthaltenen Steuerelemente in einer Schleife durchlaufen, wenn Sie das Durchlaufen anderer Steuerelemente vermeiden wollen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X