Проверка WPF для всей формы

#wpf #validation

Вопрос:

Я был серьезно разочарован системой проверки WPF. В любом случае! Как я могу подтвердить заполненную форму, нажав на «кнопку»?

По какой-то причине все в WPF так сложно! Я могу выполнить проверку в 1 строке кода в ASP.NET что требует примерно 10-20 строк кода в WPF!!

Я могу сделать это, используя свою собственную платформу ValidationEngine:

 Customer customer = new Customer();
customer.FirstName = "John";
customer.LastName = String.Empty;

ValidationEngine.Validate(customer);

if (customer.BrokenRules.Count > 0)
{
   // do something display the broken rules! 
}
 

Ответ №1:

Приложение WPF должно отключить кнопку для отправки формы, если введенные данные недействительны. Вы можете достичь этого, внедрив интерфейс IDataErrorInfo в свой бизнес-объект, используя привязки с ValidatesOnDataErrors =true . Для настройки внешнего вида отдельных элементов управления в случае ошибок установите a Validation.ErrorTemplate .

XAML:

 <Window x:Class="Example.CustomerWindow" ...>
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Save"
                        CanExecute="SaveCanExecute"
                        Executed="SaveExecuted" />
    </Window.CommandBindings>
    <StackPanel>
        <TextBox Text="{Binding FirstName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
        <TextBox Text="{Binding LastName, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
        <Button Command="ApplicationCommands.Save" IsDefault="True">Save</Button>
        <TextBlock Text="{Binding Error}"/>
    </StackPanel>
</Window>
 

Window Это создает два варианта, в которых вы можете редактировать имя и фамилию клиента. TextBox Кнопка «Сохранить» включена только в том случае, если не произошло ошибок проверки. TextBlock Под кнопкой отображаются текущие ошибки, чтобы пользователь знал, в чем дело.

По умолчанию ErrorTemplate используется тонкая красная граница вокруг ошибочного элемента управления. Если это не вписывается в вашу визуальную концепцию, ознакомьтесь с проверкой в статье Windows Presentation Foundation о CodeProject, чтобы подробно изучить, что можно с этим сделать.

Чтобы окно действительно работало, в Окне и Клиенте должна быть некоторая инфраструктура.

Код За

 // The CustomerWindow class receives the Customer to display
// and manages the Save command
public class CustomerWindow : Window
{
    private Customer CurrentCustomer;
    public CustomerWindow(Customer c) 
    {
        // store the customer for the bindings
        DataContext = CurrentCustomer = c;
        InitializeComponent();
    }

    private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = ValidationEngine.Validate(CurrentCustomer);
    }

    private void SaveExecuted(object sender, ExecutedRoutedEventArgs e) 
    {
        CurrentCustomer.Save();
    }
}

public class Customer : IDataErrorInfo, INotifyPropertyChanged
{
    // holds the actual value of FirstName
    private string FirstNameBackingStore;
    // the accessor for FirstName. Only accepts valid values.
    public string FirstName {
        get { return FirstNameBackingStore; }
        set {
            FirstNameBackingStore = value;
            ValidationEngine.Validate(this);
            OnPropertyChanged("FirstName");
        }
    }
    // similar for LastName        

    string IDataErrorInfo.Error {
        get { return String.Join("n", BrokenRules.Values); }
    }

    string IDataErrorInfo.this[string columnName]
    {
        get { return BrokenRules[columnName]; }
    }
}
 

Очевидным улучшением было бы перемещение IDataErrorInfo реализации вверх по иерархии классов, поскольку это зависит только от ValidationEngine бизнес-объекта , но не от него.

Хотя это действительно больше кода, чем в приведенном вами простом примере, он также обладает гораздо большей функциональностью, чем просто проверка на достоверность. Это дает вам точные и автоматически обновляемые указания пользователю о проблемах с проверкой и автоматически отключает кнопку «Сохранить», если пользователь пытается ввести неверные данные.

Комментарии:

1. Это кажется слишком большим, когда все, что мне нужно, — это убедиться, что все поля заполнены.

2. Если вам нужно только убедиться, что все поля заполнены, вы можете просто ввести свои коды проверки в обработчик нажатия кнопки отправить. Но тогда ваши пользователи спросят: «Почему я не могу отправить форму?», и вам все равно придется реализовать все уведомления и прочее.

3. Было бы логично, если бы все это было частью текстового поля или элементов управления вводом. Например, <Текстовое поле RequiredInput=»true» ErrorMessage=»Пожалуйста, заполните поля» />

4. Правила проверки являются частью модели данных, а не графического интерфейса. Нужно уметь проверять действительность объекта без графического интерфейса. Например, подумайте о пакетных процессорах обработки данных.

5. Вот в чем моя точка зрения! WPF может делегировать проверку на уровень домена с помощью привязки и настройки ValidatesOnDataErrors=true и/или ValidatesOnException=true. Мой ответ показывает один из способов сделать это.

Ответ №2:

Я бы предложил взглянуть на интерфейс IDataErrorInfo вашего бизнес-объекта. Также ознакомьтесь с этой статьей: Текстовое поле для самостоятельной проверки

Ответ №3:

Возможно, вас заинтересует пример приложения для библиотеки приложений WPF (WAF). В нем показано, как использовать проверку в WPF и как управлять кнопкой сохранения при наличии ошибок проверки.

Ответ №4:

Ошибка ValidatesOnDataError используется для проверки бизнес-правил в соответствии с вашими моделями представлений и будет проверена только в том случае, если привязка выполнена успешно.

ValidatesOnExceptions необходимо применять вместе с ValidatesOnDataError для обработки тех сценариев, в которых wpf не может выполнить привязку из-за несоответствия типов данных, допустим, вы хотите привязать текстовое поле к свойству Age (целое число) в вашей модели представления

 <TextBox Text="{Binding Age, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" />
 

Если пользователь вводит недопустимую запись, вводя алфавиты, а не цифры в качестве возраста, например xyz, привязка данных wpf будет молча игнорировать значение, поскольку она не может привязать xyz к возрасту, и ошибка привязки будет потеряна, если привязка не будет оформлена с помощью ValidatesOnExceptions

 <TextBox Text="{Binding Age, ValidatesOnDataErrors=true, ValidatesOnExceptions="True", UpdateSourceTrigger=PropertyChanged}" />
 

ValidatesOnException использует обработку исключений по умолчанию для ошибок привязки с помощью ExceptionValidationRule. Приведенный выше синтаксис является краткой формой для следующего

 <TextBox>
    <TextBox.Text>
        <Binding Path="Age" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
            <Binding.ValidationRules>
                  <ExceptionValidationRule />
             </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>
 

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

 <TextBox.Text>
 <Binding Path="Age" ValidatesOnDataErrors="True">
   <Binding.ValidationRules>
        <rules:NumericRule />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
 

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

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

Ответ №5:

Описание вашей проблемы для меня немного расплывчато. Я имею в виду, я не совсем понимаю, в чем ваша трудность. Предполагая, что DataContext является своего рода презентером или контроллером, у которого есть свойство, представляющее экземпляр клиента, а validateCommand является свойством типа ICommand:

   <StackPanel>  
    <TextBox Text="{Binding CurrentCustomer.FirstName}" />
    <TextBox Text="{Binding CurrentCustomer.LastName}" />
    <Button Content="Validate" 
            Command="{Binding ValidateCommand}"
            CommandParameter="{Binding CurrentCustomer}" />
    <ItemsControl ItemsSource="{Binding CurrentCustomer.BrokenRules}" />
  </StackPanel>
 

Конечно, этот XAML действительно упрощен, и есть другие способы сделать это.
Как веб-разработчик, который сейчас активно работает с WPF, я нахожу, что большинство подобных задач значительно проще в WPF.