c# – Where to place MVVM application logic?

Question:

Hello everyone!

Perhaps the question is typical. But it doesn't give me peace. If there are no questions with models, like tables, then in this case I could not find an answer.

Essence: I am finalizing my cross-stitch program (WPF), there was a need for drawing with its various aspects (types, selection, etc.).

There is something like this code (I spied the idea in 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 { }

At the moment, all mouse actions are processed in HandleCommand , and MouseEvenArgs are passed as commandParameter to understand when to draw / not draw, move / paste, etc.

Thus, ISchemeView changes the model data bypassing the ViewModel , but not in the code of the view itself. The ViewModel itself assumes the choice of "drawing brushes", scaling, input / output commands and all indirect changes to the model.

Does such code have the right to life within the framework of MVVM? Or is it necessary to "send" mouse actions from ISchemeView to ViewModel , and through it to influence the model itself?

UPD:

So, I did as suggested by RusArt : I moved editing to the codebehind of the control. What I have now:

  • Model – a set of classes that implement the above interfaces.
  • View contains the control itself and buttons for executing commands.
  • The ViewModel sends the class that implements ISchemeModel to the control through a property, commands in the View for managing the ISchemeModel (saving, maintaining a history of changes, etc.), and also "gives" a collection of elements (palette) from the ISchemeModel to the View.

Would this approach be correct? And can I call my data types a model or what should be considered a model?

I would like to get to the bottom of it.

Answer:

If you want to follow the MVVM pattern, then you have not succeeded yet. Model should not know anything about View, but you know (you pass ISchemeView and MouseEvenArgs)

Get familiar with commands (ICommand interface). Third-party implementations of the DelegateCommand type are often used:

public class DelegateCommand : ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public event EventHandler CanExecuteChanged;

    public DelegateCommand(Action<object> execute) 
                   : this(execute, null)
    {
    }

    public DelegateCommand(Action<object> execute, 
                   Predicate<object> 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);
        }
    }
}

The command contains a reference to a method that will be executed when the command is executed, and a reference to a method that indicates whether the command can be executed.

Thus, in the ViewModel you create a property of type ICommand, write methods for execution and finding out the possibility of execution, bind the command to the View. Of course, the dependency property must be defined for the View, which will give the correct parameter with the coordinates to the command (this is another question, developing your own control, now I don’t know what component you have in the View). But in the ViewModel there will be something like this:

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);
    }
}

Also discard the ISchemeView interface, which allows you to control the View from the model. This is a feature of the MVC patter, but not MVVM. There are many different ways to do what you want. For example, subscribe to model events from the view model. Remember, your model must work as an independent part of the program. You should be able to modify the model with code. Does Invalidate really happen on the View, or does it happen on the Model and the View just displays the model's state?

Scroll to Top