- PVSM.RU - https://www.pvsm.ru -
Я думаю, что многие из вас сталкивались с задачей валидации данных в формах. Это долго, утомительно и часто требует значительных усилий. Порой мне кажется, что ребята из Редмонта издеваются над нами, предлагая проводить валидацию так [1]. Шучу, конечно, но этот метод мы использовать не будем.
Мы будем производить валидацию данных формы в автоматическом режиме, при помощи правил, описанных в декларативном стиле. Возьмем простейшую форму:
Здесь есть несколько обычных текстовых полей, поле ввода числа и поле для e-mail'a. Зададим следующие правила для нашей формы:
Поля Фамилия, Имя и Отчество должны быть заполнены как минимум одним печатным (не whitespace) символом:
txtSurname
.ValidateControl()
.IsNotNullOrWhitespace();
txtName
.ValidateControl()
.IsNotNullOrWhitespace();
txtMiddleName
.ValidateControl()
.IsNotNullOrWhitespace();
Возраст должен быть не менее 16 лет. Если указанный возраст менее 21 года – необходимо вывести предупреждение, но разрешить сохранить форму:
nmAge
.ValidateControl()
.IsTrue(ctl => ctl.Value >= 16, "Возраст должен быть не менее 16 лет.", ValidationType.Required)
.IsTrue(ctl => ctl.Value >= 21, "Некоторый контент (21+) для вас будет недоступен.", ValidationType.Optional);
Поле e-mail должно быть заполнено корректным значением (или по крайней мере похожим на e-mail):
txtEMail
.ValidateControl()
.IsValidEMail(false);
Если все поля заполнены правильно – разрешить нажатие кнопки «Сохранить», иначе – нет:
butSave
.ValidateControl()
.EnableByValidationResult();
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
// проверим, что текстовые поля заполнены
txtSurname
.ValidateControl()
.IsNotNullOrWhitespace();
txtName
.ValidateControl()
.IsNotNullOrWhitespace();
txtMiddleName
.ValidateControl()
.IsNotNullOrWhitespace();
// зададим жесткое ограничение в 16 лет
// и не жесткое ограничение (предупреждение не препятствующее вводу формы) в 21 год
nmAge
.ValidateControl()
.IsTrue(ctl => ctl.Value >= 16, "Возраст должен быть не менее 16 лет.", ValidationType.Required)
.IsTrue(ctl => ctl.Value >= 21, "Некоторый контент (21+) для вас будет недоступен.", ValidationType.Optional);
// включим проверку на ввод корректного e-mail'а в этом поле
txtEMail
.ValidateControl()
.IsValidEMail(false);
// по результатам валидации будем разрешать/запрещать указанную кнопку
butSave
.ValidateControl()
.EnableByValidationResult();
}
}
Что мы получим при запуске формы? Во первых: мы получим удобное подсвечивание каждого поля, значение которого заполнено некорректными данными. Во вторых: если подвести курсор мыши к индикатору мы увидим что именно хочет от нас форма:
В третьих: кнопка «Сохранить» будет доступна к нажатию только после успешной валидации данных.
Хорошо, с простой формой мы разобрались, а как быть с формой посложнее? Усложним задачу. Будем делать импровизированную форму поиска статей для Хабра:
Мы имеем три фильтра, при выборе которых должны активироваться проверки заданных условий для каждого фильтра. Плюс активация контролов, которые относятся к выбранному фильтру. Хотелось бы отметить, что управление состоянием контролов не совсем относится к валидации данных, но в данном случае мы не будем заострять на этом внимание.
Активацию контролов будем делать так:
// список чекбоксов с категориями
var categoryCheckBoxes = pnlCategories.Controls.Cast<CheckBox>();
// управление состоянием контролов в зависимости от включенных чекбоксов
dtBegin.EnableByTimer(() => chkFilterByDate.Checked);
dtEnd.EnableByTimer(() => chkFilterByDate.Checked);
pnlCategories.EnableByTimer(() => chkFilterByCategory.Checked);
pnlTextFilter.EnableByTimer(() => chkFilterByText.Checked);
Теперь правила валидации. Если фильтр по дате включен, то начальная дата должна быть меньше либо равна конечной. Начальная дата не может быть раньше 1990 года. Валидация будет происходить в обоих DatePicker'ах, но индикация будет отображаться только на dtEnd:
dtEnd
.ValidateControl()
.IsTrue(ctl => !chkFilterByDate.Checked || dtBegin.Value >= new DateTime(1990, 1, 1),
"Начальная дата отбора не может быть раньше 1990 года")
.IsTrue(ctl => !chkFilterByDate.Checked || dtBegin.Value <= dtEnd.Value,
"Начальная дата должна быть меньше или равной конечной");
Если осуществляется фильтрация по категориям, то необходимо выбрать как минимум одну категорию:
pnlCategories
.ValidateControl()
.IsTrue(ctl => !chkFilterByCategory.Checked || categoryCheckBoxes.Any(c => c.Checked),
"Необходимо выбрать категорию");
Если осуществляется поиск текста, то необходимо задать текст и выбрать где его искать:
pnlTextFilter
.ValidateControl()
.IsTrue(ctl => !chkFilterByText.Checked || chkSearchTextInBody.Checked || chkSearchTextInHeader.Checked,
"Необходимо выбрать места поиска текста")
.IsTrue(ctl => !chkFilterByText.Checked || !string.IsNullOrWhiteSpace(txtSearchText.Text),
"Необходимо задать текст");
Также, для успешной валидации формы, должен быть задан хотя бы один из фильтров для поиска:
gbSearchParameters
.ValidateControl()
.IsTrue(ctl => chkFilterByCategory.Checked || chkFilterByDate.Checked || chkFilterByText.Checked,
"Необходимо задать условия поиска.");
Ну и по результатам валидации формы активируем главную действующую кнопку:
butSearch
.ValidateControl()
.EnableByValidationResult();
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Teleavtomatika.Forms;
namespace Teleavtomatika_Form_Validation
{
public partial class frmMain2 : Form
{
public frmMain2()
{
InitializeComponent();
// список чекбоксов с категориями
var categoryCheckBoxes = pnlCategories.Controls.Cast<CheckBox>();
// управление состоянием контролов в зависимости от включенных чекбоксов
dtBegin.EnableByTimer(() => chkFilterByDate.Checked);
dtEnd.EnableByTimer(() => chkFilterByDate.Checked);
pnlCategories.EnableByTimer(() => chkFilterByCategory.Checked);
pnlTextFilter.EnableByTimer(() => chkFilterByText.Checked);
// теперь правила валидации:
// если фильтр по дате включен, то начальная дата должна быть меньше либо равна конечной
// начальная дата не может быть раньше 1990 года
// валидация будет происходить в обоих DatePicker'ах, но индикация будет отображаться только на dtEnd
dtEnd
.ValidateControl()
.IsTrue(ctl => !chkFilterByDate.Checked || dtBegin.Value >= new DateTime(1990, 1, 1),
"Начальная дата отбора не может быть раньше 1990 года")
.IsTrue(ctl => !chkFilterByDate.Checked || dtBegin.Value <= dtEnd.Value,
"Начальная дата должна быть меньше или равной конечной");
// если осуществляется фильтрация по категориям
// то необходимо выбрать как минимум одну категорию
pnlCategories
.ValidateControl()
.IsTrue(ctl => !chkFilterByCategory.Checked || categoryCheckBoxes.Any(c => c.Checked),
"Необходимо выбрать категорию");
// если осуществляется поиск текста
// то необходимо задать текст
// и выбрать где его искать
pnlTextFilter
.ValidateControl()
.IsTrue(ctl => !chkFilterByText.Checked || chkSearchTextInBody.Checked || chkSearchTextInHeader.Checked,
"Необходимо выбрать места поиска текста")
.IsTrue(ctl => !chkFilterByText.Checked || !string.IsNullOrWhiteSpace(txtSearchText.Text),
"Необходимо задать текст");
// должен быть задан хотя-бы один из фильтров для поиска
gbSearchParameters
.ValidateControl()
.IsTrue(ctl => chkFilterByCategory.Checked || chkFilterByDate.Checked || chkFilterByText.Checked,
"Необходимо задать условия поиска.");
// Ну и по результатам валидации формы активируем главную действующую кнопку "Найти":
butSearch
.ValidateControl()
.EnableByValidationResult();
}
}
}
Посмотреть как это работает в живую можно на видео:
Исходники тут. [2]
Автор: teleavtomatika
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/interfejsy/53234
Ссылки в тексте:
[1] так: http://msdn.microsoft.com/en-us/library/ms229603(v=vs.110).aspx
[2] тут.: https://github.com/teleavtomatika/Teleavtomatika_Form_Validation
[3] Источник: http://habrahabr.ru/post/209820/
Нажмите здесь для печати.