Валидация данных это то с чем мне в последнее время приходится работать и так как в голове накопились кое какие знания, надеюсь что они кому то помогут и не дадут наступать на те же грабли.
Сегодня я попробую рассказать об основных правилах валидации, и способах её применения на практике в программировании на C# WPF. Первым будет рассмотрено валидацию на основе исключений, а потом уже перейдем к валидации которая появилась в фреймворке 3,5, валидации с использованием IDataErrorInfo.
Валидация на основе исключений достаточно простой и в то же время достаточно эффективный метод, в том случае, когда от валидации не нужно сложных взаимных проверок данных, поэтому он используется достаточно часто, так как не требует много времени на реализацию. Суть его заключается в том что для объекта который мы хотим отобразить мы создаем так званую модель, которая несет в себе только ту информацию которая нужна для пользователя.
Пример:
Для проверки данных на окне пользователя к уже сгенерированной разметке добавим необходимые нам контролы:
<TextBlock Grid.Row=«2» Grid.Column=«0» Margin=«5»>Sex:
<TextBox Name=«name» Grid.Row=«0» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Name, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«age» Grid.Row=«1» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Age, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«sex» Grid.Row=«2» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Sex, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
Главным на что тут стоит обратить внимание это Text="{Binding Path=Name, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}" тут мы объявляем, что свойству Text данного текстового поля будет привязано свойство Name, а параметры ValidatesOnExceptions и NotifyOnValidationError соответственно указывают, что после изменения данных будем уведомлять пользователя если эти изменения не соответствуют типу свойства и соответственно не могут быть записаны в объект.
В результате этой разметки в дизайнере Visual Studio 2008 мы должны увидеть следующее:

Теперь создадим наш объект и прикрутим его к нашему окну(код Window1.xaml.cs):
После компиляции и запуска приложения мы увидим следующее окно в котором можем поэкспериментировать:

На этом с первым типом валидации мы закончим.
Интерфейс IDataErrorInfo предоставляет один из лучших методов проверки данных, хотя и в нем не обошлось без ложки дёгтя, но обо всем по порядку. Для реализации данного метода нам нужно в классе нашего объекта реализовать данный интерфейс который состоит из одного индексатора и свойства. Свойство используется для указания общей ошибки а индексатор указывает на конкретное свойство в котором произошла ошибка. Выглядит данный интерфейс следующим образом:
Индексатор и свойство возвращают null если ошибки не было или строку с описанием ошибки, которую ми также можем предоставить пользователю, например с помощью подсказки или сообщения.
Давайте попробуем реализовать данный интерфейс на нашем примере:
В данном случае мы использовали только индексатор, но ничто не мешает вам использовать и свойство Error и реализовать нужную вам проверку там. Для того чтобы этот пример работал нужно указать в разметке параметр ValidatesOnDataErrors=True, он как раз и будет использовать апи IDataErrorInfo. После не больших изменений наша разметка будет выглядеть следующим образом:
<TextBlock Grid.Row=«2» Grid.Column=«0» Margin=«5»>Sex:
<TextBox Name=«name» Grid.Row=«0» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«age» Grid.Row=«1» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Age, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«sex» Grid.Row=«2» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Sex, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
Внимательный читатель заметит также то то я добавил еще один параметр UpdateSourceTrigger=PropertyChanged, который даст нам возможность валидовать данные не только после потери фокуса но и при изменении данных в поле ввода.
А теперь я бы хотел показать вам как можно программно установить контрол в состояние ошибки валидации. Это поможет вам сделать свою валидацию когда по каким то причинам вам не подойдет выше описание способы. Для этого используются два метода, Validation.MarkInvalid и Validation.ClearInvalid.
Для обозначения объекта как такого в котором данные неверны:
Для очистки состояния ошибки валидации:
Спасибо за внимание, всем поменьше ошибок.
Сегодня я попробую рассказать об основных правилах валидации, и способах её применения на практике в программировании на C# WPF. Первым будет рассмотрено валидацию на основе исключений, а потом уже перейдем к валидации которая появилась в фреймворке 3,5, валидации с использованием IDataErrorInfo.
Валидация по исключениям
Валидация на основе исключений достаточно простой и в то же время достаточно эффективный метод, в том случае, когда от валидации не нужно сложных взаимных проверок данных, поэтому он используется достаточно часто, так как не требует много времени на реализацию. Суть его заключается в том что для объекта который мы хотим отобразить мы создаем так званую модель, которая несет в себе только ту информацию которая нужна для пользователя.
Пример:
public class UserItem
{
public enum SexEnum
{
Male,
Famale
}
public string Name { get; set; }
public int Age { get; set; }
public SexEnum Sex { get; set; }
}
Для проверки данных на окне пользователя к уже сгенерированной разметке добавим необходимые нам контролы:
<Window x:Class="WpfValidation.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="400">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="5">Name:
<TextBlock Grid.Row="1" Grid.Column="0" Margin="5">Age:
<TextBlock Grid.Row=«2» Grid.Column=«0» Margin=«5»>Sex:
<TextBox Name=«name» Grid.Row=«0» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Name, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«age» Grid.Row=«1» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Age, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«sex» Grid.Row=«2» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Sex, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}">
Главным на что тут стоит обратить внимание это Text="{Binding Path=Name, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=PropertyChanged}" тут мы объявляем, что свойству Text данного текстового поля будет привязано свойство Name, а параметры ValidatesOnExceptions и NotifyOnValidationError соответственно указывают, что после изменения данных будем уведомлять пользователя если эти изменения не соответствуют типу свойства и соответственно не могут быть записаны в объект.
В результате этой разметки в дизайнере Visual Studio 2008 мы должны увидеть следующее:

Теперь создадим наш объект и прикрутим его к нашему окну(код Window1.xaml.cs):
using System.Windows;
namespace WpfValidation
{
/// /// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
loadData();
}
private void loadData()
{
UserItem item = new UserItem{
Name = "Ihor",
Age = 24,
Sex = UserItem.SexEnum.Male
};
name.DataContext = item;
age.DataContext = item;
sex.DataContext = item;
}
}
public class UserItem
{
public enum SexEnum
{
Male,
Famale
}
public string Name { get; set; }
public int Age { get; set; }
public SexEnum Sex { get; set; }
}
}
После компиляции и запуска приложения мы увидим следующее окно в котором можем поэкспериментировать:

На этом с первым типом валидации мы закончим.
Валидация с использованием интерфейса IDataErrorInfo
Интерфейс IDataErrorInfo предоставляет один из лучших методов проверки данных, хотя и в нем не обошлось без ложки дёгтя, но обо всем по порядку. Для реализации данного метода нам нужно в классе нашего объекта реализовать данный интерфейс который состоит из одного индексатора и свойства. Свойство используется для указания общей ошибки а индексатор указывает на конкретное свойство в котором произошла ошибка. Выглядит данный интерфейс следующим образом:
public interface IDataErrorInfo
{
string Error { get; }
string this[string propertyName] { get; }
}
Индексатор и свойство возвращают null если ошибки не было или строку с описанием ошибки, которую ми также можем предоставить пользователю, например с помощью подсказки или сообщения.
Давайте попробуем реализовать данный интерфейс на нашем примере:
public class UserItem : IDataErrorInfo
{
public enum SexEnum
{
Male,
Famale
}
public string Name { get; set; }
public int Age { get; set; }
public SexEnum Sex { get; set; }
public string this[string columnName]
{
get
{
string msg = null;
switch (columnName)
{
case "Name":
if (string.IsNullOrEmpty(this.Name))
msg = "Name can't be empty.";
break;
case "Age":
if (this.Age <= 0)
msg = "Age can't be less then 0.";
break;
case "Sex":
if (this.Sex != SexEnum.Famale && this.Sex != SexEnum.Male)
{
msg = "Incorect Sex.";
}
break;
default:
throw new ArgumentException(
"Unrecognized property: " + columnName);
}
return msg;
}
}
public string Error
{
get
{
return null;
}
}
}
В данном случае мы использовали только индексатор, но ничто не мешает вам использовать и свойство Error и реализовать нужную вам проверку там. Для того чтобы этот пример работал нужно указать в разметке параметр ValidatesOnDataErrors=True, он как раз и будет использовать апи IDataErrorInfo. После не больших изменений наша разметка будет выглядеть следующим образом:
<Window x:Class="ValidationApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="5">Name:
<TextBlock Grid.Row="1" Grid.Column="0" Margin="5">Age:
<TextBlock Grid.Row=«2» Grid.Column=«0» Margin=«5»>Sex:
<TextBox Name=«name» Grid.Row=«0» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Name, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«age» Grid.Row=«1» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Age, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<TextBox Name=«sex» Grid.Row=«2» Grid.Column=«1» Width=«200» Margin=«5» Text="{Binding Path=Sex, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
Внимательный читатель заметит также то то я добавил еще один параметр UpdateSourceTrigger=PropertyChanged, который даст нам возможность валидовать данные не только после потери фокуса но и при изменении данных в поле ввода.
А теперь я бы хотел показать вам как можно программно установить контрол в состояние ошибки валидации. Это поможет вам сделать свою валидацию когда по каким то причинам вам не подойдет выше описание способы. Для этого используются два метода, Validation.MarkInvalid и Validation.ClearInvalid.
Для обозначения объекта как такого в котором данные неверны:
Validation.MarkInvalid(name.GetBindingExpression(TextBox.TextProperty), new ValidationError(new ExceptionValidationRule(), name.GetBindingExpression(TextBox.TextProperty)));
Для очистки состояния ошибки валидации:
Validation.ClearInvalid(name.GetBindingExpression(TextBox.TextProperty));
Спасибо за внимание, всем поменьше ошибок.