Статьи

Проверка данных в Windows Phone 8

Всякий раз, когда вам нужно ввести данные, вы, вероятно, добавите некоторую форму проверки данных. Существует множество информации о том, как это сделать в Silverlight и WPF, но найти информацию по Windows Phone 8 довольно сложно. Проведя некоторое исследование по этой теме, я попал в библиотеку Fluent Validation, доступную на Nuget, и решил сам написать что-нибудь по этой теме. Я собрал быстрый пример, включая интеграцию с SimpleIoc в MVVM Light.

Добавление пакета

Мы начнем с пустого решения, добавим в него библиотеки MVVM Light и свяжем текст данных MainPage с MainViewModel, как вы это обычно делаете. После завершения установки MVVM пришло время добавить пакет Fluent Validation в проект.

Install-Package FluentValidation

Fluent Validation — проект с открытым исходным кодом, доступный на Codeplex

Fluent Validation имеет библиотеки для

  • .net 4.0
  • MVC 3
  • MVC 4
  • MVC 5
  • портативный

Настройки переносимой библиотеки классов

Это означает, что реализация, которую я собираюсь показать, будет работать так же хорошо в приложениях Магазина Windows, так что +1 для совместного использования кода!

Создание модели

Демо-приложение представляет собой простую форму регистрации, новый пользователь сможет ввести свое имя, дату рождения и страну. Мы будем строить правила проверки этих свойств позже. В этом приложении будет только один класс моделей, который называется Member.

    public class Member
    {
        public string Name { get; set; }
        public DateTime BirthDate { get; set; }
        public string Country { get; set; }
    }

Настройка проверки

Есть некоторая работа, связанная с настройкой всего, не волнуйтесь, это вряд ли ракетостроение.

Мы собираемся создать ValidaterFactory. Нам это нужно, потому что мы собираемся использовать SimpleIoc для внедрения валидаторов в ViewModels. Если вы не хотите этого делать, вы можете просто создать валидаторы и создавать их экземпляры всякий раз, когда вам это нужно, на странице Codeplex Fluent Validation есть отличная документация по этому вопросу.

ValidatorFactory наследуется от класса ValidatorFactoryBase, включенного в библиотеку

    public class ValidatorFactory : ValidatorFactoryBase
    {
        public ValidatorFactory()
        {
            //register the Validators
            SimpleIoc.Default.Register<IValidator<Member>, MemberValidator>();
        }
     
        public override IValidator CreateInstance(Type validatorType)
        {
            return SimpleIoc.Default.GetInstance(validatorType) as IValidator;
        }
    }

В конструкторе ValidatorFactory я регистрирую все валидаторы. Вы также можете сделать это в ViewModelLocator, как и любой другой класс / репозиторий / viewmodel, но это делает мой VMLocator чище и немного сближает логику проверки.

Функция CreateInstance должна быть переопределена и возвращает экземпляр запрошенного валидатора.

Создание валидатора

Почти на этом этапе мы создаем валидатор для класса Member. Валидатор наследует от AbstractValidator <T>, где T — это класс, который вы хотите проверить.

    public class MemberValidator : AbstractValidator<Member>
    {
        public MemberValidator()
        {
            RuleFor(member => member.Name).NotEmpty();
            RuleFor(member => member.BirthDate).LessThan(DateTime.Today);
        }
    }

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

Нам нужно зарегистрировать этот класс в нашем IOC, поэтому в ViewModelLocator добавьте эту строку

    SimpleIoc.Default.Register<ValidatorFactory>(true);

Мы передаем ‘true’ в качестве параметра, чтобы убедиться, что объект создается в момент регистрации, таким образом, все валидаторы также регистрируются в IOC (как видно из конструктора ValidatorFactory).

Для справки, это все встроенные валидаторы Fluent Validation

  • Не ноль
  • Не пустой
  • Не равный
  • равных
  • длина
  • Меньше, чем
  • LessThanOrEqual
  • Лучше чем
  • GreaterThanOrEqual
  • сказуемое
  • RegEx
  • Электронное письмо

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

Проверка данных

Итак, у нас есть фабрика, у нас есть валидатор, все мы регистрируемся в нашем IOC, время подключить ViewModels и выполнить некоторую проверку.

В MainViewModel мы добавляем свойство типа Member, с которым будем связываться

    private Member _newMember;
    public Member NewMember
    {
        get { return _newMember; }
        set
        {
            if (_newMember == value) return;
     
            _newMember = value;
     
            RaisePropertyChanged(() => NewMember);
        }
    }

Далее мы добавим два поля, одно для валидатора и одно для команды сохранения

    private ICommand _saveMemberCommand;
    private IValidator<Member> _validator;

Далее следует свойство ICommand для команды Save.

    public ICommand SaveMemberCommand
    {
        get { return _saveMemberCommand ?? (_saveMemberCommand = new RelayCommand(SaveMember)); }
    }

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

    public MainViewModel(ValidatorFactory validator)
    {
        NewMember = new Member
        {
            BirthDate = DateTime.Today
        };
     
        _validator = validator.GetValidator<Member>();
    }

В качестве параметра мы получаем нашу ValidatorFactory, используя ее функцию GetValidator <T>, мы можем заполнить поле _validator. Создается экземпляр NewMember, а для свойства BirthDate установлено значение по умолчанию на сегодняшний день.

И последнее, но не менее важное, метод SaveMember

    private void SaveMember()
    {
        if (IsValid())
        {
            MessageBox.Show("Registration completed!");
        }
    }
     
    private bool IsValid()
    {
        ValidationResult validationResult = _validator.Validate(NewMember);
     
        if (!validationResult.IsValid)
        {
            ShowErrors(validationResult.Errors);
     
            return false;
        }
     
        return true;
    }
     
    private void ShowErrors(IEnumerable<ValidationFailure> errors)
    {
        StringBuilder builder = new StringBuilder();
     
        builder.AppendLine("The following errors occured:");
        foreach (ValidationFailure error in errors)
        {
            builder.AppendLine("- " + error.ErrorMessage);
        }
     
        MessageBox.Show(builder.ToString(), "error", MessageBoxButton.OK);
    }

Чтобы проверить экземпляр, мы вызываем функцию Validate для валидатора для этого типа, в данном случае «Member». Эта функция возвращает ValidationResult. ValidationResult содержит свойство bool IsValid и список найденных ошибок.

Мы перебираем этот список и помещаем каждую ошибку в StringBuilder, чтобы получить хорошее сообщение об ошибке в окне сообщения. Если я попытаюсь сохранить с пустым именем и сегодняшней датой рождения, я получу это.

Наша проверка работает! Но эти сообщения могут быть немного лучше. Fluent Validator предоставляет нам варианты настройки имени свойства или всего сообщения. Измените конструктор MemberValidator на этот

    public MemberValidator()
    {
        RuleFor(member => member.Name).NotEmpty().WithMessage("You should enter your name");
        RuleFor(member => member.BirthDate).LessThan(DateTime.Today).WithName("date of birth");
    }

В строке 3 мы используем .WithMessage («»), чтобы заменить сообщение по умолчанию на то, что мы хотим. Строка 4 заменяет имя свойства по умолчанию, результат похож, но немного отличается

Добавление пользовательского валидатора

Допустим, мы хотим добавить проверку к свойству Country. Это свойство должно иметь значение «Бельгия», «Нидерланды» или «Люксембург» (это называется BeNeLux). Очевидно, это не входит в качестве одного простого валидатора, поэтому мы просто создадим его сами.

Нам понадобится класс, который наследует от PropertyValidator и переопределяет функцию IsValid.

    public class CountryMustBeInBenelux : PropertyValidator
    {
        public CountryMustBeInBenelux()
            : base("{PropertyName} is not a Benelux country")
        {
     
        }
     
        protected override bool IsValid(PropertyValidatorContext context)
        {
            if (context.PropertyValue == null)
                return false;
     
            string country = context.PropertyValue.ToString().ToLower();
     
            return country == "belgium" || country == "netherlands" || country == "luxemburg";
        }
    }

Строка, которую мы передаем в базовый конструктор, является сообщением по умолчанию, которое будет использоваться для этого валидатора. Функция IsValid принимает параметр PropertyValidatorContext. Он будет содержать свойство, которое мы хотим проверить, и его значение.

Далее в конструкторе MemberValidator добавляем проверку для свойства Country

    public MemberValidator()
    {
        RuleFor(member => member.Name).NotEmpty().WithMessage("You should enter your name");
        RuleFor(member => member.BirthDate).LessThan(DateTime.Today).WithName("date of birth");
     
        RuleFor(member => member.Country).SetValidator(new CountryMustBeInBenelux());
    }

Как вы видите, нам нужно использовать функцию SetValidator, чтобы добавить наш собственный валидатор.

Результат теперь такой (я ввел имя и правильную дату рождения, чтобы немного очистить список ошибок)

Вывод

В этой статье я рассмотрел использование Fluent Validator для проверки данных в приложениях Windows Phone 8. Я использовал переносную версию библиотеки, так что эти примеры должны прекрасно работать и на Windows 8.

Библиотека Fluent Validator имеет гораздо больше опций, чем то, что я здесь обсуждал, и все они хорошо документированы, загляните на страницу Codeplex, если вы хотите сделать больше и глубже проверить.

Пример проекта можно найти на моем SkyDrive