Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
1 / 1 / 1
Регистрация: 18.12.2015
Сообщений: 12
1

MVVM ответ на действие пользователя

13.12.2021, 13:00. Показов 1108. Ответов 10

Author24 — интернет-сервис помощи студентам
Привет! Как реализовать ответ на запрос пользователя в паттерне MVVM? У меня на форме есть кнопка на которой висит команда, описанная в ViewModel, эта команда вызывает метод модели. Как мне уведомить пользователя о том что все успешно выполнено, или что команда не может быть выполнена по таким то причинам? C# WPF
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.12.2021, 13:00
Ответы с готовыми решениями:

Тест по делфи не сверяет ответ пользователя и правильный ответ из ini файла
уважаемые знатоки подскажите в чем вопрос программа не сверяет ответ пользователя и правильный...

Найти пользователя и выполнить действие
Здравствуйте, появилась нерешенная тема, итак чтобы выполнялись действия на разных компьютерах,...

Отследить действие пользователя (за компьютером он или нет)
Добрый день господа, помогите решить проблемку. Мне нужно понять находится ли пользователь за...

Помощь в реализации вопросов о Скроле и подтверждении на действие пользователя.
Всем привет. С C# начал знакомиться совсем недавно, собственно как и с программированием в общем. ...

10
1181 / 623 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 13:18 2
Xristian, вообще, если Вам достаточно метода "сделать на коленке", то всего одна строка:
C#
1
MessageBox.Show("Ops :(", "Something whent wrong.");
Если же Вы делаете комерческое приложение, тогда такого подхода будет недостаточно.
Я бы сделал отдельную, кастомную визуализацию окна ошибки. Далее определился бы с стилистикой приложения, ибо диалоговое окно не всегда уместно (зачастую способ оповещения пользователя реализовывают, как часть интерфеса). Ну и потом бы вызвал эту визуализацию, передав в неё соответствующую информацию.

Добавлено через 5 минут
Всё напрямую зависит от подхода. Их очень много и неясно, как видите это Вы.
1
1 / 1 / 1
Регистрация: 18.12.2015
Сообщений: 12
13.12.2021, 13:41  [ТС] 3
Это очевидное решение, просто вызвать в модели показать сообщение. Я бы хотел сделать это, придерживаясь принципа mvvm, а по нему модель не вмешивается в представление. В идеале, я бы хотел сделать уведомления которые будут появляться в нижнем левом углу, старые будут сдигаться вверх а новые появляться снизу формы, и чтобы они со временем исчезали. Я не понимаю как это можно сделать используя mvvm?
Как я себе это представляю, можно создать в модели observablecollection в которую добавлять ответы на запросы, а viewmodel по подписке будет получать обновления этой коллекции и разбирать на отдельные блоки для показа.
Наверное я ответил на свой вопрос) Спасибо Вам за ответ.
0
2861 / 1980 / 368
Регистрация: 14.08.2018
Сообщений: 6,412
Записей в блоге: 4
13.12.2021, 13:59 4
Цитата Сообщение от Xristian Посмотреть сообщение
или что команда не может быть выполнена по таким то причинам?
Это можно задать через параметр RelayCommand - CanExecute(object obj), если объект не удовлетворяет условию, то команда не выполнится и UI-элемент, который к ней привязан, не будет активен.
0
1181 / 623 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 14:05 5
Цитата Сообщение от Xristian Посмотреть сообщение
Я бы хотел сделать это, придерживаясь принципа mvvm
В таком случае, на уровне архитектуры это делается следующим образом: в Моделе у Вас вызывается ошибка -> в ViewModel -- обрабатывается -> в View -- отображается.

Не советую использовать "void", так как ошибка может просто кануть в лету. Но сейчас это не суть важно.

Пример:
C#
1
2
3
4
5
6
7
public class SomeModel
{
    public void Foo()
    {
        throw new ArgumentExcaption("Somethig whent wrong.");
    }
}
Далее в ViewModel мы её обрабатываем:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SomeViewModel
{
    private readonly SomeModel model = new SomeModel();
    private RelayCommand _doSomethingCommand;
    public RelayCommand DoSomethingCommand => _doSomethingCommand
            ?? (_doSomethingCommand = new RelayCommand(OnDoSomething));
 
    public void OnDoSomething(object parameter)
    {
        // тут мы отхватываем ошибку и тут мы её обрабатываем. Для изменения состояния View. 
        // на этом уровне Вы имеете право, например, открыть диалоговое окно с ошибкой
        model.Foo();
    }
}
Почему ошибка обрабатывается на уровне ViewModel, а не на уровне модели?
Допустим я изменю метод модели следующим образом:
C#
1
2
3
4
5
6
7
8
9
10
11
    public void Foo()
    {
        try
        {
            throw new ArgumentExcaption("Somethig whent wrong.");
        }
        catch(Excaption ex)
        {
            MessageBox.Show("Ops :(", "Something whent wrong.");
        } 
    }
Как видите, я в моделе уже делаю какие-то обработки. Но в чём же проблема? Допустим я теперь захочу использовать эту модель уже не в WPF/UWP/WinForms проекте, а в консольном. Но в консольном проекте я не смогу вывести MessageBox.Show.

Поэтому с Model ошибка поднимается в ViewModel и обрабатывается уже там. Это что касаемо "принципа mvvm".
1
1286 / 865 / 258
Регистрация: 08.08.2014
Сообщений: 2,468
13.12.2021, 14:44 6
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Это можно задать через параметр RelayCommand - CanExecute(object obj), если объект не удовлетворяет условию, то команда не выполнится и UI-элемент, который к ней привязан, не будет активен.
CanExecute для этих целей не особо подходит. Наиболее типовой сценарий его использования - заблокировать кнопку на время выполнения асинхронной команды. Во всех же прочих случаях, засеренная кнопка вызывает у пользователей вопросы и куда разумнее позволить её нажать даже в заведомо невалидном состоянии VM и потом сказать пользователю в чём он неправ (не заполнил что-то, или заполнил неправильно, или фаза луны не та).

Это уже не говоря о том, что основная валидация выполняется на стороне модели, а модель может быть далеко в интернете, и для валидации ей, вероятно, нужно передавать кучку данных, а CanExecute может спамиться непредсказуемо часто.
0
2861 / 1980 / 368
Регистрация: 14.08.2018
Сообщений: 6,412
Записей в блоге: 4
13.12.2021, 14:56 7
Цитата Сообщение от kotelok Посмотреть сообщение
что основная валидация выполняется на стороне модели
А почему бы не передавать в модель уже чистые данные, готовые для использования? VM проведёт всю обработку, отдаст готовые данные в модель, а та уже сама определит их куда надо (в БД так в БД, в файл так в файл)...
0
1286 / 865 / 258
Регистрация: 08.08.2014
Сообщений: 2,468
13.12.2021, 15:03 8
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
А почему бы не передавать в модель уже чистые данные, готовые для использования? VM проведёт всю обработку, отдаст готовые данные в модель, а та уже сама определит их куда надо (в БД так в БД, в файл так в файл)...
Потому что валидация данных - это задача модели. Потому что модель - это, как правило, какой-то внешний API, а на стороне клиента от модели остаётся лишь набор прокси-классов (ну и немного кэширования).

И API (модель) ну никак не может доверять валидации, которая [не] была выполнена на стороне UI.

Т.е. часть валидации да, можно продублировать и на стороне UI, просто для оптимизации, но модель при этом всё равно должна на своей стороне обеспечить полную валидацию.

Добавлено через 1 минуту
Но даже если клиент толстый и модель находится на его стороне, то с точки зрения переиспользования её логики, вся валидация должна находиться именно в модели. Просто чтобы не дублировать эту логику в каждой VM, которая решит с этой моделью поработать.
1
1181 / 623 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 15:16 9
Лучший ответ Сообщение было отмечено Xristian как решение

Решение

Накидал пример. Очень грубый, но для базового понимания:

Главня VM содержит список ошибок и подписку для отображение ошибок на View.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public class MainViewModel : OnNotifyPropertyChanged
    {
        public ObservableCollection<WarningViewModel> Warnings { get; } = new ObservableCollection<WarningViewModel>()
        {
            new WarningViewModel{ Message = "Warning 1" },
            new WarningViewModel{ Message = "Warning 2" },
        };
       
        public MainViewModel()
        {
            WarningHadler.WarningsChanged += OnWarningsChanged;
        }
 
        private void OnWarningsChanged(object sender, string message)
        {
            Warnings.Add(new WarningViewModel() { Message = message });
        }
    }
Тут я не парился и просто сделал статическое событие. В продакшен приложенияих для этого реализуют целые произведения искусства (но не в этом).
C#
1
2
3
4
5
6
7
8
9
    public delegate void WarningsActionHandler(object sender, string message);
    public class WarningHadler
    {
        public static event WarningsActionHandler WarningsChanged;
        public static void RaiseWarningEvent(object sender, string message)
        {
            WarningsChanged?.Invoke(sender, message);
        }
    }
Тут просто какой-то контент. Будь то странциа авторизации или чего угодно.
Я не парился я просто взял проект из другой темы.
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
    public class GroupViewModel : OnNotifyPropertyChanged
    {
        private readonly SomeModel model = new SomeModel();
 
        private StudentViewModel _currentStudent;
        public StudentViewModel CurrentStudent { get => _currentStudent; set => SetProperty(ref _currentStudent, value); }
 
        public ObservableCollection<StudentViewModel> Students { get; } = new ObservableCollection<StudentViewModel>()
        {
            new StudentViewModel { Name = "Иван" },
            new StudentViewModel { Name = "Андрей" },
            new StudentViewModel { Name = "Виктор" },
        };
 
 
        private RelayCommand _doSomethingCommand;
        public RelayCommand DoSomethingCommand => _doSomethingCommand ?? (_doSomethingCommand = new RelayCommand(OnDoSomething));
 
        public void OnDoSomething(object parameter)
        {
            // тут мы отхватываем ошибку и тут мы её обрабатываем. Для изменения состояния View. 
 
            try 
            {
                model.Foo();
            }
            catch(Exception ex)
            {
                WarningHadler.RaiseWarningEvent(this, "Some error");
            }
          
        }
 
        public GroupViewModel()
        {
            CurrentStudent = Students[0];
        }
    }
MVVM ответ на действие пользователя


Тут, понятное дело, не хватает анимаций, кучи обработок, навигации и т.д, но это не касается темы.
Например я задал DataContext напрямую из xaml.cs файла -- это не совсем корректно, но это отдельная, ёмкая тема.
WpfApp1.7z
2
1181 / 623 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
13.12.2021, 15:31 10
Ну и не будет лишним показать, как я обрабатываю ошибки в Xamarin-приложении. Стараюсь обрабатывать следующие ситуации:
- Есть ли у меня интернет?
- Бежит ли сервис на сервере?
- Случилась ли какая-то беда?

Покажу на примере уже выше изложенного кода, но в этот раз вызову не Foo, а Task, который возвращает студента.
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
    public class RequestHandler : IRequestHandler
    {
        // тут куча всякого кода
 
        public async Task<T> TaskHandlerAsync<T>(Task<T> task)
        {
            if (!_connectivityService.HasInternetConnection)
            {
                await ShowMessage(Translations.Translations.Instance.Translate("txtNoInternetConnection"));
                return default(T);
            }
            else if (!_connectivityService.IsServerAlive)
            {
                await ShowMessage(Translations.Translations.Instance.Translate("txtUnreachableServer"));
                return;
            }
 
            try
            {
                return await task;
            }
            catch (ValidationException validationException)
            {
                var translatedError = ErrorTranslator.Instance.TranslateError(validationException.Error);
                await ShowMessage(translatedError);
 
                Logger.Error($"[{GetCallingClassName()}][{GetCallingMethodName()}] {translatedError}");
            }
            catch (Exception exception)
            {
                await ShowMessage(Translations.Translations.Instance.Translate("txtSomethingWentWrong"));
                Logger.Error($"[{GetCallingClassName()}][{GetCallingMethodName()}] {exception.Message}");
                
            }
 
            return default(T);
        }
    }
Далее я регистрирую с помощью DependencyInjection
C#
1
DependencyService.Register<IRequestHandler, RequestHandler>();
Ну и в VM использую следующим образом:
C#
1
2
3
4
5
6
7
8
9
10
        public IRequestHandler _requestHandler => DependencyService.Get<IRequestHandler>();
 
        public void OnDoSomething(object parameter)
        {
             var student = _requestHandler.TaskHandlerAsync(model.GetStudentById(0));          
            if (student!= null)
            {
                //.. code
            }
        }
2
Модератор
Эксперт .NET
15827 / 10977 / 2850
Регистрация: 21.04.2018
Сообщений: 32,224
Записей в блоге: 2
13.12.2021, 15:36 11
Цитата Сообщение от Xristian Посмотреть сообщение
Как реализовать ответ на запрос пользователя в паттерне MVVM? У меня на форме есть кнопка на которой висит команда, описанная в ViewModel, эта команда вызывает метод модели. Как мне уведомить пользователя о том что все успешно выполнено, или что команда не может быть выполнена по таким то причинам? C# WPF
Примерный алгоритм:
1) Первый уровень: простейшая валидация данных в методе Execute команды. Если данные не валидны, то кнопка отключается. Но здесь можно сделать только простейшую валидацию, так как она вызывается очень часто в UI потоке;
2) Второй уровень: Вызов метода модели возвращающего данные о валидации. Эти данные обрабатываются в методе Execute команды и при необходимости устанавливается флаг ошибки (или другое свойство) или вызываете делегат диалога уведомления об ошибке. В этом уровне может быть уже более серьёзная обработка, но ё равно не очень длительная так как Execute тоже выполняется в UI потоке. И даже если применить асинхронность, то подразумевается достаточная быстрая реакция после клика пользователя. Не микросекунду, но и не секунды. Максимум десятые доли секунды;
3) Третий уровень: после первичной валидации Модель вызывает свой асинхронный метод. В этом методе тоже могут ошибки, исключения. И для уведомления о них нужно предусмотреть отдельный канал в Модели. Это может быть событие, свойство, метод обратного вызова, какой-то сервис и т.д. VM получает данные из этого канала и извещает об этом View, втом числе путём вызова делегата диалога для таких сообщений.
0
13.12.2021, 15:36
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
13.12.2021, 15:36
Помогаю со студенческими работами здесь

Сравнить ответ пользователя с верным
#include &lt;iostream.h&gt; #include &lt;stdio.h&gt; #include &lt;windows.h&gt; #include &lt;conio.h&gt; using...

Сравнить правильный ответ с ответом пользователя
Моя задача, составить - тест. Изначально вопросы и ответы хранятся в .ini файле. Максимальный...

Привязать ответ пользователя к переменной aiogram
Здравствуйте, опишу сразу ситуацию Я хочу сделать так, чтобы бот писал - Введите код продукта ...

Как получить ответ пользователя в переменную
Приветствую! Пишу телеграм бота, подскажите следующий момент. Пользователь вводит команду, бот...

Проверить ответ пользователя - вычисление выражения
Программа спрашивает у пользователя чему равно выражение 17+3 и не выходит пока пользователь не...

Как сохранить ответ от пользователя Телеграмм бота
Здравствуйте, создал команду в @BotFather, при вызове этой команды в телеграмм боте, бот отвечает...

ForceReplay как записать ответ пользователя в переменную?
Здравствуйте, заранее прошу прощения, если задал тупой вопрос или на него уже есть ответ на форуме....


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

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru