Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515

Где лучше в MVVM реализовать дополнительную логику?

25.09.2025, 19:46. Показов 7037. Ответов 118
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
 Комментарий модератора 
Тема создана разделением темы Привязка DP-свойства UserControl к вложенному элементу


Цитата Сообщение от Элд Хасп Посмотреть сообщение
проблема безопасности, когда свойство Result может быть перезадано по месту использования DPCalculatorUC, осталось.
так оно (заданный Result) ведь всегда может быть перезадано? или я ошибаюсь... переназначить свойство - это же не удалить/создать объект заново...

мне почему-то кажется, что это не проблема, а как раз и задумано было, чтобы можно было переназначать свойства в зависимости от нужного (например, цвет текста при value>1000, условно говоря) - вот и вынесли это DP в отдельный класс... а за пример реализации и использования в XAML - спасибо!..

? только в каких случаях это DP подключают обычно?.. как по мне, так Styles и Templates и даже Converters - нормальные внутренние (для объекта) свойства, дальше которых уже и придумать нечего (с практической точки зрения) чтобы захотеть подключать из-вне класса ещё и DP за гранью всего, что можно задать и в объекте...
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
25.09.2025, 19:46
Ответы с готовыми решениями:

Где располагать логику приложения, когда используем паттерн mvvm?
Изучаю wpf и паттерн mvvm. Никогда не задумывался, где что располагать, я считал (считаю пока),...

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

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

118
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
25.09.2025, 20:40
Цитата Сообщение от JeyCi Посмотреть сообщение
только в каких случаях это DP подключают обычно?..
Когда нужна какая-то дополнительная логика. DP-свойство - это тоже часть логики.

Цитата Сообщение от JeyCi Посмотреть сообщение
как по мне, так Styles и Templates и даже Converters - нормальные внутренние (для объекта) свойства
Но в них не возможно задать логику.

Например, здесь UserControl по типу NumericUpDown

Реализовано дополнительное свойство Value2, которое позволяет задать "обратную" привязку от Value к источнику. Это нужно в некоторых случаях, когда прямая привязка из-за валидации диапазона будет давать отличное от имеющегося в Value значение.

Добавлено через 4 минуты
Цитата Сообщение от JeyCi Посмотреть сообщение
только в каких случаях это DP подключают обычно?..
Когда нужна какая-то дополнительная логика. DP-свойство - это тоже часть логики.

Цитата Сообщение от JeyCi Посмотреть сообщение
как по мне, так Styles и Templates и даже Converters - нормальные внутренние (для объекта) свойства
Но в них не возможно задать логику.

Например, здесь UserControl по типу NumericUpDown

Реализовано дополнительное свойство Value2, которое позволяет задать "обратную" привязку от Value к источнику. Это нужно в некоторых случаях, когда прямая привязка из-за валидации диапазона будет давать отличное от имеющегося в Value значение.

Цитата Сообщение от JeyCi Посмотреть сообщение
так оно (заданный Result) ведь всегда может быть перезадано? или я ошибаюсь... переназначить свойство - это же не удалить/создать объект заново...
В этом и проблема.
По идее (в начале темы), оно предназначалось для внутренней привязки, чтобы во "вне" могли видеть значение какого-то внутреннего элемента.
Но внешние потребители могут не только просмотреть значение этого свойства, на и задать ему значение (в том числе новой привязкой). А это разрушит внутреннею привязку. То есть перестанет работать задуманная логика.
0
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
26.09.2025, 12:38  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
позволяет задать "обратную" привязку от Value к источнику.
кстати да, validateValueCallback есть у DependencyProperty... т.е. валидацию можно делать не на уровне Модели, а на уровне View... т.к. не все контролы поддерживают Mode=TwoWay
, видимо...

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Когда нужна какая-то дополнительная логика.
но ведь мы можем её задать и в getter/setter of CLR properties так же, как и в GetValue/SetValue of DP
Placing additional logic in the CLR wrappers other than GetValue() and SetValue() should be avoided
да и просто через ICommands (System.Windows.Input) или Behaviors (System.Windows.Interactity) реализовать доп. бизнес логику можно... Есть ли какие-нибудь советы когда что лучше выбрать?

или по причине потокобезопасности какие-то из этих реализаций могут быть небезопасны?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
27.09.2025, 00:02
Цитата Сообщение от JeyCi Посмотреть сообщение
не все контролы поддерживают Mode=TwoWay, видимо...
Не в этом дело.
Mode=TwoWay не "спасает" при возращении PropertyMetadata.CoerceValueCallback или анимацией другого значения.
Прочитайте за приоритеты присвоения значения DP-свойству.

Цитата Сообщение от JeyCi Посмотреть сообщение
но ведь мы можем её задать и в getter/setter of CLR properties так же, как и в GetValue/SetValue of DP
getter/setter of CLR не может содержать никакой логики кроме вызовов GetValue/SetValue of DP.

Цитата Сообщение от JeyCi Посмотреть сообщение
Есть ли какие-нибудь советы когда что лучше выбрать?
Есть песок, известняк, глина, лес, камни, уголь, железная руда и т.п. Есть рекомендации что лучше выбрать для дома?
Думаю, вы понимаете, что рекомендации для землянки, сруба, трёх-этажки, высотки будут разные.


Цитата Сообщение от JeyCi Посмотреть сообщение
или по причине потокобезопасности какие-то из этих реализаций могут быть небезопасны?
Большинство фрамеворков GUI (Формы, WPF, UWP, WinUI и др.) поддерживают потокобезопасность через запрет работы в любом потоке кроме потока созхдавшего GUI и его элементы.
Но в общем случае потокобезопасность это очень обширный вопрос и решается множеством различных способов.
Наиболее часто встречающиеся: lock, ReaderWriterLock, конкурентные коллекции, атомарные операции.

К WPF это имеет отношение только к безопасности обновления коллекций и состояния команд.
Безопасност обновления коллекций решается через BindingOperations.EnableCollectionSynchronization.
Безопасность обновления команды решается через маршалинг ICommand.CanExecuteChanged в UI поток.

Добавлено через 27 минут
JeyCi, в общем случае зависит от архитектуры решения (а не только GUI).

Абстрактный пример.

На Марсе есть некая техника (Модель).
Часть этой техники отвечает за получение и отправку сообщений - API или интерфейс Модели.

На Земле есть приёмопередающая станция - комплекс антенн, питания, преобразователей, которые отвечают за предоставление возможности общения с Марсом для Центра Управления.
Это - Модель Представления (ViewModel).

Есть собственно Центр Управления - это View.


На Марсе есть термометр. Необходимо при достижении каких-то пределов, срочно произвести консервирующие действия.


1-вариант.
Так как время реакции на действие низкое, то мы заводим должность "кликер обновления". Человек занимающий эту должность, кликает 1 раз в секунду на кнопку отправляющую запрос на Марс для получения данных о температуре.

2-вароиант.
Мы модернизируем компьютеры ЦУ , встраивая в них ПО "кликера". То есть мы добавляем "логику в View" и это абсолютно нормально.

3-вариант.
Мы добавляем в принимающий комплекс автоматику, которая 10 раз в секунду запрашивает температуру и только при её изменении посылает сообщение в ЦУ - это "Логика ViewModel" и она тоже абсолютна верна.

4-вариант.
Мы думаем: "А нафига мы постоянно перегружаем канал связи, который и так чрезвычайно узкий, лишними сообщениями?".
Мы добавляем на Марсе автоматику, котрая сама отправляет сообщение при изменении температуры на 1 градус.
Тое абсолютно "легальное" решение.

5-вариант.
Мы думаем: "Пока придёт сообщение на Землю, оператор отреагирует на него, отправит команду консервации, она будет получена на Марсе - это минимум займёт час. Да, за это время вся аппаратура сгорит!".
И мы встраиваем всю автоматику на Марсе, а на Землю отправляем только логи.


Все пять вариантов правильные, но зависят от возможностей их реализовать, от ТЗ.
Так же и WPF.
Сказать в общем случае, где правильно реализовать ту или иную логику - не возможно.

Можно ли реализовать контроль диапазона в Модели - конечно, можно. И это правильно будет в одних условиях (в целом это наиболее часто встречающаяся задача).
Можно ли реализовать контроль диапазона в Представлении - конечно, можно. Но так как чаще всего обеспечение безопасности лежит на "Марсе" (т.е. Модели), то это очень редко это бывает целесообразно.
Замена Логики на Марсе - это трудно реализуемая и очень дорогая задача, и на такое в истории Человечества пока ни разу не шли.
В противовес этому, апгрейт Model это чаще всего сравнительно простая задача. Поэтому при изменении ТЗ это наиболее часто встречающийся выбор. Бонусом он получается платнформонезависимым. То есть может работать одна и таже логика в WPF, Формах ил WinUI.
Но изредка всё же встречаются задачи, в которых контроль за "вызовом действий консервации" должен оставаться за GUI или пользователем.
В этом случае логика обратной связи между "изменение температуры" и "команда консервации" реализуется в Представлении.
0
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
27.09.2025, 11:25  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
оператор отреагирует на него, отправит команду консервации
не реагирует у меня пока в NET.Framework 4.5 - свойство Command не существует в пространстве имен XAML, просит xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" - а System.Windows.Interactivity для NET.Framework 4.5 (для 4.0) не подключается пока... посмотрю ещё, как загнать ICommand в обработчик event'a...

p.s. кстати выводится на кнопку x:Name="Btn1" Command="{Binding SelectItemCommand}" CommandParameter="{Binding tb1}" с ViewModel SelectItemCommand = new RelayCommand((param) => ExecuteSelectItemCommand(param)); и его (метода) реализацией...

Цитата Сообщение от Элд Хасп Посмотреть сообщение
изредка всё же встречаются задачи, в которых контроль за "вызовом действий консервации" должен оставаться за GUI или пользователем.
при работе с БД - даже очень часто - Добавить, Удалить, Изменить Record в БД

Цитата Сообщение от Элд Хасп Посмотреть сообщение
при достижении каких-то пределов, срочно произвести консервирующие действия.
да в БД - сразу надо всё консервировать -- меня пока это тз интересует...

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Большинство фрамеворков GUI (Формы, WPF, UWP, WinUI и др.) поддерживают потокобезопасность через запрет работы в любом потоке кроме потока созхдавшего GUI и
я бы ещё добавила необходимость использования потокобезопасных контейнеров и касательно архитектуры - async Task правильно прерывать при длительных операциях и потребности выхода из Task до окончания операции

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Думаю, вы понимаете, что рекомендации для землянки, сруба, трёх-этажки, высотки будут разные.
поэтому и спросила, что для землянки, что для сруба, что для высотки... логику (и её изменение) в DP, ICommand или Behavior... но в принципе согласна, в 1-м случае логику закладываем во View, во втором и в 3-м во ViewModel, вероятно... только Behavior - мне кажется излишним везде использовать для изменений логики - только если анимацию или видеопоток добавлять в Style (но это не логика, это view)

Добавлено через 1 час 4 минуты
?? или про консервацию вы имели ввиду Services в DI ?? мне показалось, что для запуска комманд - достаточно (класса под спойлером)
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Windows.Input;
 
namespace myTest
{
     public class RelayCommand : ICommand
     {
            private readonly Action<object> _executeAction;
            public RelayCommand(Action<object> executeAction)
            {
                _executeAction = executeAction;
            }
            public bool CanExecute(object parameter) { return true; }
            public void Execute(object parameter) { _executeAction(parameter); }
    
            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }
     }
 
}

- хотя чтобы загнать его в бэк-энд и не тревожится о том, что же там внутри, - можно и Service (если уже написан)... но если реализации ещё НЕТ, то как по мне, то архитектурно - без разницы - то ли новый класс в проект, то ли писать реализацию сервиса и подключать его ??
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
27.09.2025, 12:02
Цитата Сообщение от JeyCi Посмотреть сообщение
только Behavior - мне кажется излишним везде использовать для изменений логики
Напротив.
Behavior имеет смысл использовать только для изменения логики (поведения).
Во всех остальных случаях достаточно "Attached Property".

Одна из часто встречающихся задач Behavior для привязки свойств только для чтения [WPF, Элд Хасп]

Behavior - это Freezable во FreezableCollection Behaviors.
Behaviors же - это теневое Attached Property. Поэтому при обращении к нему всегда вызывается логика метода GetBehaviors в котором и происходит инициализация коллекции.
В результате мы получаем в экземпляре Behavior родительский элемент (AssociatedObject) и, самое важное, возможность задать в одном Behavior несколько DP-свойств, которые могут действовать взаимосвязанно для обеспечения единой логики.

Цитата Сообщение от JeyCi Посмотреть сообщение
не реагирует у меня пока в NET.Framework 4.5 - свойство Command не существует в пространстве имен XAML, просит xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" - а System.Windows.Interactivity для NET.Framework 4.5 (для 4.0) не подключается пока... посмотрю ещё, как загнать ICommand в обработчик event'a...
Этого не понял.

Лучше будет если создадите новую тему и там архив или ссылку на Git репозиторий дадите.

Цитата Сообщение от JeyCi Посмотреть сообщение
при работе с БД - даже очень часто - Добавить, Удалить, Изменить Record в БД
Я о другой логике. Той, которую можно автоматизировать.
Эту автоматизацию можно реализовать в любом слое, но почти всегда правильным будет реализовать это в Модели.

В задаче с БД, например, при "Добавить" нужно проверить корректность данных. Не пользователь же сам должен "руками и глазами" проверять все поля и искать такие же по всей БД. Эта корректность проверяется автоматически. Её можно реализовать на любом уровне. Но оптимальным будет её внедрение в БД (чтобы она сама отвечала за свою целостность), в крайнем случае в Репозиторий. А это все относится к Модели.
А задача ViewModel и View - показать ошибку валидации пользователю в случае некорректных данных.

Цитата Сообщение от JeyCi Посмотреть сообщение
да в БД - сразу надо всё консервировать
Скорее всего, это лучше всего реализовать в самой БД.

Цитата Сообщение от JeyCi Посмотреть сообщение
я бы ещё добавила необходимость использования потокобезопасных контейнеров и касательно архитектуры - async Task правильно прерывать при длительных операциях и потребности выхода из Task до окончания операции
Ну, потокобезопасность - это обширная тема.
Таски они как бы к этой теме не относятся. Вернее при их реализации, часто нужно учитывать потокобезопасное обращение с объектами, но каких-то внедрённых средств потокобезопасности в них нет. Разве что условно к этому можно отнести Контекст Синхронизации. Но в этом случае нужно чтобы все потребители объекта работали с ним через общий Контекст Синхронизации. Только так можно обеспечить потокобезопасность работы с этим объектом. Собственно по этому в UI элементах выкидывается исключение при работе с ним не из UI потока (вернее потока в котором они были созданы) - это гарантирует что все потребители будут в одном потоке, в частом случае, в одном контексте синхронизации.
То есть для Тасков контекст синхронизации - это способ безопасной работы с некоторыми объектами, но все же это не создание потокобезопасности.

Цитата Сообщение от JeyCi Посмотреть сообщение
поэтому и спросила, что для землянки, что для сруба, что для высотки... логику (и её изменение) в DP, ICommand или Behavior... но в принципе согласна, в 1-м случае логику закладываем во View, во втором и в 3-м во ViewModel,
Лучше на каких-то конкретных примерах.
Так слишком абстрактно получается.

Добавлено через 7 минут
Цитата Сообщение от JeyCi Посмотреть сообщение
что для запуска комманд - достаточно (класса под спойлером)
Это очень слабая реализация.
Она не позволяет вызвать обновление своего состояния из ViewModel (или других потребителей).
А, например, при асинхронном обновлении данных (состояния Модели) это нужно делать обязательно.

Лучше взять простой пример отсюда: Простые реализации для тем на форуме
Или использовать какой-то фрамеворк для создания ViewModel.

Добавлено через 1 минуту
Цитата Сообщение от JeyCi Посмотреть сообщение
?? или про консервацию вы имели ввиду Services в DI ?
Нет. Имел ввиду любую автоматизация действий реагирующих на изменении состояния Модели - в примере реакция на изменении температуры на Марсе.

Добавлено через 3 минуты
JeyCi, мы уже вышли далеко за рамки этой темы.
Наверное, лучше будет всё перенести в отельную тему.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
27.09.2025, 12:08
JeyCi, дальше пишем в этой теме.
0
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
27.09.2025, 17:40  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Или использовать какой-то фрамеворк для создания ViewModel.
да, это, конечно, самое простое - например CommunityToolkit.Mvvm - тут и Async-команды...
Цитата Сообщение от Элд Хасп Посмотреть сообщение
очень слабая реализация. Она не позволяет вызвать обновление своего состояния из ViewModel (или других потребителей).
обновить состояние Модели? - но её состояние меняет не реализация RelayCommand интерфейса ICommand (там под спойлером), а уже сама конкретная команда, я так понимаю (применяемая в ViewModel)...

p.s.
всё равно она у меня не проходит в классе LoginVM - не подхватывает parameter в MessageBox (выдаёт param, а не его значение) - т.е. если ещё даже не трогать состояние модели
решено {
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
       private User user;
        public ICommand LoginCommand { get; set; }
        
        public LoginVM()
        {
            user = new User();
            LoginCommand = new RelayCommand((param)=>LoggedIn(param));
        }
        public string UserName
        {
            get { return user.UserName; }
            set
            {
                user.UserName = value;
                OnPropertyChanged("UserName");
            }
        }
 
        private void LoggedIn(object parameter)
        {
            //var element = parameter as User;
            //MessageBox.Show(@"Logged in successfull as {parameter?.UserName}");
            MessageBox.Show(@"Logged in successfull as {parameter}");
            
        }
- это потому что то, что вы назвали "слабой реализацией" - надо как-то улучшить?? - (я про RelayCommand реализацию под спойлером предыдущего поста)..

p.s. LoginVM наследую от
C#
1
2
3
4
5
6
7
8
    public class ViewModelBase : INotifyPropertyChanged
    {
         public event PropertyChangedEventHandler PropertyChanged;
         public void OnPropertyChanged(string property) {
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
         }
             
    }
в XAML на button Command="{Binding LoginCommand}"
CommandParameter="{Binding UserName}"


CORRECTED MessageBox.Show(@"Logged in successfull as " + parameter);


- ok, теперь состояние Модели надо пробовать менять по RelayCommand : ICommand?.. так будет ли AsyncRelayCommand - реализация получше ?? или менять Модель из View (которая кстати о ней не знает) правильнее как-то по-другому? (тут явно дело не в синхронно/асинхронно).. всё-таки SQLCommand послать из VM и будь, что будет - мне кажется правильнее, бд пусть сама разбирается - лучше, как в VBA с ADODB - pCon.BeginTrans & pCon.CommitTrans для Insert, Update... иначе Rollback (сам делается)

или я не очень поняла, что там слабого в той реализации?? (для изменения состояния Модели)
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
27.09.2025, 21:29
Цитата Сообщение от JeyCi Посмотреть сообщение
обновить состояние Модели? - но её состояние меняет не реализация RelayCommand интерфейса ICommand (там под спойлером), а уже сама конкретная команда, я так понимаю (применяемая в ViewModel)...
Не всегда.

Например, удалённая БД.
Поддерживает выполенение одновременно только одной операции.
В Модели есть состояние bool isBusy.
Сценарий работы с Моделью такой:
1) Вызывается синхронный метод;
2) Метод сразу возвращает значение валидации данных;
3) Так же метод начинает выполнять асинхронный таск изменяющий БД;
4) В начале метода isBusy=true;
5) Таск перед завершением сбрасывает (естественно асинхронно) сбрасывает isBusy=false;
6) Все команды в VM имеют зависимость CanExecute от isBusy.

В таком сценарии все кнопки, после клика по любой, становятся Disabled.
После завершения выполнения таска, когда isBusy станет опять false, кнопки надо перевалидировать. Но автоматически это не произойдёт. Перевалидация кнопок, в вашей простой реализации, зависит только от CommandManager.RequerySuggested. А это событие возникает только при существенных действиях в Окне (или Окнах). Так как isBusy - это состояние Модели, то CommandManager.RequerySuggested на него не реагирует, и перевалидация кнопок не вызывается.

Решается это прослушкой событий Модели и поднятием CanExecuteChanged для зависимых команд. Для этого в нормальных реализациях команд, предусматривается специальный метод. В моих простых реализациях это public void RaiseCanExecuteChanged(). В других реализациях - метод может именоваться по другому.

Цитата Сообщение от JeyCi Посмотреть сообщение
всё равно она у меня не проходит в классе LoginVM - не подхватывает parameter в MessageBox (выдаёт param, а не его значение) - т.е. если ещё даже не трогать состояние модели
Ну, ошибка очень простая. И не имеет отношения к WPF. Вы перепутали символы @ и $.
Правильно:
C#
23
           MessageBox.Show($"Logged in successfull as {parameter}");
Подробный текст — @ в переменных, атрибутах и строковых литералах
Интерполяция строк в C#.

Но архитектурно, в ViewModel не может быть MessageBox.
Вообще, не может быть зависимости от сборки PresentationFramework.
Реализация команд (ваша и моя простая) тоже нарушает это правило.
Поэтому в правильных фрамеворках используются другие реализации команд.

Цитата Сообщение от JeyCi Посмотреть сообщение
всё-таки SQLCommand послать из VM и будь, что будет - мне кажется правильнее, бд пусть сама разбирается - лучше
Да. Так верно.

Цитата Сообщение от JeyCi Посмотреть сообщение
или я не очень поняла, что там слабого в той реализации??
Подытожу:
1) Нет возможности поднять CanExecuteChanged для экземпляра команды;
2) Зависимость от CommandManager, что делает такую реализацию команд "платформозависимой", что нарушает принципы MVVM.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
28.09.2025, 02:13
JeyCi, развитая реализация команды с маршалингом CanExecuteChanged в контекст синхронизации слушателей: public class RelayCommandSyncContext : ICommand
0
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
28.09.2025, 10:45  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Вы перепутали символы @ и $.
у меня ни с тем, ни с тем не проходило... но не в этом суть...
Цитата Сообщение от Элд Хасп Посмотреть сообщение
После завершения выполнения таска, когда isBusy станет опять false, кнопки надо перевалидировать. Но автоматически это не произойдёт. Перевалидация кнопок, в вашей простой реализации, зависит только от CommandManager.RequerySuggested. А это событие возникает только при существенных действиях в Окне (или Окнах). Так как isBusy - это состояние Модели, то CommandManager.RequerySuggested на него не реагирует, и перевалидация кнопок не вызывается.
ну, понятно, что прослушку окончания действий на сервере надо организовать, / как и/или прослушку возврата из таска (что в принципе и сама библиотека из System может сделать - делает в await - или лучше Service подключать, чем System??) -- хотя да, для платформонезависимости лучше Service (?), так понимаю (чтобы не завязывать всё на конкретном фреймворке)
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Подытожу:
...
2) Зависимость от CommandManager, что делает такую реализацию команд "платформозависимой", что нарушает принципы MVVM.
это потому что он из System, например Win10 у меня ?
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
28.09.2025, 11:33
Цитата Сообщение от JeyCi Посмотреть сообщение
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Подытожу:
...
2) Зависимость от CommandManager, что делает такую реализацию команд "платформозависимой", что нарушает принципы MVVM.
это потому что он из System, например Win10 у меня ?
Нет.
namespace - это просто идентификаторы для полного имени класса. Чтобы разрешать коллизии имён.
Имеет значение сборка. Сборки: PresentationFramework, PresentationCore, WindowsBase - это View сборки. Зависимости от них на слоях ViewModel и Модели не должно быть. Это в идеале, конечно. На практике, в простых реализациях, особенно на уровне ViewModel - они бывают. Но всегда нужно понимать, что это нарушение паттерна и предвидеть к каким потенциальным проблемам это может привести.

Цитата Сообщение от JeyCi Посмотреть сообщение
ну, понятно, что прослушку окончания действий на сервере надо организовать, / как и/или прослушку возврата из таска
Суть примера была не в прослушке.
А в том что асинхронное изменение состояния команды не приводит к автоматической перевалидации кнопки, её использующей.
Для перевалидации нужно явно подымать CanExecuteChanged в зависимых командах.

А пример просто показывает в каком случае модель может асинхронно изменить своё состояние.
И на практике это очень часто происходит.

Цитата Сообщение от JeyCi Посмотреть сообщение
хотя да, для платформонезависимости лучше Service (?),
По разному.
Здесь нужно ориентироваться на практику конкретного коллектива.
Например, в Avalonia два типовых способа реализации ViewModel: CommunityToolkit и ReactiveUI. Если вы входите в команду, которая использует ReactiveUI, будет крайне не логичным самому использовать CommunityToolkit.
Так же это касается и всех остальных фреймворков.

Не по теме:

Что касается непосредственно Avalonia, то проект типа "Avalonia MVVM", создаваемый Студией - это антипример. То есть, это просто называется MVVM, но по факту нарушает базовые принципы MVVM. Его даже "Кривым MVVM" назвать нельзя. Это просто "нечто названное MVVM по прихоти разработчиков".

0
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
28.09.2025, 16:30  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
А в том что асинхронное изменение состояния команды не приводит к автоматической перевалидации кнопки, её использующей.
ну, конечно, эти async'и из модели во view не запрыгнут... - а они могут быть вызваны из AsyncRelayCommand (так понимаю)... может ли
<cmd:EventToCommand Command="{Binding SomeCommand, Mode=OneWay}" PassEventArgsToCommand="True"/> - сигнализировать об окончании действия на receiver'e команды, отправленной из VM, чтобы sender потом среагировал, как вы сказали, перевалидацией элемента view... пример реализации RelayCommand<T> ? что должно быть в <T>?..

или я не права и мы всё-таки для этих целей (получить смену property=IsBusy и по этому event'у - сделать перевалидацию) - снова возвращаемся к предпочтительности использования DP? (если это возможно)

сама ещё не делала этого

Добавлено через 13 минут
да и в любом случае, оборачивая connection в try-catch - мы будем вынуждены пробрасывать из catch Модели сразу во View - System.Windows.MessageBox.Show(Ex.Message); или использовать предложенный вами "кривой" "Avalonia MVVM" (или его подобие)... или есть пример кода не-кривого?

Добавлено через 7 минут
и остаётся вопрос - почему нельзя обойтись одними PropertyTriggers ?

Добавлено через 1 час 4 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
развитая реализация команды с маршалингом CanExecuteChanged в контекст синхронизации слушателей: public class RelayCommandSyncContext : ICommand
using System.Windows.Input; - действительно, тоже завязана на Windows dll

Добавлено через 29 минут
Цитата Сообщение от Элд Хасп Посмотреть сообщение
1) Вызывается синхронный метод;
и по-моему (не уверена) - уже все Main можно делать async await - не совсем понимаю, зачем вызывать синхронный метод - мы всегда await какого-то возвращения из какого-то task'a... да и вызвать синхонный из асинхронного - как-то проблематично (невозможно!), насколько помню... или асинхронный из синхронного - ваш сценарий - то в синхронном кнопки не станут Disabled сами по себе пока ждут await... можно же с таском послать bool StopTask - и отлавливать его кнопкой Resume... в принципе RelayCommand<T> Class - это и делает, посылая в таск параметр (тот же bool)... -- и как бы да - прерывание команды запущенной из VM мы автоматически делаем из View - нажимая кнопку Resume (по которой изменяется bool и дальше всё автоматически - сворачивается task до окончания -- там только garbage collection не понятно как сработает)...

Добавлено через 6 минут
и я пока не сделала правильный Binding этого RelayCommand<T> ...

Добавлено через 30 минут
в общем, понятно, надо, наверно, как-то в RelayCommand<T> посланном из VM подхватить сам объект View (в принципе VM и так находится в DataContext от View) и там по месту действия команды сменить этот bool, который binding'ом изменит нужное во View - про async ...

Итого: Binding (даже на команды) - в принципе звучит получше, чем ссылаться (referencing) на View из VM или Model
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
28.09.2025, 22:06
Цитата Сообщение от JeyCi Посмотреть сообщение
using System.Windows.Input; - действительно, тоже завязана на Windows dll
С чего вы взяли?
Это просто namespace. Он может быть в любой сборке.
В том числе и вашей личной.
Этот интерфейс из Standard 2.0


Цитата Сообщение от JeyCi Посмотреть сообщение
ну, конечно, эти async'и из модели во view не запрыгнут... - а они могут быть вызваны из AsyncRelayCommand (так понимаю)... может ли
Асинхронная команда - это всего лишь асинхронное выполнение какого-то метода.
Будет ли в его процессе изменено состояние модели.
Это совершенно не связанные вещи.

Раз вас так запутал вызовов метода модели, то давайте представим другой пример.
Какой-то торговый бот работающий с биржей онлайн.
Модель постоянно получает обновление от биржи - по нескольку сот раз в секунду.
В View есть кнопка для отработки определённого сигнала.
Пока сигнала нет - кнопка Disabled.
Так как обновление модели с биржи асинхронно (но команда в кнопке синхронная!), то после получения сигнала необходимо поднять CanExecuteChanged в команде, чтобы кнопка перевалидировалась и перешла в состояние Enabled.

Цитата Сообщение от JeyCi Посмотреть сообщение
да и в любом случае, оборачивая connection в try-catch - мы будем вынуждены пробрасывать из catch Модели сразу во View - System.Windows.MessageBox.Show(Ex.Messag e); или использовать предложенный вами "кривой" "Avalonia MVVM" (или его подобие)... или есть пример кода не-кривого?
Ничего модель пробрасывать не должна.
И try-catch скорее всего в ней будет избыточным.
Обычно try-catch находится в команде ViewModel и оборачивает вызов метода модели.
Сообщение в View об исключении может быть реализовано по разному. Первое что пришло в голову:
- Диалоговый сервис;
- Поднятие события;
- VM обертка над connection и в нём свойство для приёма исключения.

Вот моя реализация асинхронной команды, которая показывает исключение, если оно было при выполнении Execute: An example of my implementation of base classes (.Net 8): BaseInpc, RelayCommand, RelayCommandAsync, RelayCommand<T>, RelayCommandAsync<T>.

Цитата Сообщение от JeyCi Посмотреть сообщение
насколько помню... или асинхронный из синхронного - ваш сценарий - то в синхронном кнопки не станут Disabled сами по себе пока ждут await...
Причём здесь состояние кнопок и выполнение метода модели? Без разницы синхронный или асинхронный.
Важно не то как метод выполняется, а что состояние модели изменяется асинхронно или синхронно, но не зависимо от действий пользователя в GUI. А раз нет действий в GUI, то не подымается CommandManager.RequerySuggestedт и кнопки "саи по себе" не обновятся. Нужно поднятие CanExecuteChanged в командах для обновления кнопок.

Цитата Сообщение от JeyCi Посмотреть сообщение
надо, наверно, как-то в RelayCommand<T> посланном из VM подхватить сам объект View
Точно не надо.
ViewModel может реагировать на состояние Модели, но ни как не View.

Цитата Сообщение от JeyCi Посмотреть сообщение
там по месту действия команды сменить этот bool, который binding'ом изменит нужное во View
Но это никак не повлияет на состояние кнопок.
Для перевалидации их состояния нужно поднятие CanExecuteChanged.

Цитата Сообщение от JeyCi Посмотреть сообщение
Итого: Binding (даже на команды) - в принципе звучит получше, чем ссылаться (referencing) на View из VM или Model
Модель не может ссылаться ни на кого.
ViewModel может ссылаться на Model, но не может ссылаться на View.
А в целом концепции MV* паттернов (в том числе MVVM) лучше соответствует слабая связь слоёв реализованная через интерфейсы.
Вот схема связей для MVVM:
Структура связей в WPF Решении
0
28.09.2025, 23:17

Не по теме:

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Структура связей в WPF Решении
Ух, надеюсь юные подаваны не будут бездумно делать одну общую либу интерфейсов для всего...

0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
28.09.2025, 23:41
Цитата Сообщение от Wolfdp Посмотреть сообщение
Ух, надеюсь юные подаваны не будут бездумно делать одну общую либу интерфейсов для всего...
Это же не сборка, а слой.
Он может состоять из множества сборок. Для каждой из которых будут свои зависимости.
0
29.09.2025, 03:11

Не по теме:

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это же не сборка, а слой.
Он может состоять из множества сборок. Для каждой из которых будут свои зависимости.
ИМХО, из схемы это не очень очевидно. Тем более у вас там написано "общая бибилотекА."

0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
29.09.2025, 08:44
Wolfdp, эта схема из другой темы. Я дал ссылку на неё. Детали там. Дублировать сюда их не стал.

Добавлено через 1 час 30 минут
Wolfdp, вот схема Структуры Решения с GUI на WPF.
В нём уже расписано по-проектно какой минимум должен быть в Решении:
Структура WPF решения


Добавлено через 4 минуты
Но в реале Модель часто использует сущности (обычно, условно иммутабельные DTO), которые доступны не только в VM, но и в View.
Возможно, ещё могут потребоваться типы общего употребления, то есть те о которых известно всем слоям.
Такие типы следует помещать в отдельную сборку (проект) связь с которой может быть у любого проекта Решения.
0
 Аватар для Andrey-MSK
3308 / 2196 / 386
Регистрация: 14.08.2018
Сообщений: 7,387
Записей в блоге: 4
29.09.2025, 08:54
JeyCi, Вот так организуется команда из пакета CommunityToolkit
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
private string _workName;
// Свойство для привязки в UI
public string WorkName
{
    get => _workName;
    set
    {
        // Если значение свойства изменилось
        if (Set(ref _workName, value))
        {
            // Обновление состояния CanExecute
            SaveWorkCommand.NotifyCanExecuteChanged();
        }
    }
}
 
private AsyncRelayCommand _saveWorkCommand;
// Асинхронный метод команды
// Пока метод не выполнится, кнопка не включится
private async Task SaveWorkExecuted()
    => await SaveWorkAsync();
// Проверка доступности команды
private bool SaveWorkCanExecute()
    => PosNumberWork != null
       && !string.IsNullOrEmpty(WorkName)
       && SelectedMeasure != null
       && Labor != null
       && ErectCoeff != null
       && SelectedGroup != null;
// Команда
public AsyncRelayCommand SaveWorkCommand
    => _saveWorkCommand ??= new AsyncRelayCommand(SaveWorkExecuted, SaveWorkCanExecute);
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16113 / 11234 / 2887
Регистрация: 21.04.2018
Сообщений: 33,035
Записей в блоге: 2
29.09.2025, 09:10
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
JeyCi, Вот так организуется команда из пакета CommunityToolkit
Ну, как бы смысл CommunityToolkit в том чтобы не создавать явно команд и свойств.

Пишу здесь в редаторе - могут быть ошибки:
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
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
 
public partial class MyViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private string _workName;
 
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private string? _posNumberWork;
 
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private object? _selectedMeasure;
 
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private object? _labor;
 
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private object? _erectCoeff;
 
    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SaveWorkCommand))]
    private object? _selectedGroup;
 
    [RelayCommand(CanExecute = nameof(SaveWorkCanExecute))]
    private async Task SaveWork()
    {
        await SaveWorkAsync();
    }
 
    private bool SaveWorkCanExecute()
        => PosNumberWork != null
           && !string.IsNullOrEmpty(WorkName)
           && SelectedMeasure != null
           && Labor != null
           && ErectCoeff != null
           && SelectedGroup != null;
 
    private async Task SaveWorkAsync()
    {
        // Ваша логика сохранения
        await Task.Delay(100); // Имитация асинхронной работы
    }
}
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
29.09.2025, 09:10
Помогаю со студенческими работами здесь

WPF, MVVM, сложная логика контрола
Какие механизмы есть в WPF, чтобы оставаясь в рамках MVVM иметь возможность реализации сложной...

MVVM. Ищу более удобную реализацию следующей логики
Простой случай: пользователь авторизируется в приложении: Model //Authorization ...

MVVM бизнес логика и Model
Всем привет! Я уже довольно долго пытаюсь разобраться что же представляет из себя архитектура...

Логика в свойстве (MVVM)
Не судите строго! Допускается-ли логика внутри свойства? Или как правильно реализовать по...

Как реализовать работу нескольких окон под шаблоном MVVM?
Использую шаблон MVVM. Всё что было в слое View всегда умудрялся держать только в xaml, и всё...


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

Или воспользуйтесь поиском по форуму:
20
Закрытая тема Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru