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

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

25.09.2025, 19:46. Показов 8777. Ответов 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
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 09:55
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от JeyCi Посмотреть сообщение
ru_stackoverflow_com
Ссылки можно кидать только на англоязычный SO, на русский запрещено.
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 09:57  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Это вообще-то функция хранилища данных
ну, в общем, да - можно и на сервере (в бд) - такое считать...

p.s. к #41 - там хороший пример - сохраните, please
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 10:12
Цитата Сообщение от JeyCi Посмотреть сообщение
да - можно и на сервере (в бд) - такое считать...
Оно там и должно считаться. Зачем тянуть 100500 записей из БД чтобы выдать с помощью кода три? Тем более СУБД сделает это в разы быстрее чем код на ЯП...
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 10:20  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Зачем тянуть 100500 записей из БД чтобы выдать с помощью кода три?
тянуть фильтром WHERE smth= и сложить 3 штуки, и послать на разный UI эти 3 штуки, и ещё какие-нибудь мелочи -- тоже смысла нет хранить на сервере... -- нет жёстко-заданных способов обработки данных - всё зависит от тз
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 10:27
Цитата Сообщение от JeyCi Посмотреть сообщение
тоже смысла нет хранить на сервере...
Зачем жалеть сервер СУБД, он для этого создан, это его работа... Вам там места жалко на хранимку? Или трудно в коде написать запрос SQL и с помощью DbCommand (SqlCommand) его выполнить? И вообще - он железный, памяти у него дофига, ресурсов тоже - пусть пашет.
А клиента незачем грузить всей этой фигнёй, так как иногда клиенты работают на офисных калькуляторах, которые офис еле тянут...
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 13:03  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Раз вас так запутал вызовов метода модели, то давайте представим другой пример.
Какой-то торговый бот работающий с биржей онлайн.
Модель постоянно получает обновление от биржи - по нескольку сот раз в секунду.
В View есть кнопка для отработки определённого сигнала.
Пока сигнала нет - кнопка Disabled.
Так как обновление модели с биржи асинхронно (но команда в кнопке синхронная!), то после получения сигнала необходимо поднять CanExecuteChanged в команде, чтобы кнопка перевалидировалась и перешла в состояние Enabled.
вот сложно у вас как-то - хотите обязательно CanExecuteChanged по Event'у, иначе Disabled... ведь DoEvents никто не отменял - зачем обездвиживать кнопку? или это только для WinForms?
C#
1
2
3
4
5
6
IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10);  // ИЛИ Do Command Execution 
      Application.DoEvents();  //to make the UI responsive !!!
}
а сигнал из Модель - которая отловит, например Volume>1000 с биржи -- грубо говоря дописать в DataGrid (с пометкой времени прихода), (если график не рисовать и стрелку выкинуть на график) -- всё тот же Binding Command в DataGrid

Добавлено через 5 минут
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Зачем жалеть сервер СУБД... Вам там места жалко на хранимку?
там бэк-энд разработчик может заартачится, что не надо захламлять сервер... (ему потом сопровождать нелегко)...

Но вообще, мне ваша идея понятна и симпатична - закинуть бд на облако и тянуть с него нужное в свой UI -- скажите технически вы весь свой юзерский App выполняете на клиенте? ИЛИ какие-то Сервисы тоже располагаете на облаке, или они есть в NET.Core, или (если самописные) - то откуда их подключаете (с облака [сервера] или из Windows или local App)? или что-то с Github у вас в runtime идёт? (но там вроде нескомпилированные коммиты только хранить можно)
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 13:40
Цитата Сообщение от JeyCi Посмотреть сообщение
ведь DoEvents никто не отменял
Никогда это не используйте. Есть более продвинутые вещи для многопоточного выполнения.
Цитата Сообщение от JeyCi Посмотреть сообщение
там бэк-энд разработчик может заартачится, что не надо захламлять сервер... (ему потом сопровождать нелегко)...
Это его проблемы. Сервер СУБД должен делать то для чего он был создан - хранить данные, выполнять запросы с любыми расчётами и выдавать результат клиенту. Делает он это гораздо быстрее, чем всё что написано снаружи. Если мало возможностей Transact-SQL (Диалект MS SQL Server) то используйте UDF/SP написанные на NET. У этой СУБД есть такая возможность. И повторюсь - всё что выполняется на сервере работает в разы быстрее.
Цитата Сообщение от JeyCi Посмотреть сообщение
скажите технически вы весь свой юзерский App выполняете на клиенте?
Да. Написан тонкий клиент, который только гоняет/готовит данные. всё остальное происходит на сервере СУБД.
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 13:58  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Никогда это не используйте. Есть более продвинутые вещи для многопоточного выполнения.
банально, BackgroundWorker, объявленный в MainWindow...
Кликните здесь для просмотра всего текста
у меня в С++ 2 кнопки - одна становится Disabled, одна остаётся активной - для прерывания процесса ... и на них колл-бэки...
C++
1
2
            btn_1.WhenAction = THISBACK ( btn1_Click );         
            btn_2.WhenAction = [&] {this->status.Set("Stopped");};
на 2-й кнопке лямбда отлавливает Event смены-триггера-остановки в background потоке -- всё в while loop'e -- пока там же (в background) не сменится триггер bool -- и не вернётся в главный поток в изменённом состоянии с последующим изменением состояния 1-й кнопки в главном потоке

но вот Сервисы - тоже ведь в др потоке мониторят - с момента OnStart до момента OnStop... - почему сервис нельзя прикрепить к кнопке? - с момента Disabled до момента Enabled - на том и перевалидировать кнопку на её новое состояние?
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 14:26
Цитата Сообщение от JeyCi Посмотреть сообщение
но вот Сервисы - тоже ведь в др потоке мониторят - с момента OnStart до момента OnStop... - почему сервис нельзя прикрепить к кнопке? - с момента Disabled до ммента Enabled - на том и перевалидировать кнопку на её новое состояние?
Зачем вам сервис на кнопку? Что вы хотите сделать? Или это просто вопрос ради вопроса?
Цитата Сообщение от JeyCi Посмотреть сообщение
у меня в С++ 2 кнопки
И с какого боку тут C++?

Добавлено через 5 минут
JeyCi, Пример взаимодействия потоков VM и UI
VM
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
private SynchronizationContext _viewContext;
 
public SynchronizationContext ViewContext
{
    get => _viewContext;
    set => Set(ref _viewContext, value); // Тут была логика, потому такая сложная запись
}
 
private async Task LoadVMDataAsync()
{
    try
    {
        List<HBSpecificationGroup> groups = await _mainDA.HBSpecificationGroupDA.GetHBSpecificationGroupsAsync(Branch);
        // Синхронизируем контекст
        ViewContext.Send(_ =>
        {
            // Что-то делаем в нужном контексте
            if (Groups.Count > 0)
                Groups.Clear();
 
            if (groups.Count > 0)
                groups.ForEach(g => Groups.Add(g));
            // Дёргаем событие
            GroupsLoaded?.Invoke(this, EventArgs.Empty);
        }, null);
    }
    catch (Exception ex)
    {
        if (ex.InnerException != null)
        {
 
        }
 
        _windowService.ShowErrorMessage(ex.Message, "Ошибка");
    }
}
View
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
public partial class HBSpecificationGroupWindow : Window
{
    private CollectionViewSource _sourceGroups;
    private ListCollectionView _viewGroups;
 
    public HBSpecificationGroupWindow()
    {
        InitializeComponent();
    }
 
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Получаем экземпляр VM из сервиса
        IHBSpecificationGroupWindowVM vm = (IHBSpecificationGroupWindowVM)DataContext;
 
        _sourceGroups = (CollectionViewSource)Resources["GroupsSorted"];
        _viewGroups = (ListCollectionView)_sourceGroups.View;
        // Подписываемся на событие
        vm.GroupsLoaded += OnGroupsLoaded;
        // Получаем контекст окна через который работаем с другого потока
        vm.ViewContext = SynchronizationContext.Current;
    }
 
    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        IHBSpecificationGroupWindowVM vm = (IHBSpecificationGroupWindowVM)DataContext;
        // Отписываемся от события
        vm.GroupsLoaded -= OnGroupsLoaded;
    }
    // Что-то делаем по событию
    private void OnGroupsLoaded(object sender, System.EventArgs e)
    {
        if (_viewGroups.Count > 0)
            _ = _sourceGroups.View.MoveCurrentToFirst();
    }
}
Добавлено через 2 минуты
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
// Получаем экземпляр VM из сервиса
Из DataContext конечно. Что-то не то написал...
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 14:46  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Или это просто вопрос ради вопроса?
ради вопроса был - чтобы также просто, как у меня было... (думаю, где делать на c++ или c#)... и чем свой сервис лучше обыкновенной dll... - до сих пор думаю... ведь и то, и то надо подключать
но спасибо за пример!
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 14:54
JeyCi,
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Пример взаимодействия потоков VM и UI
Одинаково делается и для сервиса.
1. Пишем интерфейс, в котором объявляем свойство SynchronizationContext и методы сервиса (Start, Stop и т.д.), события.
2. Запускаем сервис как transient (по вызову).
3. В нужном месте получаем его из DI по интерфейсу.
4. Присваиваем ему контекст и запускаем нужные методы.
5. Подписываемся на его события и по ним делаем что нам нужно (включаем/выключаем кнопки, элементы UI, рисуем что-то и т.д.)
6. Балдеем от того что всё работает как нужно и без лишних затрат процессорного времени.


Добавлено через 3 минуты
JeyCi,
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Code
1
ViewContext.Send
Тут есть один момент. Для синхронизации используются два метода - Send() и Post(). Первый синхронный, используется если нужно дождаться выполнения и получить реальные данные. Второй асинхронный - всё что будет в нём -> запустил и забыл.
Оба метода не вешают контекст, так выбирать вам исходя из задачи.
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 14:54  [ТС]
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
6. Балдеем от того что всё работает как нужно и без лишних затрат процессорного времени.
с облаком (или GoogleDrive - если туда можно бд закинуть для скорости) ещё не связывалась, чтобы бд туда закинуть... а sqlite и на Windows работает... вы к облаку коннектились хоть раз в жизни? - есть примерчик?.. чтобы оттестить (до мыслей о полноценном сервере)
0
 Аватар для Andrey-MSK
3392 / 2278 / 388
Регистрация: 14.08.2018
Сообщений: 7,706
Записей в блоге: 4
30.09.2025, 14:57
Цитата Сообщение от JeyCi Посмотреть сообщение
есть примерчик?
Почитайте про Microsoft Azure. Это MS SQL Server в облаке на серверах Microsoft.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
30.09.2025, 19:19
Цитата Сообщение от JeyCi Посмотреть сообщение
P.P.P.S.
и пример App:
Цитата Сообщение от JeyCi Посмотреть сообщение
- посмотрите линк,
Там большой пример и много разных вопросов решается.
На что собственно вы там хотели обратить внимание?

Если судить концептуально, то там неверен сам ход рассуждения.
ViewModel - это обёртка Model для удобной работы View.
Поэтому рассуждения должны быть "от Модели", а нет от "вот у меня есть два окна, а в нём такие-то регионы и т.д."

Добавлено через 21 минуту
Типа.
Описание Модели:
1) Модель находится в неавторизированном состоянии
2) Для авторизации надо вызывать метод авторизации с передачей в него таких-то данных
3) По завершению выполнения метод возвращает результат авторизации
4) Если авторизация была удачна, то состояние Модели переходит в авторизированное.

Надо:
1) Для неавторизированного состояния показать Окно (или страницу, регион) авторизации, в котором вводятся данные авторизации и есть кнопка для команды авторизации
2) Если авторизация неудачно - вывести ошибку авторизации
3) Если авторизация удачна - вывести представление для авторизированного состояния.

Реализация (один из вариантов)
ViewModel:
1) ViewModel для авторизированного состояния
2) ViewModel для авторизации
3) Основная ViewModel для переключения между состояниями. Для этого предусматривается свойство (Current - для примера) в котором переключаются VM
4) Все VM получают в конструкторе Model

Представление:
1) Одноконное многостраничное.
2) Страницы представления реализованы UserControl, которые через DataTemplate связаны с нужными ViewModel
3) Контент Окна принимает Current и больше ничего в нём нет. Можно даже не создавать XAML Окна, а использовать базовый Window.

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

Добавлено через 17 минут
Цитата Сообщение от JeyCi Посмотреть сообщение
едь DoEvents никто не отменял - зачем обездвиживать кнопку? или это только для WinForms?
Только для Форм. И, с точки зрения WPF, очень расточительно.
Вместо изменения представления одной кнопки перерисовывать все окна....

Цитата Сообщение от JeyCi Посмотреть сообщение
вот сложно у вас как-то - хотите обязательно CanExecuteChanged по Event'у, иначе Disabled...
Пусть не по эвенту. Любое асинхронное (по отношению к GUI) изменение модели, для корректного состояния кнопки с командой, требует поднятия CanExcuteChanged команды после этого изменения.
По другому ни как.

А... Не. Можно ещё вызывать после изменения CommandManager.InvalidateRequery().
Но это можно сделать только в View WPF и потребует от View "отлова" асинхронных изменений ViewModel (и всех вложенных объектов), а так же вызовет перевалидацию ВСЕХ команд, а не только одной нужной.

Добавлено через 2 минуты
Цитата Сообщение от JeyCi Посмотреть сообщение
всё тот же Binding Command в DataGrid
Команда - это ПРОСТО обычное СВОЙСТВО.
Ничего экзотического Binding с ним сделать не может. Он только вернёт его значение.
Нужно ли вызывать и когда вызывать перевалидацию команды или кнопки Binding не знает, это не его функция.
0
30.09.2025, 19:50

Не по теме:

Цитата Сообщение от Andrey-MSK Посмотреть сообщение
Ссылки можно кидать только на англоязычный SO, на русский запрещено.
лол, а чем это обусловлено?

0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 20:26  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Всё работает через единую Модель и внедряется только она.
и что - на уровень VM размещать разделение привилегий (например, по факту авторизации) ? (ну, допустим - ведь это м.б. ответственность сервера)...

или ещё хуже - сервер бд школы (электронные дневники учащихся) - и что, для конкретного класса после соответствующей авторизации вытягивать модель по всей школе?.. я бы сказала, что на 2 страницы, можно обратиться к 2ум разным моделям, которые каждая по-своему просто реализует какой-нибудь самописный IModels...

Добавлено через 23 минуты
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Команда - это ПРОСТО обычное СВОЙСТВО.
Ничего экзотического Binding с ним сделать не может. Он только вернёт его значение.
Нужно ли вызывать и когда вызывать перевалидацию команды или кнопки Binding не знает, это не его функция.
вообще логика всё та же (как у меня была в с++, только реализованная в с# ICommand) - в VM : отправка команды, колл-бэком (внутренне реализованным в RelayCommand) отлов результата работы команды и перевалидация команды "завершена"... и по факту - ручная перевалидация кнопки в Enabled...

вот а если сервис подключить - по вашему тз - с момента OnStart - пусть себе шлёт Сигналы для отлова (или messages), а VM отлавливает и в DataGrid закидывает... -- тоже примерно по схеме выше (отловили - опубликовали - отловили - опубликовали) - ну и состояние менять особо сильно не надо... до OnStop сервиса...

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

Всем спасибо за конструктивный разговор - (отлов асинхронных сообщений уже, наверно, для др. ветки)
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
30.09.2025, 20:46
Цитата Сообщение от Andrey-MSK Посмотреть сообщение
JeyCi, Пример взаимодействия потоков VM и UI
А при чём здесь взаимодействие потоков?
Асинхронность может быть и в одном потоке.
Речь же про поднятие CanExecuteChanged. Его надо подымать при любом асинхронном изменении зависимости, даже если это изменение произошло в UI потоке.

Пример с двумя кнопками.
В одной есть поднятие CanExecuteChanged, в другой нет.
Зависимость изменяется асинхронно, но в потоке диспетчера окна.
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
using Simplified;
using System.Windows;
using System.Windows.Threading;
 
namespace CFTopics2025.CF.CanExcuteTest
{
    public partial class CanExecuteTestWindow : Window
    {
        private readonly CanExecuteTestVM vm;
        private readonly DispatcherTimer timer;
        public CanExecuteTestWindow()
        {
            InitializeComponent();
            vm = (CanExecuteTestVM)DataContext;
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += delegate
            {
                vm.IsBusy = !vm.IsBusy;
            };
            timer.Start();
        }
    }
 
    public class CanExecuteTestVM : BaseInpc
    {
        private bool isBusy;
        private RelayCommand? _simpleCommand;
        private RelayCommand? _raiseCommand;
 
        public bool IsBusy { get => isBusy; set { Set(ref isBusy, value); RaiseCommand.RaiseCanExecuteChanged();} }
 
        public RelayCommand SimpleCommand => _simpleCommand ??= new RelayCommand(() => { }, () => IsBusy);
        public RelayCommand RaiseCommand => _raiseCommand ??= new RelayCommand(() => { }, () => IsBusy);
    }
}
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Window x:Class="CFTopics2025.CF.CanExcuteTest.CanExecuteTestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CFTopics2025.CF.CanExcuteTest"
        mc:Ignorable="d"
        Title="CanExecuteTestWindow" Height="250" Width="200">
    <Window.DataContext>
        <local:CanExecuteTestVM/>
    </Window.DataContext>
    <StackPanel>
        <TextBlock Text="{Binding IsBusy}"
                Margin="20" Padding="15 5" HorizontalAlignment="Center"/>
        <Button Content="Simple" Command="{Binding SimpleCommand}"
                Margin="20" Padding="15 5" HorizontalAlignment="Center"/>
        <Button Content="Raise" Command="{Binding RaiseCommand}"
                Margin="20" Padding="15 5" HorizontalAlignment="Center"/>
    </StackPanel>
</Window>
Результат:


Но если быстро кликать по любой из кнопок, то Simple-кнопка будет менять своё состояние корректно.
За это отвечает CommandManager.RequerySuggested, на который подписываются команды в своём конструкторе.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
30.09.2025, 20:57
Цитата Сообщение от JeyCi Посмотреть сообщение
и что - на уровень VM размещать разделение привилегий (например, по факту авторизации) ?
Зачем?
Разделение привилегий происходит на уровне Модели.
Реализации могут быть разные.
Например, два таких варианта:
1) Модель "максимум", но с запретом каких-то состояний, методов и др., в зависимости от установленной в авторизации роли. Для каждой роли - своё представление. Нужное представление выбирается DataTemplateSelector.
2) Есть некая базовая Модель общая для всех ролей. Для каждой роли строится своя производная Модель. Модель авторизации, в случае успеха, возвращает нужную Модель роли. В этом случае представление для роли выбирается через его связь с типов в DataTemplate.

1-й вариант менее безопасный и требует большего внимания к распределению и управлению разрешениями.
2-й более безопасный, но не подходит для динамического управления разрешениями. Применяется там где ролей немного и у всех ролей жёстко зашитые разрешения. Типа: гость, пользователь, админ - всё.
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,554
30.09.2025, 21:02  [ТС]
вот пример из Google использования CanExecuteMyCommand
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
public class MyViewModel : ObservableObject // Assuming ObservableObject provides PropertyChanged notification
{
    private string _userName;
    public string UserName
    {
        get => _userName;
        set
        {
            SetProperty(ref _userName, value); // Raises PropertyChanged
            MyCommand.NotifyCanExecuteChanged(); // Notify command to re-evaluate CanExecute
        }
    }
 
    public RelayCommand MyCommand { get; }
 
    public MyViewModel()
    {
        MyCommand = new RelayCommand(ExecuteMyCommand, CanExecuteMyCommand);
    }
 
    private void ExecuteMyCommand()
    {
        // Command logic here
    }
 
    private bool CanExecuteMyCommand()
    {
        return !string.IsNullOrWhiteSpace(UserName);
    }
}
логика: не пропустит команду, если не введено имя...
off:
можем внедрить всё в ту же авторизацию (мы логику особо сильно и не меняем уже 3 страницы - только Login)
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
30.09.2025, 22:03
Цитата Сообщение от JeyCi Посмотреть сообщение
вот пример из Google использования CanExecuteMyCommand
У меня тестовый пример тоже самое показывает - внедрение в зависимость поднятия CanExecuteChanged

Цитата Сообщение от JeyCi Посмотреть сообщение
логика: не пропустит команду, если не введено имя...
Базовую реализацию для команды авторизации я бы делал через её параметр.
Типа:
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
using Simplified;
using System.Windows;
 
namespace CFTopics2025.CF.CanExcuteTest
{
    public partial class AuthorizationWindow : Window
    {
        public AuthorizationWindow()
        {
            InitializeComponent();
        }
    }
 
    public class AuthorizationViewModel : BaseInpc
    {
        private readonly AuthorizationModel model;
        private RelayCommand<AuthorizeData>? _authorize;
        private WorkViewModel? _work;
        private Exception? _authorizationException;
        public AuthorizationViewModel()
            : this(new AuthorizationModel())
        { }
 
        public AuthorizationViewModel(AuthorizationModel model)
        {
            this.model = model;
        }
 
        public WorkViewModel? Work { get => _work; set => Set(ref _work, value); }
        public Exception? AuthorizationException { get => _authorizationException; set => Set(ref _authorizationException, value); }
 
 
        public RelayCommand<AuthorizeData> Authorize
            => _authorize ??= new RelayCommand<AuthorizeData>
            (
                data =>
                {
                    try
                    {
                        WorkModel? work = model.Authorize(data.Name, data.Password);
                        if(work is null)
                            throw new NullReferenceException("Авторизация не удачна");
                        Work = new WorkViewModel(work);
                    }
                    catch (Exception ex)
                    {
                        Work = null;
                        AuthorizationException = ex;
                    }
                },
                data => !string.IsNullOrWhiteSpace(data.Name));
 
    }
 
 
 
    public class WorkViewModel
    {
        private readonly WorkModel model;
 
        public WorkViewModel(WorkModel model)
        {
            this.model = model;
        }
    }
 
    public class WorkModel
    {
    }
 
    public class AuthorizationModel
    {
        public WorkModel? Authorize(string? name, string? password)
        {
            ArgumentException.ThrowIfNullOrWhiteSpace(name);
            ArgumentNullException.ThrowIfNull(password);
            return new WorkModel();
        }
    }
 
    public class AuthorizeData
    {
        public string? Name { get; set; }
        public string? Password { get; set; }
    }
 
}
XML
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
<Window x:Class="CFTopics2025.CF.CanExcuteTest.AuthorizationWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CFTopics2025.CF.CanExcuteTest"
        mc:Ignorable="d"
        Title="AuthorizationWindow" Height="450" Width="800">
    <Window.Style>
        <Style TargetType="Window">
            <Setter Property="Content" Value="{Binding Work}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Work}" Value="{x:Null}">
                    <Setter Property="Content" Value="{Binding}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Style>
    <Window.DataContext>
        <local:AuthorizationViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:AuthorizationViewModel}">
            <DataTemplate.Resources>
                <local:AuthorizeData x:Key="data"/>
            </DataTemplate.Resources>
            <StackPanel Grid.IsSharedSizeScope="True">
                <TextBox Text="Авторизация" HorizontalAlignment="Center"/>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="name"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="Имя: " Margin="5"/>
                    <TextBox x:Name="tbName" Grid.Column="1" Margin="5"
                             Text="{Binding Name, Source={StaticResource data}, UpdateSourceTrigger=PropertyChanged}"/>
                </Grid>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="name"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="Пароль: " Margin="5"/>
                    <TextBox x:Name="tbPassword" Grid.Column="1" Margin="5"
                             Text="{Binding Password, Source={StaticResource data}, UpdateSourceTrigger=PropertyChanged}"/>
                </Grid>
                <Button Content="Авторизировать"
                        CommandParameter="{DynamicResource data}"
                        Command="{Binding Authorize}">
                </Button>
                <TextBlock Text="{Binding AuthorizationException}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:WorkViewModel}">
            <StackPanel>
                <TextBox Text="Работа" HorizontalAlignment="Center" FontSize="40"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
</Window>
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
30.09.2025, 22:03

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

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
60
Закрытая тема Создать тему
Новые блоги и статьи
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru