karolyzz
karolyzz

Reputation: 510

MVVM pattern for WPF: Model vs ViewModel

I can't really wrap my head around the following problem:

All I have in the application is a textboxfor the user input, a button for performing a background calculation on that input and a textblock. Imagine I have to use MVVM, so I have my view, viewmodel and model classes.

I bind the controls (textbox, button and textblock) from the view to the viewmodel on corresponding properties and commands. However, I'm not sure where the viewmodel functionality should end. For instance, would the following be a way to structure the application?

Model:

public class Model
{

    public string Input { get; set; }
    public string Output { get; set; }

    public void FancyMethod ()
    {
       // Use input to calculate output
    }

}

ViewModel:

public class ViewModel
{

    public string Input {get; set;}
    public string Output {get; set;}
    public ICommand command {get; set;}
    public Model model {get; set;}

    public ViewModel() 
    {
      model = new Model();
    }

    // When the button is pressed, model.input = Input and then execute model.FancyMethod()

}

Upvotes: 0

Views: 2808

Answers (3)

Stefan
Stefan

Reputation: 17658

If you want to keep a clean layer model, you should not include public Model model {get; set;} in your ViewModel.

So, for example, if you have a command, targeting some business model, your structure should be something like this:

//you don't have this one... but well, maybe other cases have
public class SomeService : ISomeService
{
    //member of ISomeService
    public void SomeFancyMethod(Model model)
    {
        //do stuff..
    }
}

public class Model //might be database, or domain model.
{
   public string Input { get; set; }
   public string Output { get; set; }
}

As for your viewmodel, it will become something like this:

public class ViewModel
{
    private ISomeService _someService;

    //note: someService is passed through a IoC service like ninject, unity, autofac etc.
    public ViewModel(ISomeService someService)
    {
        _someService = someService;
        //initialize the command:
        command = new RelayCommand(() =>
        {    
            _someService .SomeFancyMethod(new Model()
            {
                //properties could be mapped with an automapper.
            });
        });
    }

    public ICommand command {get; private set;}
    public string Input {get; set;}
    public string Output {get; set;}
 }

Note: there are some additional techniques involved:

  • using an inversion of control container, and pass the service through the constructor.
  • abstracting the service by means of an interface (ISomeService)
  • possibly some automapper to isolate your mapping from and towards Models/ViewModels

"So why make this so 'complicated'? You are just making a copy.", a commonly heard argument against this pattern:

Well:

  1. it isn't complicated
  2. doing this will separate your layers. This mean that changes in your datalayer doesn't break your View. In the long run, you'll benefit, as change will come and you'll need to maintain the code.

Upvotes: 2

mm8
mm8

Reputation: 169200

I guess the FancyMethod() contains your business logic and produces a value that you want to display in the view. In this case, FancyMethod() belongs to your model as it contains some business logic that is the same regardless of whether it's being executed in the context of a client application or some other component.

So your model would look something like this, i.e. it accepts an input and produces an output but it doesn't expose any properties that a view may bind to:

public class Model
{
    public string FancyMethod(string input)
    {
        // Use input to calculate output
    }
}

You could then inject your view model with the model and call the FancyMethod when the user executes the command by clicking on the Button in the view:

public class ViewModel
{
    private readonly Model _model;
    public ViewModel(Model model)
    {
        _model = model;
        command = new RelayCommand(Execute, CanExecute);
    }

    public string Input { get; set; }
    public string Output { get; set; }
    public ICommand command { get; private set; }

    private bool CanExecute(object _)
    {
        return !string.IsNullOrEmpty(Input);
    }
    private void Execute(object _)
    {
        Output = _model.FancyMethod(Input);
    }
}

Obviously the view model class should also implement the INotifyPropertyChanged interface and raise change notifications to the view.

In short the business logic belongs to the model and the application logic, for example what happens when a user clicks a Button, belongs to the view model.

Upvotes: 1

CJ Thiele
CJ Thiele

Reputation: 53

I think its not necessary to outsorce the Input and Output properties in another class. The reason for this is that the properties reflect the input and output of the view. So they have to be in the viewmodel. You can outsorce the SomeFancyMethod in a service class to separate the logic from the viewmodel anlogous to mvc.

Upvotes: 0

Related Questions