Pull to refresh

Валидация данных в WPF приложениях

Валидация данных это то с чем мне в последнее время приходится работать и так как в голове накопились кое какие знания, надеюсь что они кому то помогут и не дадут наступать на те же грабли.
Сегодня я попробую рассказать об основных правилах валидации, и способах её применения на практике в программировании на 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 мы должны увидеть следующее:

image

Теперь создадим наш объект и прикрутим его к нашему окну(код 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; }
}
}

После компиляции и запуска приложения мы увидим следующее окно в котором можем поэкспериментировать:
image
На этом с первым типом валидации мы закончим.

Валидация с использованием интерфейса 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));


Спасибо за внимание, всем поменьше ошибок.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.