Страницы

Поиск по вопросам

воскресенье, 15 декабря 2019 г.

Где размещать логику приложения MVVM?

#c_sharp #wpf #mvvm #логика


Всем привет!

Возможно вопрос типичный. Но не дает мне покоя. Если с моделями, вроде таблиц, вопросов
не возникает, то в данном случае не смог найти ответа.

Суть: дорабатываю свою программу (WPF) вышивки крестиком, возникла потребность рисования
с различными его аспектами (типы, выделение и тп).

Есть примерно такой код (идею подсмотрел в FastGrid):

enum CellBlockType
{
    CrossStitch,
    HalfStitch,
    //...
}

interface ISchemeCellBlock
{
    CellBlockType BlockType { get; }
    //...
}

interface ISchemeCell
{
    int BlockCount { get; }
    ISchemeCellBlock GetBlock(int index);
}

interface ISchemeModel
{
    int RowCount { get; }
    int ColumnCount { get; }
    ISchemeCell GetCell(ISchemeView view, int row, int column);
    void HandleCommand(ISchemeView view, CellAddress address, object commandParameter,
ref bool handled);
}

interface ISchemeView
{
    void InvalidateCell(int row, int column);
    void InvalidateAll();
    bool ShowGuideline { get; set; }
    //...
}

struct CellAddress { }


В данный момент все действия мыши обрабатываются в HandleCommand, а в качестве commandParameter
передаются MouseEvenArgs, чтобы понимать когда рисовать/не рисовать, перемещать/вставлять
и тп.

Таким образом, ISchemeView изменяет данные модели минуя ViewModel, но и не в коде
самого представления. В самой же ViewModel предполагается выбор "кистей рисования",
масштабирование, команды ввода/вывода и все косвенные изменения модели.

Имеет ли право на жизнь такой код в рамках MVVM? Или же надо "отправлять" действия
мыши из ISchemeView во ViewModel, а уже через нее воздействовать на саму модель?

UPD:

Итак, сделал как предложил RusArt: переместил редактирование в codebehind контрола.
Что имею теперь:


Model - набор классов, реализующих вышеописанные интерфейсы.
View содержит сам контрол и кнопки для выполнения команд.
ViewModel отдает класс, реализующий ISchemeModel, в контрол через свойство, команды
во View для управления ISchemeModel (сохранение, ведение истории изменений и т.п.),
а также "отдает" во View коллекцию элементов (палитру) из ISchemeModel.


Такой подход будет правильный? И можно ли назвать мои типы данных моделью или что
ею считать?

Хотелось бы до конца разобраться.
    


Ответы

Ответ 1



Если вы хотите следовать паттерну MVVM, то у вас пока не получается. Model ничего не должна знать про View, а у вас знает (вы передаете ISchemeView и MouseEvenArgs) Познакомьтесь с командами (интерфейс ICommand). Часто используются сторонние реализации типа DelegateCommand: public class DelegateCommand : ICommand { private readonly Predicate _canExecute; private readonly Action _execute; public event EventHandler CanExecuteChanged; public DelegateCommand(Action execute) : this(execute, null) { } public DelegateCommand(Action execute, Predicate canExecute) { _execute = execute; _canExecute = canExecute; } public override bool CanExecute(object parameter) { if (_canExecute == null) { return true; } return _canExecute(parameter); } public override void Execute(object parameter) { _execute(parameter); } public void RaiseCanExecuteChanged() { if( CanExecuteChanged != null ) { CanExecuteChanged(this, EventArgs.Empty); } } } Команда содержит ссылку на метод, который будет выполняться при выполнении команды, и ссылку на метод, который указывает, может ли выполниться команда. Таким образом, во ViewModel вы создаете свойство типа ICommand, пишите методы на выполнение и выяснение возможности выполнения, привязываете команду ко View. Конечно, для View должно быть определено dependency property, которое отдаст правильный параметр с координатам в команду (это уже другой вопрос, разработка своего контрола, сейчас не знаю какой компонент у вас во View). Но во ViewModel будет что-то примерно такое: public class SchemeViewModel : NotifyPropertyChangedBase { ISchemeModel _model; public ICommand SelectCommand {get;} = new DelegateCommand(Select, CanSelect); void Select(object o) { var coords = o as CellAddress; _model.Select(coords); } bool CanSelect(object o) { var coords = o as CellAddress; return _model.CanSelect(coords); } } Также откажитесь от интерфейса ISchemeView, который позволяет управлять View из модели. Это фишка паттера MVC, но не MVVM. Есть много разных способов сделать что вы хотите. Например, подписаться на события модели из вьюмодели. Помните, ваша модель должна работать как независимая часть программы. Вы должны уметь изменить модель кодом. Invalidate правда происходит во View, или оно происходит в модели, а View просто отображает состояние модели?

Комментариев нет:

Отправить комментарий