#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.