Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1

MVVM блокировка кнопки до валидации

10.04.2025, 10:56. Показов 6184. Ответов 71
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.

Исходные данные: разработка в шаблоне MVVM, у некоего окна есть кнопка "ОК", к которой в xaml привязана команда из VM. Также на уровне VM делается валидация данных при помощи INotifyDataErrorInfo. При открытии окна кнопка "ОК" доступна для нажатия хотя дефолтные данные в окне не валидны. Кнопка блокируется если начать вводить не правильные данные, то есть кнопка реагирует только тогда, когда начинает работать механизм валидации.

Вопрос в следующем: как при открытии окна сделать кнопку "ОК" недоступной для нажатия с сохранение её зависимости от CanExecute изменяемой при валидации?

Добавлено через 5 минут
Дополнительное пояснение. Допустим есть одно текстовое поле, которое не может быть пустым и эта проверка делается в VM, после чего устанавливается HasErrors, на который и реагирует кнопка. Но сразу после открытия окна поле пустое и если его не трогать оно также остаётся пустым (не валидным), но в этом случае HasErrors не устанавливается.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
10.04.2025, 10:56
Ответы с готовыми решениями:

Обработка валидации данных в слое VM (MVVM)
Доброго дня всем. Понимаю, что тема избита, но так как я только изучаю WPF и его применение в...

Рисую линию без MVVM все ОК. C MVVM произвольный старт
Добрый день всем. Рисую линию при помощи анимации. Все получилось -рисуется, стирается. Но у...

Приложение MVVM. Быстрая "Нестрогая MVVM" реализация
В этой теме будет размещена простая и быстрая реализация. Основная тема:...

71
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
11.04.2025, 17:21
Студворк — интернет-сервис помощи студентам
proa33, в том то и вопрос - зачем тут эта 'другая' сущность...
Здесь вход в понимание валидации и активирования команд произошел методом от противного ("не с той стороны"). Сперва бы сделать нормальную модель всего этого, прочувствовать работу, а уж потом прикручивать сюда INotifyDataErrorInfo.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
11.04.2025, 17:27
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Чего-то это мне не нравится - ObservableObject...
А BaseViewModel? По факту это не ViewModel, а ObservableValidator.
Такая реализация по умолчанию рассчитана на валидное состояние VM после инициализации полей.

Uswer, это очень похоже на "обрезки" Community Toolkits.
Если вам нужно "попроще и понятнее для себя", то лучше использовать что-то своё интуитивно понятное.
Если вам нужно сделать "для работы", то лучше использовать какой-то распространённый фреймворк (тот же Community Toolkits) и все решения уже реализовывать его инструментами.

Добавлено через 2 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Такая реализация
И конкретно эта реализация несколько кривая.
Например, в public IEnumerable GetErrors(string propertyName) должна быть обработка string.IsNullOrEmpty(propertyName). При таком запросе должны возвращаться все ошибки по всем свойствам.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
11.04.2025, 20:26  [ТС]
Цитата Сообщение от proa33 Посмотреть сообщение
Дальше все это надо выбросить и переделать по нормальному.
Ну Ок. Нормальную реализацию такого примера показать можете? Мне же надо где-то это увидеть, раз я такое говно написал!
Цитата Сообщение от proa33 Посмотреть сообщение
Валидацию проходит свойство Name а не ваша модель
Так и задумано.
Цитата Сообщение от wizard41 Посмотреть сообщение
в том то и вопрос - зачем тут эта 'другая' сущность...
Я думал, что это очевидная вещь для тех кто "шарит", кроме того, даже название решения должно было подсказать. Ну раз это не очевидно, то расскажу. _model это DTO получаемый и отправляемый в сервис. Ничего с ним сделать нельзя, какой пришёл, такой и ушёл. Однако в коде этот DTO, точнее его свойства, оборачиваются свойствами VM. Именно там (в VM) выполняется валидация и прочие манипуляции. Разве не для этого вообще существует слой ViewModel, а знатоки?
Цитата Сообщение от wizard41 Посмотреть сообщение
Сперва бы сделать нормальную модель всего этого, прочувствовать работу, а уж потом прикручивать сюда INotifyDataErrorInfo.
Опять пустые слова. Не, ну серьёзно. Модель не должна реализовывать INotifyDataErrorInfo НИКАК. Это не её уровень ответственности. Элд Хасп, прошу рассудить прав ли я с точки зрения чистоты MVVM. INotifyDataErrorInfo это интерфейс чисто слоя VM, причём тут модель?
Цитата Сообщение от Элд Хасп Посмотреть сообщение
И конкретно эта реализация несколько кривая.
Возможно, так как я собираю нечто из говна и палок. Иду на ощупь в новом для себя MVVP+WPF.

Ладно. Всем спасибо за участие, я как нибудь сам разберусь. Потрачу кучу времени, но разберусь. Считаю тему исчерпанной (мне всё понятно).
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
11.04.2025, 20:52
Цитата Сообщение от Uswer Посмотреть сообщение
Модель не должна реализовывать INotifyDataErrorInfo НИКАК
В данном случае я специально привел слово "модель" прописными буквами, обозначая за модель - всю эту систему в целом, т.е. общие принципы построения, а не Model как слой mvvm.

Добавлено через 1 минуту
Цитата Сообщение от Uswer Посмотреть сообщение
_model это DTO получаемый и отправляемый в сервис
Тогда тут в корне все неверно...
Uswer, извини, но начинать надо не с этого.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
11.04.2025, 21:18  [ТС]
Цитата Сообщение от wizard41 Посмотреть сообщение
Тогда тут в корне все неверно...
Ну научи как надо, ёпрст.

Добавлено через 1 минуту
Простой же пример: есть элементарная DTO, которую надо обрабатывать в коде (создавать новые экземпляры, изменять существующие). Вот прям на этих двух окнах покажи.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
11.04.2025, 21:31
Цитата Сообщение от Uswer Посмотреть сообщение
прям на этих двух окнах покажи
Да сколько можно их показывать? )) Вон, у Элд Хасп таких примеров куча, причем всяких разных реализаций и взаимодействий, собранные с отдельной теме в его подписи. Почему бы не поизучать примеры там?
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
11.04.2025, 21:35  [ТС]
Ну молодец, отдыхай.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
11.04.2025, 21:56
Uswer, ну вот чистая критика того что на гит-хабе: я открыл, по диагонали посмотрел - вроде да, что-то должно работать. Но INotifyDataErrorInfo не там где надо - бросилось в глаза сразу. Да и вообще, зачем он тут нужен (пока). Далее, непонятно почему твою DTO рожает вью-модель..? Ну и прочие мелкие детали.

Суть то вопроса более узка: как запретить выполнение команд (деактивировать контролы) при некоторых условиях.. И когда этот момент станет ясен, можно переходить к более "крутым" фишкам, типа реализации INotifyDataErrorInfo.. Но ты это даже не понял, а сразу начал с потолка..

Твой проект нужно не исправлять, а переделывать, что требует чуть большего кол-ва времени чем есть в наличии конкретно сейчас. Но вскоре он разрешится, так или иначе.
3 человека тебе говорят одно и то же, но нет, надо блин упорствовать...

Добавлено через 2 минуты
P.S. Завтра попробую соорудить рабочий пример по этой теме. Но думаю, до этого времени уже Элд Хасп что-то толковое напишет и выложит.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
12.04.2025, 00:08  [ТС]
Цитата Сообщение от wizard41 Посмотреть сообщение
Но INotifyDataErrorInfo не там где надо
А где надо по твоему? В соседних темах я спрашивал как делать валидацию данных на уровне VM, там те же участники, что и здесь, рекомендовали INotifyDataErrorInfo. Я поискал, нашёл пример и на его базе сделал свой пример.
Чтобы было понятно - то что на гите это не образец для подражания, это было сделано тяп-ляп, чтобы показать проблему. А то начался разбор на запчасти "это не там лежит", "то не то".
Цитата Сообщение от wizard41 Посмотреть сообщение
Твой проект нужно не исправлять, а переделывать,
Ля-я-я, это всего лишь пример воспроизводящий проблему.

Не по теме:

Ё-моё, когда же отвечающие научатся слышать вопрошающего?!



Цитата Сообщение от wizard41 Посмотреть сообщение
Суть то вопроса более узка: как запретить выполнение команд (деактивировать контролы) при некоторых условиях..
Уже давно задача трансформирована и звучит не так. Суть такая: нужно при выполнении команды валидировать данные, если есть ошибки валидации, прекратить выполнение команды и отобразить ошибки во View. Именно поэтому в MainWindowViewModel метод OnCanAddPersonCommandExecute всегда возвращает true.
Да задача не соответствует показанному на гите, но ещё раз - на гите воспроизведение проблемы так, как её наблюдаю я сейчас. Но хочу от этого избавиться.
Цитата Сообщение от wizard41 Посмотреть сообщение
Но ты это даже не понял
Я понял для чего нужен и как применяется INotifyDataErrorInfo. И для справки - реализация применения INotifyDataErrorInfo не моя, если бы ты читал тему внимательно, то видел, что я указывал автора.

Цитата Сообщение от wizard41 Посмотреть сообщение
Завтра попробую соорудить рабочий пример по этой теме.
Стоп, хватит, остановись, не трать время.

Я же вроде бы писал
Цитата Сообщение от Uswer Посмотреть сообщение
Считаю тему исчерпанной (мне всё понятно).
wizard41, зачем вы продолжаете?

Добавлено через 10 минут
Цитата Сообщение от wizard41 Посмотреть сообщение
Далее, непонятно почему твою DTO рожает вью-модель..?
Представь, что объекты Person извлекаются из БД самописным классом и затем пуляются в коллекцию MainWindowViewModel. Насколько я смог усвоить, то БД+самописный класс = Model в терминах шаблона MVVM. Теперь задача: нужно чтобы пользователь создал новую персону. Где родить экземпляр Person? Или, по твоему, для такой простой операции нужно нагородить километровую конструкцию кода в Model, чтобы что?
0
1596 / 601 / 185
Регистрация: 05.12.2015
Сообщений: 970
12.04.2025, 00:41
Uswer, Посмотри этот пример - переделал твой, это только один из вариантов. Может что пропустил - напиши проверим.
Вложения
Тип файла: zip DTO-Validation-master.zip (19.9 Кб, 9 просмотров)
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
12.04.2025, 00:45
Цитата Сообщение от Uswer Посмотреть сообщение
Стоп, хватит, остановись, не трать время.
Окей, хозяин-барин.

Между делом, стандартный шаблон валидации уже накидал:

где:
- ничего лишнего (DTO) не создается;
- кнопка добавления изначально не активна;
- валидация происходит в реальном времени (при изменении свойств).

Вывод причин невалидного ввода - ну, могут вариантов 10 на вскидку предложить, всяких разных. В том числе и через NotifyDataErrorInfo.

Цитата Сообщение от Uswer Посмотреть сообщение
то видел, что я указывал автора
Я это видел и даже смотрел реализацию.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
12.04.2025, 00:55  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
Именно поэтому в MainWindowViewModel метод OnCanAddPersonCommandExecute всегда возвращает true.
Тьфу, блин - сам в конец запутался. Выдал мысли в голове за действительность. Извиняюся.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
13.04.2025, 15:50  [ТС]
Людям, которые могут прочесть тему, поясню, что в теме обсуждается самая первая фиксация на гитхабе. Последняя фиксация содержит полностью переработанную версию валидации DTO объекта.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
13.04.2025, 19:36
Цитата Сообщение от Uswer Посмотреть сообщение
самая первая фиксация на гитхабе
Uswer, я сделал простую реализацию в вилке вашего репа от первой фиксации. Отправил Pull Request.

Основные моменты.
1) Person - DTO только для чтения. Создаются экземпляры в Модели.
2) Модель с локальным кешем и функцией добавления персов в неё.
3) VM добавления создаётся MainVM специальным методом с передачей в него Модели:
C#
19
        public PersonViewModel CreatePersonVM() => new PersonViewModel(model);
4) Окно добавления создаётся командой View:
C#
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    public static partial class CommandHelper
    {
        private static RoutedUICommand _addPersonWindow;
        public static RoutedUICommand AddPersonWindow
        {
            get
            {
                if (_addPersonWindow is null)
                {
                    _addPersonWindow = new RoutedUICommand("Открыть окно добавления Person", nameof(AddPersonWindow), typeof(CommandHelper));
                    CommandManager.RegisterClassCommandBinding(typeof(Window), new CommandBinding(_addPersonWindow, OnAddPersonWindow, OnCanAddPersonWindow));
                }
                return _addPersonWindow;
            }
        }
 
        private static void OnCanAddPersonWindow(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = e.Source is FrameworkElement fe && fe.DataContext is MainWindowViewModel;
        }
 
        private static void OnAddPersonWindow(object sender, ExecutedRoutedEventArgs e)
        {
            if (!(e.Source is FrameworkElement fe && fe.DataContext is MainWindowViewModel vm))
                throw new NotImplementedException("Команда должна вызываться для контекста данных MainWindowViewModel");
 
            var wind = new AddPersonWindow()
            {
                DataContext = vm.CreatePersonVM(),
            };
            wind.Show();
        }
 
    }
5) За базовую VM взят класс BaseInpc. В RelayCommand добавлена подписка на CommandManager.RequerySuggested.

6) Валидация происходит без запоминания. Такая реализация не оптимальна в рантайм, но зато намного упрощает понимание как же она работает и облегчает обучение.

Добавлено через 29 секунд
C#
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
    public partial class PersonViewModel : INotifyDataErrorInfo
    {
 
        public bool HasErrors =>
            string.IsNullOrWhiteSpace(Name) ||
            string.Equals(Name, "Вася", StringComparison.OrdinalIgnoreCase) ||
            !ValidateAge(DateOfBirth);
 
 
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        protected void RaiseErrorsChanged([CallerMemberName] string propertyName = null)
            => ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
 
        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                foreach (var err in GetErrors(nameof(Name)))
                {
                    yield return $"{nameof(Name)}: {err}";
                }
                foreach (var err in GetErrors(nameof(DateOfBirth)))
                {
                    yield return $"{nameof(DateOfBirth)}: {err}";
                }
                yield break;
            }
 
            if (propertyName is nameof(Name))
            {
                if (string.IsNullOrWhiteSpace(Name))
                {
                    yield return "Name is required";
                }
 
                if (string.Equals(Name, "Вася", StringComparison.OrdinalIgnoreCase))
                {
                    yield return "Васям не положено!";
                }
            }
 
            else if (propertyName is nameof(DateOfBirth))
            {
                if (!ValidateAge(DateOfBirth))
                {
                    yield return "Must be 18 or older";
                }
            }
        }
    }
Добавлено через 27 минут
Uswer, сделал ещё фиксацию "Добавлен вывод всех ошибок при наведении на кнопку."
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
13.04.2025, 21:21
Uswer, по последней фиксации 7b4cac1404cfaad4aa3e5bba397f83aa39218cc5 буду писать комменты сразу здесь по мере проверки кода.

Добавлено через 1 минуту
C#
9
 public abstract class BaseViewModel : INotifyDataErrorInfo, IValidatable
А где INPC? В VM предназначенной для WPF - это основной интерфейс. А его нет.

Добавлено через 2 минуты
C#
13
14
    private Dictionary<string, HashSet<string>> _errors; // Сейчас так
    private readonly Dictionary<string, HashSet<string>> _errors = new(); // Надо так
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
13.04.2025, 21:30  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А где INPC?
Я его специально выпилил везде, так как для данного примера он не особо нужен и только загромождает код. В основном проекте INPC конечно же есть.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
// Надо так
Словарь приватный и кое где пересоздаётся полностью, поэтому "ну допустим пусть будет так".
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
13.04.2025, 21:50
C#
19
public IEnumerable GetErrors(string propertyName)
Нужно обязательно обрабатывать запрос с пустым именем.

C#
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                foreach (KeyValuePair<string, HashSet<string>> prop in _errors)
                {
                    foreach (string error in prop.Value)
                    {
                        if (!string.IsNullOrWhiteSpace(error))
                            yield return $"{prop}: {error}";
                    }
                }
            }
 
            if (_errors.TryGetValue(propertyName, out HashSet<string> propertyErrors))
            {
                foreach (var error in propertyErrors)
                yield return error;
            }
        }
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
13.04.2025, 22:06
Добавлено через 19 минут
Цитата Сообщение от Uswer Посмотреть сообщение
Словарь приватный и кое где пересоздаётся полностью
Это плохо.
Лишний мусор и лишние проверки без всякой необходимости в этом.

Цитата Сообщение от Uswer Посмотреть сообщение
Я его специально выпилил везде, так как для данного примера он не особо нужен
Желательно HasErrors прокидывать через INPC. Часто на это свойство завязывают какие-то триггера или привязки.
А без INPC они работать не будут.

Добавлено через 1 минуту
Uswer, не понял зачем вам этот интерфейс IValidatable?

Добавлено через 5 минут
Внешняя регистрация правил валидации?
Если этот интерфейс имеет обобщённый характер, то результат валидации (ValidationRes.ErrorMessage) это не всегда строка. Там может быть любой объект.

Добавлено через 5 минут
C#
102
        public bool Validate<T>(T validateValue, [CallerMemberName] string propertyName = null)
Реализация этого метода подразумевает хранение только одной ошибки для каждого свойства.
А словарь _errors рассчитан на хранение множества ошибок по каждому свойству.
Следовательно, либо метод Validate<T> реализован не полно, либо словарь ошибок избыточен.

Добавлено через 3 минуты
Полный код с моими исправлениями
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
 
namespace DTO_Validation.ViewModels
{
    public abstract class BaseViewModel : INotifyDataErrorInfo, IValidatable
    {
        #region INotifyDataErrorInfo
 
        private readonly Dictionary<string, HashSet<string>> _errors = new Dictionary<string, HashSet<string>>();
 
        public bool HasErrors => _errors.Count > 0;
 
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
 
        public IEnumerable GetErrors(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                foreach (KeyValuePair<string, HashSet<string>> prop in _errors)
                {
                    foreach (string error in prop.Value)
                    {
                        if (!string.IsNullOrWhiteSpace(error))
                            yield return $"{prop}: {error}";
                    }
                }
            }
 
            if (_errors.TryGetValue(propertyName, out HashSet<string> propertyErrors))
            {
                foreach (var error in propertyErrors)
                    yield return error;
            }
        }
 
        protected void AddValidationError(string error, [CallerMemberName] string propertyName = "")
        {
            if (propertyName == null)
                propertyName = string.Empty;
 
            if (!_errors.ContainsKey(propertyName))
                _errors[propertyName] = new HashSet<string>();
 
            if (_errors[propertyName].Add(error))
                RaiseValidationErrorsChanged(propertyName);
 
            // После изменения состояний ошибок надо подымать INPC для HasErrors
        }
 
        protected void ClearValidationErrors([CallerMemberName] string propertyName = "")
        {
            if (propertyName == null)
                propertyName = string.Empty;
 
            if (string.IsNullOrEmpty(propertyName))
            {
                _errors.Clear();
            }
            else
            {
                if (_errors.Remove(propertyName))
                    RaiseValidationErrorsChanged(propertyName);
            }
            // После изменения состояний ошибок надо подымать INPC для HasErrors
        }
 
        protected void ClearAllValidationErrors()
        {
            RaiseValidationErrorsChanged(string.Empty);
        }
 
        private void RaiseValidationErrorsChanged(string propertyName)
        {
            RaiseErrorsChanged(new DataErrorsChangedEventArgs(propertyName));
        }
 
        protected virtual void RaiseErrorsChanged(DataErrorsChangedEventArgs e) => ErrorsChanged?.Invoke(this, e);
        #endregion
 
        #region IValidatable
        private readonly Dictionary<string, Func<object, ValidationRes>> _verificationMethods = new Dictionary<string, Func<object, ValidationRes>>();
 
        public void Register(string propertyName, Func<object, ValidationRes> validatingMetod)
        {
            if (propertyName == null)
                propertyName = string.Empty;
 
            if (validatingMetod is null)
            {
                _verificationMethods.Remove(propertyName);
            }
            else
            {
                _verificationMethods[propertyName] = validatingMetod;
            }
        }
 
        public bool Validate<T>(T validateValue, [CallerMemberName] string propertyName = null)
        {
            bool _isValid = true;
 
            if (propertyName == null)
                propertyName = string.Empty;
 
            ClearValidationErrors(propertyName);
 
            if (_verificationMethods.TryGetValue(propertyName, out Func<object, ValidationRes> validatingMetod))
            {
                ValidationRes result = validatingMetod(validateValue);
                if (!result.Success) // Ошибка валидации есть
                {
                    AddValidationError(result.ErrorMessage, propertyName);
                    _isValid = false;
                }
            }
 
            return _isValid;
        }
        #endregion
 
 
    }
}
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
13.04.2025, 22:10
Uswer, Теперь к PersonViewModel переходим.

Добавлено через 1 минуту
C#
8
    public PersonDTO Person { get; }
Если PersonDTO мутабельный, то делать его публичным нельзя. Это делает возможным напрямую изменение его свойств потребителями. И тогда никакие уведомления об изменениях не сработают.

Добавлено через 1 минуту
C#
10
    public Action<PersonDTO> SaveChanges { get; set; }
Не пойму зачем это свойство-делегат нужно.
0
2296 / 1612 / 400
Регистрация: 26.06.2017
Сообщений: 4,774
Записей в блоге: 1
13.04.2025, 22:10  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
_errors рассчитан на хранение множества ошибок по каждому свойству.
Согласен и это легко исправить.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
13.04.2025, 22:10

Избежать одновременного выполнения LostFocus и команды кнопки MVVM
Помогите пожалуйста много об этом спрашивали, но ответа так и не нашла, может кто-то уже решил...

Кнопки в MVVM
Добрый день! У меня есть на View есть кнопка, которая должна просто открывать проводник. Код ее уже...

Не могу сделать отображение данных с кнопки и TextBox в MVVM
Не могу сделать отображение данных с помощью кнопки и TextBox. Только начал копаться в MVVM и хочу...

Добавление кнопки в форме с кнопки и смена фона новой кнопки
Хочу сделать так чтоб когда я запускал программу там было 2 кнопки, одна отвечала за создание новой...

Временное отключение валидации
&lt;asp:TextBox ID=&quot;FNBox&quot; runat=&quot;server&quot;&gt;&lt;/asp:TextBox&gt; &lt;asp:RequiredFieldValidator...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru