Подтвердить что ты не робот

Проверка подлинности WinForm UI

Мне нужно выполнить проверку ввода во всем приложении winform. Существует множество различных форм, в которые можно вводить данные, и я бы не хотел управлять ими по форме и создавать isValid и т.д. Для каждого элемента. Как другие справлялись с этим?

Я вижу, что большинство связанных сообщений посвящено веб-приложениям и/или упоминанию Блок приложений для проверки корпоративной библиотеки. Теперь я признаю, что я не тщательно изучил ELVAB, но, похоже, это излишний фактор для того, что мне нужно. Моя нынешняя мысль состоит в том, чтобы написать библиотеку классов с различными требованиями и передать ей элемент управления в качестве параметра. У меня уже есть библиотека функций RegEx для таких вещей, как isValidZipCode, и поэтому я могу начать с нее.

То, что я хотел бы иметь, это кнопка Validate, в которой onClick циклически перемещается по всем элементам управления на этой странице формы и выполняет необходимую проверку. Как я могу это сделать?

4b9b3361

Ответ 1

В моем собственном приложении мне нужно проверить размеры по мере их ввода. Последовательность, которую я использовал, выглядит следующим образом

  • Пользователь выбирает или затем перемещает от контроля.
  • Элемент управления теряет фокус и уведомляет Просмотр отправки его идентификатора и текст ввода.
  • Просмотр проверяет, какая формальная программа (класс, реализующий интерфейс) создал форму и передал ее ID и текст ввода
  • Программа формы возвращает ответ.
  • Если ответ в порядке, View обновляет правильность Ввод формы Класс.
  • Если ответ выполнен в порядке, Форма через интерфейс, который он ОК, чтобы переключить фокус на следующую запись.
  • Если ответ не подходит, просмотр рассматривает ответ и использует Интерфейс формы сообщает форме, что делать. Это обычно означает фокус переходит к оскорбительной записи с сообщением, отображающим пользователь, что произошло.

Преимущество такого подхода заключается в том, что валидация централизована в одном месте для данной Формируемой программы. Мне не нужно изменять каждый элемент управления или даже беспокоиться о различных типах элементов управления в форме. Когда я разработал программное обеспечение, я решил, как пользовательский интерфейс будет работать для текстовых полей, списков, комбинированных ящиков и т.д. Также разные уровни серьезности обрабатываются по-разному.

View заботится о том, чтобы инструктировать форму, что делать через интерфейс. Как это фактически реализовано, обрабатывается самой Формой в ее реализации интерфейса. View не заботится, отображается ли форма для желтого цвета для предупреждения и красного цвета для ошибки. Только то, что он обрабатывает эти два уровня. Позже, если появится лучшая идея отображения предупреждений и ошибок, я могу внести изменения в форму, а не обманывать логику View или проверку в программе Shape.

Вы уже на полпути, если вы подумываете о том, чтобы создать класс для проверки логики проверки, это приведет вас к концу в новом дизайне.

Ответ 2

Валидация уже встроена в библиотеку WinForms.

Каждый Control -уложенный объект имеет два события с именем Validating и Validated. Также он имеет свойство CausesValidation. Если для этого параметра установлено значение true (оно истинно по умолчанию), тогда элемент управления участвует в проверке. В противном случае это не так.

Валидация происходит как часть фокуса. Когда вы фокусируетесь на контроле, его события проверки активируются. Фактически фокусные события запускаются в определенном порядке. Из MSDN:

Когда вы меняете фокус, используя клавиатура (TAB, SHIFT + TAB и т.д.), вызывая функцию Select или SelectNextControl или установление ContainerControl..::. ActiveControl свойство в текущую форму, фокус события происходят в следующем порядке:

  • Enter
  • GotFocus
  • Оставьте
  • Validating
  • Подтверждено
  • LostFocus

Когда вы меняете фокус, используя мышь или вызов метода Фокус, события фокуса происходят в следующем порядок:

  • Enter
  • GotFocus
  • LostFocus
  • Оставьте
  • Validating
  • Подтверждено

Если свойство CausesValidation равно установите значение false, подтверждение и Проверенные события подавляются.

Если свойство Отмена CancelEventArgs установлено в true в Проверка делегата события, всех событий что обычно происходит после Проверяющее событие подавляется.

Также ContainerControl имеет метод ValidateChildren(), который будет проходить через содержащиеся элементы управления и проверять их.

Ответ 3

Я понимаю, что эта тема довольно старая, но я думал, что опубликую решение, которое я придумал.

Самая большая проблема с проверкой на WinForms - это проверка выполняется только тогда, когда элемент управления "потерял фокус". Поэтому пользователь должен щелкнуть внутри текстового поля, а затем щелкнуть в другом месте для выполнения процедуры проверки. Это нормально, если вы беспокоитесь только о правильности введенных данных. Но это не сработает, если вы пытаетесь убедиться, что пользователь не оставил текстовое поле пустым, пропустив его.

В моем решении, когда пользователь нажимает кнопку отправки для формы, я проверяю каждый элемент управления на форме (или любом конкретном контейнере) и использую отражение, чтобы определить, определен ли метод проверки для элемента управления. Если это так, выполняется метод проверки. Если какая-либо проверка не выполняется, процедура возвращает сбой и позволяет остановить процесс. Это решение работает хорошо, особенно если у вас есть несколько форм для проверки.

1) Просто скопируйте и вставьте этот раздел кода в свой проект. Мы используем Reflection, поэтому вам нужно добавить System.Reflection в ваши операторы using.

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 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 determine if the control has a validating method attached to it
    // and if it does, let 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 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) Используйте стандартное проверочное событие для любого элемента управления, который вы хотите проверить. Будьте уверены, использовать e.Cancel, когда проверка не удалась!

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, "");
    }
Не пропустите этот шаг! Это позволит использовать табуляцию для другого элемента управления, даже если проверка не выполняется.

4) Наконец, в методе Submit Button вызовите метод Validation и укажите, какой контейнер вы хотите проверить. Это может быть вся форма или просто контейнер в форме, такой как панель или группа.

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();
    }

Счастливое кодирование!

Ответ 4

Мне бы не нужно было управлять элементом управления по форме и создавать isValid и т.д. за элемент.

В качестве некоторого уровня вам нужно будет определить, что означает valid для каждого элемента управления, если только вы не заботитесь о том, что элемент управления имеет какое-то значение.

Тем не менее, там компонент ErrorProvider, который вы можете использовать, работает очень хорошо.

Ответ 5

Нам повезло с Noogen ValidationProvider. Он прост для простых случаев (проверки типов данных и требуемых полей) и легко добавлять пользовательскую проверку для более сложных случаев.

Ответ 6

Во всех моих формах я реализую событие isValidating для конкретного элемента управления, и если данные не проверяются, у меня есть errorProvider в форме, и я использую его метод SetError (...) для установки ошибки к соответствующему вопросу с соответствующей информацией о том, почему это неправильно.

edit > Следует отметить, что я обычно использую шаблон mvc при этом, поэтому конкретная проверка для этого элемента управления/члена модели происходит в модели, поэтому isValidating выглядит примерно так:

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

Ответ 7

Так или иначе. Или вы можете иметь одно проверяющее событие, связанное со всеми или элементами управления, которые нуждаются в аналогичных проверках. Это приведет к удалению цикла из кода. Скажем, у вас есть четыре текстовых поля, которые могут иметь только целое число. Вы можете сделать одно событие для каждого из них. У меня нет IDE, поэтому код ниже - это лучшее, что я могу придумать.

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

В случае:

  • Отдать отправителя как текстовое поле.
  • Проверьте, является ли значение в текстовом поле числовым.

И так у вас есть события, выстроенные в линию.

Надеюсь, что это поможет.

Ответ 8

Если вы объедините приведенные выше идеи с этим общим проверочным обработчиком событий, вы получите хорошую "фреймворк" проверки достоверности со всеми методами проверки в своих бизнес-классах. Я просто расширил код Брюса датской идеей. Это было сделано для компонентов Entity Framework и Dev Express, но эти зависимости можно легко удалить. Наслаждайтесь!

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 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 determine if the control has a validating method attached to it
    // and if it does, let 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 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;
    }

}

Пример метода проверки:

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.");
    }
}

Ответ 9

Использование элементов управления через управление может работать, но оно подвержено ошибкам. Я работал над проектом, который использовал этот метод (если он был проектом Delphi, а не С#), и он работал так, как ожидалось, но было очень сложно обновить, если элемент управления был добавлен или изменен. Возможно, это было правильно. Я не уверен.

В любом случае это сработало, создав один обработчик событий, который затем был привязан к каждому элементу управления. Затем обработчик будет использовать RTTI для определения типа элемента управления. Затем он будет использовать свойство имени элемента управления в большом элементе select, чтобы найти код проверки для запуска. Если проверка не удалась, пользователю было отправлено сообщение об ошибке, а элементу управления был задан фокус. Чтобы сделать вещи более сложными, форма была разделена на несколько вкладок, и надлежащая вкладка должна была быть видимой для него, чтобы получить контроль.

Так что мой опыт.

Я бы скорее использовал шаблон пассивного представления, чтобы удалить все бизнес-правила из формы и вставить их в класс Presenter. В зависимости от состояния вашей формы, которое может быть больше работы, чем ваше желание инвестировать.

Ответ 10

Просто грубая идея:


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

Вы можете вставлять текстовые поля внутри панели и только прокручивать элементы управления там, если вы хотите избежать чередования других элементов управления.

Ответ 11

Почему вы не используете проверочное событие? Вы можете иметь одно проверочное событие и проверять элементы управления там. Нет необходимости использовать циклы, и каждый элемент управления будет проверен при вводе данных.