|
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
|
||||||||
Где лучше в MVVM реализовать дополнительную логику?25.09.2025, 19:46. Показов 7037. Ответов 118
Метки нет (Все метки)
мне почему-то кажется, что это не проблема, а как раз и задумано было, чтобы можно было переназначать свойства в зависимости от нужного (например, цвет текста при value>1000, условно говоря) - вот и вынесли это DP в отдельный класс... а за пример реализации и использования в XAML - спасибо!.. ? только в каких случаях это DP подключают обычно?.. как по мне, так Styles и Templates и даже Converters - нормальные внутренние (для объекта) свойства, дальше которых уже и придумать нечего (с практической точки зрения) чтобы захотеть подключать из-вне класса ещё и DP за гранью всего, что можно задать и в объекте...
0
|
||||||||
| 25.09.2025, 19:46 | |
|
Ответы с готовыми решениями:
118
|
|
Модератор
|
||||||
| 25.09.2025, 20:40 | ||||||
|
Например, здесь UserControl по типу NumericUpDown Реализовано дополнительное свойство Value2, которое позволяет задать "обратную" привязку от Value к источнику. Это нужно в некоторых случаях, когда прямая привязка из-за валидации диапазона будет давать отличное от имеющегося в Value значение. Добавлено через 4 минуты Например, здесь UserControl по типу NumericUpDown Реализовано дополнительное свойство Value2, которое позволяет задать "обратную" привязку от Value к источнику. Это нужно в некоторых случаях, когда прямая привязка из-за валидации диапазона будет давать отличное от имеющегося в Value значение. По идее (в начале темы), оно предназначалось для внутренней привязки, чтобы во "вне" могли видеть значение какого-то внутреннего элемента. Но внешние потребители могут не только просмотреть значение этого свойства, на и задать ему значение (в том числе новой привязкой). А это разрушит внутреннею привязку. То есть перестанет работать задуманная логика.
0
|
||||||
|
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
|
||||
| 26.09.2025, 12:38 [ТС] | ||||
validateValueCallback есть у DependencyProperty... т.е. валидацию можно делать не на уровне Модели, а на уровне View... т.к. не все контролы поддерживают Mode=TwoWay, видимо...
System.Windows.Input) или Behaviors (System.Windows.Interactity) реализовать доп. бизнес логику можно... Есть ли какие-нибудь советы когда что лучше выбрать?или по причине потокобезопасности какие-то из этих реализаций могут быть небезопасны?
0
|
||||
|
Модератор
|
|||||
| 27.09.2025, 00:02 | |||||
Mode=TwoWay не "спасает" при возращении PropertyMetadata.CoerceValueCallback или анимацией другого значения.Прочитайте за приоритеты присвоения значения DP-свойству. getter/setter of CLR не может содержать никакой логики кроме вызовов GetValue/SetValue of DP.Думаю, вы понимаете, что рекомендации для землянки, сруба, трёх-этажки, высотки будут разные. Но в общем случае потокобезопасность это очень обширный вопрос и решается множеством различных способов. Наиболее часто встречающиеся: 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 [ТС] | |||||||||||
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)); и его (метода) реализацией... -- меня пока это тз интересует...Добавлено через 1 час 4 минуты ?? или про консервацию вы имели ввиду Services в DI ?? мне показалось, что для запуска комманд - достаточно (класса под спойлером) Кликните здесь для просмотра всего текста
- хотя чтобы загнать его в бэк-энд и не тревожится о том, что же там внутри, - можно и Service (если уже написан)... но если реализации ещё НЕТ, то как по мне, то архитектурно - без разницы - то ли новый класс в проект, то ли писать реализацию сервиса и подключать его ??
0
|
|||||||||||
|
Модератор
|
|||||||||
| 27.09.2025, 12:02 | |||||||||
Behavior имеет смысл использовать только для изменения логики (поведения).Во всех остальных случаях достаточно "Attached Property". Одна из часто встречающихся задач Behavior для привязки свойств только для чтения [WPF, Элд Хасп] Behavior - это Freezable во FreezableCollection Behaviors. Behaviors же - это теневое Attached Property. Поэтому при обращении к нему всегда вызывается логика метода GetBehaviors в котором и происходит инициализация коллекции. В результате мы получаем в экземпляре Behavior родительский элемент (AssociatedObject) и, самое важное, возможность задать в одном Behavior несколько DP-свойств, которые могут действовать взаимосвязанно для обеспечения единой логики. Лучше будет если создадите новую тему и там архив или ссылку на Git репозиторий дадите. Эту автоматизацию можно реализовать в любом слое, но почти всегда правильным будет реализовать это в Модели. В задаче с БД, например, при "Добавить" нужно проверить корректность данных. Не пользователь же сам должен "руками и глазами" проверять все поля и искать такие же по всей БД. Эта корректность проверяется автоматически. Её можно реализовать на любом уровне. Но оптимальным будет её внедрение в БД (чтобы она сама отвечала за свою целостность), в крайнем случае в Репозиторий. А это все относится к Модели. А задача ViewModel и View - показать ошибку валидации пользователю в случае некорректных данных. Таски они как бы к этой теме не относятся. Вернее при их реализации, часто нужно учитывать потокобезопасное обращение с объектами, но каких-то внедрённых средств потокобезопасности в них нет. Разве что условно к этому можно отнести Контекст Синхронизации. Но в этом случае нужно чтобы все потребители объекта работали с ним через общий Контекст Синхронизации. Только так можно обеспечить потокобезопасность работы с этим объектом. Собственно по этому в UI элементах выкидывается исключение при работе с ним не из UI потока (вернее потока в котором они были созданы) - это гарантирует что все потребители будут в одном потоке, в частом случае, в одном контексте синхронизации. То есть для Тасков контекст синхронизации - это способ безопасной работы с некоторыми объектами, но все же это не создание потокобезопасности. Так слишком абстрактно получается. Добавлено через 7 минут Она не позволяет вызвать обновление своего состояния из ViewModel (или других потребителей). А, например, при асинхронном обновлении данных (состояния Модели) это нужно делать обязательно. Лучше взять простой пример отсюда: Простые реализации для тем на форуме Или использовать какой-то фрамеворк для создания ViewModel. Добавлено через 1 минуту Добавлено через 3 минуты JeyCi, мы уже вышли далеко за рамки этой темы. Наверное, лучше будет всё перенести в отельную тему.
1
|
|||||||||
|
262 / 151 / 33
Регистрация: 29.06.2019
Сообщений: 1,515
|
|||||||||||||
| 27.09.2025, 17:40 [ТС] | |||||||||||||
|
p.s. решено {
p.s. LoginVM наследую от
Command="{Binding LoginCommand}"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
|
|||||||||||||
|
Модератор
|
||||||||||
| 27.09.2025, 21:29 | ||||||||||
|
Например, удалённая БД. Поддерживает выполенение одновременно только одной операции. В Модели есть состояние 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(). В других реализациях - метод может именоваться по другому.@ и $.Правильно:
Интерполяция строк в C#. Но архитектурно, в ViewModel не может быть MessageBox. Вообще, не может быть зависимости от сборки PresentationFramework. Реализация команд (ваша и моя простая) тоже нарушает это правило. Поэтому в правильных фрамеворках используются другие реализации команд. 1) Нет возможности поднять CanExecuteChanged для экземпляра команды; 2) Зависимость от CommandManager, что делает такую реализацию команд "платформозависимой", что нарушает принципы MVVM.
0
|
||||||||||
|
Модератор
|
|
| 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 [ТС] | ||||
|
0
|
||||
|
Модератор
|
||||
| 28.09.2025, 11:33 | ||||
|
namespace - это просто идентификаторы для полного имени класса. Чтобы разрешать коллизии имён. Имеет значение сборка. Сборки: PresentationFramework, PresentationCore, WindowsBase - это View сборки. Зависимости от них на слоях ViewModel и Модели не должно быть. Это в идеале, конечно. На практике, в простых реализациях, особенно на уровне ViewModel - они бывают. Но всегда нужно понимать, что это нарушение паттерна и предвидеть к каким потенциальным проблемам это может привести. А в том что асинхронное изменение состояния команды не приводит к автоматической перевалидации кнопки, её использующей. Для перевалидации нужно явно подымать CanExecuteChanged в зависимых командах. А пример просто показывает в каком случае модель может асинхронно изменить своё состояние. И на практике это очень часто происходит. Здесь нужно ориентироваться на практику конкретного коллектива. Например, в 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 [ТС] | ||||
<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 минут Добавлено через 1 час 4 минуты using System.Windows.Input; - действительно, тоже завязана на Windows dllДобавлено через 29 минут Добавлено через 6 минут и я пока не сделала правильный Binding этого RelayCommand<T> ... Добавлено через 30 минут в общем, понятно, надо, наверно, как-то в RelayCommand<T> посланном из VM Итого: Binding (даже на команды) - в принципе звучит получше, чем ссылаться (referencing) на View из VM или Model
0
|
||||
|
Модератор
|
||||||||
| 28.09.2025, 22:06 | ||||||||
|
Это просто namespace. Он может быть в любой сборке. В том числе и вашей личной. Этот интерфейс из Standard 2.0 Будет ли в его процессе изменено состояние модели. Это совершенно не связанные вещи. Раз вас так запутал вызовов метода модели, то давайте представим другой пример. Какой-то торговый бот работающий с биржей онлайн. Модель постоянно получает обновление от биржи - по нескольку сот раз в секунду. В View есть кнопка для отработки определённого сигнала. Пока сигнала нет - кнопка Disabled. Так как обновление модели с биржи асинхронно (но команда в кнопке синхронная!), то после получения сигнала необходимо поднять CanExecuteChanged в команде, чтобы кнопка перевалидировалась и перешла в состояние Enabled. И 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>. Важно не то как метод выполняется, а что состояние модели изменяется асинхронно или синхронно, но не зависимо от действий пользователя в GUI. А раз нет действий в GUI, то не подымается CommandManager.RequerySuggestedт и кнопки "саи по себе" не обновятся. Нужно поднятие CanExecuteChanged в командах для обновления кнопок. ViewModel может реагировать на состояние Модели, но ни как не View. Для перевалидации их состояния нужно поднятие CanExecuteChanged. ViewModel может ссылаться на Model, но не может ссылаться на View. А в целом концепции MV* паттернов (в том числе MVVM) лучше соответствует слабая связь слоёв реализованная через интерфейсы.Вот схема связей для MVVM: Структура связей в WPF Решении
0
|
||||||||
| 28.09.2025, 23:17 | |
|
0
|
|
| 29.09.2025, 03:11 | |
|
0
|
|
|
Модератор
|
|
| 29.09.2025, 08:44 | |
|
Wolfdp, эта схема из другой темы. Я дал ссылку на неё. Детали там. Дублировать сюда их не стал.
Добавлено через 1 час 30 минут Wolfdp, вот схема Структуры Решения с GUI на WPF. В нём уже расписано по-проектно какой минимум должен быть в Решении: Структура WPF решения Добавлено через 4 минуты Но в реале Модель часто использует сущности (обычно, условно иммутабельные DTO), которые доступны не только в VM, но и в View. Возможно, ещё могут потребоваться типы общего употребления, то есть те о которых известно всем слоям. Такие типы следует помещать в отдельную сборку (проект) связь с которой может быть у любого проекта Решения.
0
|
|
|
|
||||||
| 29.09.2025, 08:54 | ||||||
|
JeyCi, Вот так организуется команда из пакета CommunityToolkit
0
|
||||||
|
Модератор
|
|||||||
| 29.09.2025, 09:10 | |||||||
|
Пишу здесь в редаторе - могут быть ошибки:
1
|
|||||||
| 29.09.2025, 09:10 | |
|
Помогаю со студенческими работами здесь
20
MVVM бизнес логика и Model
Как реализовать работу нескольких окон под шаблоном MVVM? Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
||||
|
Новый ноутбук
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
— Расскажи мне о Мире, бродяга,
Ты же видел моря и метели.
Как сменялись короны и стяги,
Как эпохи стрелою летели.
- Этот мир — это крылья и горы,
Снег и пламя, любовь и тревоги,
И бескрайние. . .
|