|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Попытался впервые применить штатную валидацию в проекте WPF и немножко впал в ступор, не понимая, как лучше поступить в моем случае. Так как, вполне возможно, я чего-то недопонял, я изложу своё видение, а вы поправьте меня, если я что-то понял не так. Про визуальную сторону вопроса речь пока не идет, с шаблонами контролов с ошибкой и отслеживанием HasError вроде как все понятною. Задача вроде как достаточно тривиальная: есть карточка (допустим, клиента) из БД. Она загружается в ViewModel и отображается на представлении, позволяя пользователю ее отредактировать и сохранить. Также возможно создание нового клиента. От валидации требуется: А1) подсвечивать некорректно заполненное автономное поле (например незаполненное обязательное поле, неверный формат email) А2) подсвечивать взаимно некорректные поля (перекрестная проверка, например поле обязательно к заполнению только при включенном определенном флажке) А3) подсветить некорректные поля после нажатия кнопки "Сохранить" при валидации сохранения (например, такое имя в базе уже существует, выберите другое имя), при этом подсветка должна слетать, как только началось изменение поля (ибо до следующего нажатия кнопки "Сохранить" мы не лезем в базу и не знаем о корректности поля) А4) очень желательно включение валидации только после первого нажатия кнопки "сохранить". То есть пользователь создал новую карточку, там все не заполнено, то есть почти все некорректно, пусть спокойно заполняет без надоедливых индикаторов, как только нажмет "Сохранить", тогда уже начинается работа индикаторов в реальном времени, помогающих исправить ошибки. При этом я придерживаюсь максимально декларативного способа описания представления и принципа - ViewModel ничего не знает о View и не управляет им. Что мы имеем в арсенале WPF: 1. Свои правила валидации. Во-первых, декларативное подключение правила дико громоздко. То есть для подключения правила у меня возникает СЕМЬ дополнительных строк в разметке НА КАЖДЫЙ байндинг. Во-вторых, правило работает только со значением. Оно не может (?) получить информацию об объекте, хранящем свойство, к которому привязано правило, соответственно, не может оценить какие-то другие его свойства. В-третьих, базовый класс правила не является DependencyObject, следовательно я не могу создать для него свойства зависимости, которые могли бы решить вопросы как перекрестной проверки элемента (в том числе самого с собой), так и отключения правила (как говорил выше - п.А4 - до первого нажатия кнопки "Сохранить") Ну и в-четвертых, правило срабатывает только когда пользователь полез ручонками в контрол. То есть для созданного или загруженного из БД объекта я не получаю валидацию. Также ошибка не сбрасывается при программной установке свойства, даже если байндинг TwoWay. То есть если я пытаюсь загрузить в тот же View другую ViewModel, индикаторы остаются на месте. В итоге, с учетом вышеперечисленного, особенно "в-четвертых" я вообще плохо понимаю методику удобного практического использования правил. 2. Исключения в модели. Даже если опустить вечную философскую дискуссию о применимости исключений для валидации, остается проблема с индикацией. Исключение при установке свойства позволит установить индикатор ошибки на контрол. А снять его как? Допустим, пользователь переключил какой-то, ошибочное поле стало неактивным или заполнилось автоматически корректным значением (со стороны модели). Ошибка продолжает висеть, потому что нет "антиисключения", которое позволит ее снять. 3. Реализация INotifyDataErrorInfo. Выглядит наиболее грамотным решением вопроса, так как позволяет управлять индикацией валидации, сбрасывать ее при необходимости, собирать сводные ошибки, отключать валидацию до первой попытке сохранения и т.п. Но из достоинств соответственно вытекают недостатки. Во-первых, о декларативности речь не идет, то есть вся валидация перемещается в код, а хотелось бы примитивные вещи типа проверки на пустоту или на невыбранный SelectedItem цеплять в разметке, чтобы не засорять код. Во-вторых, запуск процесса валидации также придется прописывать вручную - то есть либо прописывать веер хендлеров для перехвата PropertyChanged на каждом свойстве, либо делать громоздкую обработку OnPropertyChanged, в любом случае разрушается атомарность, независимость процесса валидации полей, получается не очень удобный монолит. 4. BindingGroup Ну BindingGroup, в общем-то, ни о чем. То есть он позволяет слегка декларативно проверить группу контролов (правило при этом все равно пишется специфическое под ViewModel) при этом, грубо говоря, у тебя будет выделена красной рамкой вся форма, а не конкретные контролы. То есть я не вижу в BindingGroup полноценной замены INotifyDataErrorInfo, если уж его реализовывать. ======================================================================== В общем, правильно ли я все изложил выше или упустил какие-то полезные моменты? Как вы реализуете подобные сценарии в своих проектах? ... |
|||
:
Нравится:
Не нравится:
|
|||
10.02.2016, 15:08 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Shocker.Pro, да вроде в целом всё верно. Давно забил на валидацию в Binding'е, так как сам код связывания превращается в какой то ад с кучей срок. Всю валидацию лучше реализовывать через ViewModel и не париться. Я стараюсь просто придерживаться того, что всё, что отображается во View, должно быть отражением ViewModel. То есть, если мне нужно указать пользователю, что поле Login некорректно и не должно быть пустым, то создаю соответствующее свойство IsLoginValid и через DataTrigger в View меняю отображения нужных мне элементов. Так проще, понятнее, а валидация в связывании только усложняет процесс отладки, увеличивает количество XAML кода, требует создания дополнительных классов и т.д. Вообще у меня для тебя плохие новости :) чем дальше в лес, тем больше дров. Особенно, если интерфейс сложный и нашпигован огромным количеством визуальных элементов, тем меньше подходит декларативный способ описания интерфейса. К примеру, не так давно была задача сделать таблицу 200 на 100 ячеек, в каждой ячейке отображается довольно сложное содержимое состоящее примерно из 10 визуальных элементов (включая ItemsControl), векторные изображения, Binding'и. Размер ячеек динамически меняется в зависимости от режима отображения. То есть когда ячейки маленькие, то почти все влазят на экран. Виртуализация тут ни как не помогала, а при прокручивании содержимого дико тормозила, так как постоянно генерировала и удаляло кучу контролов. К сожалению через XAML реализовать такое не представляется возможным. Сделать, конечно можно. Но тормозить всё будет безбожно, потому как на экране будет размещаться over 100'000 контролов. В результате пришлось ячейки для таблицы делать через Visual и прирост производительности был существенно большим. Но платой за этой было то, что пришлось весь Render'инг этих ячеек делать ручками :) Короче весь смысл XAML в том, чтоб облегчить жизнь, но не решить все жизненные проблемы :) ... |
|||
:
Нравится:
Не нравится:
|
|||
10.02.2016, 17:06 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Roman MejtesТо есть, если мне нужно указать пользователю, что поле Login некорректно и не должно быть пустым, то создаю соответствующее свойство IsLoginValid и через DataTrigger в View меняю отображения нужных мне элементов.То есть ты вообще не завязываешься на HasError и встроенную поддержку ErrorTemplate правильно я понял? А INotifyDataErrorInfo реализовывать приходилось? ... |
|||
:
Нравится:
Не нравится:
|
|||
10.02.2016, 17:12 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Shocker.ProRoman MejtesТо есть, если мне нужно указать пользователю, что поле Login некорректно и не должно быть пустым, то создаю соответствующее свойство IsLoginValid и через DataTrigger в View меняю отображения нужных мне элементов.То есть ты вообще не завязываешься на HasError и встроенную поддержку ErrorTemplate правильно я понял? А INotifyDataErrorInfo реализовывать приходилось?в своё время я так же как и ты, пытался скушать кактус :) но так или иначе, всё это неудобно и работает только когда логика простая. Мне важно, чтоб валидация работала везде одинаково, а не по ситуации. если мне нужно разместить поверх контрола свой Adorner, для индикации ошибки, то я проще сделаю это сам, чем буду использовать тот, что идет в коробке к валидации :) ... |
|||
:
Нравится:
Не нравится:
|
|||
10.02.2016, 17:34 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Roman MejtesВообще у меня для тебя плохие новости :) чем дальше в лес, тем больше дров. Особенно, если интерфейс сложный и нашпигован огромным количеством визуальных элементовRoman Mejtesв своё время я так же как и ты, пытался скушать кактус :) но так или иначе, всё это неудобно и работает только когда логика простая.Стандартные фреймворки решают 90% задачи. Когда надо решить 100%, начинаешь сначала извращаться, потом выводишь систему, потом появляются хелперы и библиотеки, в конечном итоге появляется собственный фреймворк. И ты начинаешь решать через него и оставшиеся 90% задачи для единообразия. Вот только пройти все равно приходится через кактус ) Я внимательно вникаю в твои советы, но не использую твой готовый код, просто пишу свой и по своему, подглядывая в твой и стараюсь пройти те этапы, которые ты уже прошел, иначе не будет понимания. ... |
|||
:
Нравится:
Не нравится:
|
|||
10.02.2016, 23:29 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
3-й способ как основной. Если какие-то мелочи хочется упростить, то вперемешку с 1-м (главное, чтоб потом не запутаться, где что делается, так что увлекаться таким дополнением не стоит). 2-й не представляю, как делать по нажатию кнопки. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.02.2016, 17:35 |
|
Валидация в WPF
|
|||
---|---|---|---|
#18+
Shocker.Pro 3. Реализация INotifyDataErrorInfo. Это, но надо помнить, что не все контролы его поддерживают и для таких придется реализовать еще и IDataErrorInfo (XAML: ValidatesOnDataErrors=True) Shocker.Proполучается не очень удобный монолитувы ... |
|||
:
Нравится:
Не нравится:
|
|||
16.03.2016, 20:56 |
|
|
start [/forum/topic.php?fid=21&msg=39167871&tid=1440716]: |
0ms |
get settings: |
11ms |
get forum list: |
14ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
151ms |
get topic data: |
13ms |
get forum data: |
3ms |
get page messages: |
57ms |
get tp. blocked users: |
2ms |
others: | 14ms |
total: | 271ms |
0 / 0 |