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

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

10.04.2025, 10:56. Показов 6482. Ответов 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
3463 / 2784 / 575
Регистрация: 04.09.2018
Сообщений: 8,757
Записей в блоге: 3
11.04.2025, 17:21
Студворк — интернет-сервис помощи студентам
proa33, в том то и вопрос - зачем тут эта 'другая' сущность...
Здесь вход в понимание валидации и активирования команд произошел методом от противного ("не с той стороны"). Сперва бы сделать нормальную модель всего этого, прочувствовать работу, а уж потом прикручивать сюда INotifyDataErrorInfo.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 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
3463 / 2784 / 575
Регистрация: 04.09.2018
Сообщений: 8,757
Записей в блоге: 3
11.04.2025, 20:52
Цитата Сообщение от Uswer Посмотреть сообщение
Модель не должна реализовывать INotifyDataErrorInfo НИКАК
В данном случае я специально привел слово "модель" прописными буквами, обозначая за модель - всю эту систему в целом, т.е. общие принципы построения, а не Model как слой mvvm.

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

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

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

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

Добавлено через 2 минуты
P.S. Завтра попробую соорудить рабочий пример по этой теме. Но думаю, до этого времени уже Элд Хасп что-то толковое напишет и выложит.
0
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 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
3463 / 2784 / 575
Регистрация: 04.09.2018
Сообщений: 8,757
Записей в блоге: 3
12.04.2025, 00:45
Цитата Сообщение от Uswer Посмотреть сообщение
Стоп, хватит, остановись, не трать время.
Окей, хозяин-барин.

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

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

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

Цитата Сообщение от Uswer Посмотреть сообщение
то видел, что я указывал автора
Я это видел и даже смотрел реализацию.
0
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 1
12.04.2025, 00:55  [ТС]
Цитата Сообщение от Uswer Посмотреть сообщение
Именно поэтому в MainWindowViewModel метод OnCanAddPersonCommandExecute всегда возвращает true.
Тьфу, блин - сам в конец запутался. Выдал мысли в голове за действительность. Извиняюся.
0
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 1
13.04.2025, 15:50  [ТС]
Людям, которые могут прочесть тему, поясню, что в теме обсуждается самая первая фиксация на гитхабе. Последняя фиксация содержит полностью переработанную версию валидации DTO объекта.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 1
13.04.2025, 21:30  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А где INPC?
Я его специально выпилил везде, так как для данного примера он не особо нужен и только загромождает код. В основном проекте INPC конечно же есть.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
// Надо так
Словарь приватный и кое где пересоздаётся полностью, поэтому "ну допустим пусть будет так".
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
 Аватар для Элд Хасп
16166 / 11286 / 2892
Регистрация: 21.04.2018
Сообщений: 33,176
Записей в блоге: 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
2298 / 1614 / 400
Регистрация: 26.06.2017
Сообщений: 4,780
Записей в блоге: 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
Ответ Создать тему
Новые блоги и статьи
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru