MVVM model responsibility

307 views Asked by At

This is a basic MVVM question.

Lets say that i have a student editor window which allows the user to set the student's payment method (cash or cheque). For flexibility, the possible payment methods must be retrieved from the server and the list varies on the student's age which can also be changed.

The questions are:

  1. where should the possible payments methods be stored? Model or view model?

  2. if model, when the user changes the age who should download the new list of payment methods?

what should the model contain and do in MVVM?

2

There are 2 answers

2
Eduardo Bonet On

Well, in my opinion the model should contain the list of payment methods, while the VM should contain a list to bind to the view. Example, what I would do is to have a List<PaymentOptions> on the model and a BindingList<PaymentOptions> or ObservableCollection<PaymentOptions> for the VM.

2
Gayot Fow On

The Model is the logical place to house payment methods and the business rules associated with each method. One approach is to use an enumeration that describes each payment method and this is queried using 'switch' statements.

An alternate design incorporates the strengths of polymorphism, and might look like this...

public class Model
{
    private readonly List<PaymentMethod> _allPaymentMethods; 
    public Model()
    {
        // get payment types from the db
        // to populate master list
        _allPaymentMethods = new List<PaymentMethod> {new Cash(), new CreditCard()};
    }
    public List<PaymentMethod> GetPaymentMethods(int age)
    {
        List<PaymentMethod> result =
            _allPaymentMethods.Where(q => q.Age == age).ToList();
        return result;
    }
}
public abstract class PaymentMethod
{
    public string Name { get; protected set; }
    public int Age { get; protected set; }
    public abstract void ProcessPayment();
    public override string ToString()
    {
        return Name;
    }
}
public class CreditCard:PaymentMethod
{
    public CreditCard()
    {
        Name = "Credit Card";
        Age = 25;
    }
    public override void ProcessPayment()
    {
        Console.WriteLine("Thanks for using your card");
    }
}
public class Cash:PaymentMethod
{
    public Cash()
    {
        Name = "Cash";
        Age = 22;
    }
    public override void ProcessPayment()
    {
        Console.WriteLine("Thanks for paying cash");
    }
}

This sample hard codes two methods: Cash and Credit Card, and each class knows how to do its work while relying upon inheritance to handle the common attributes. This approach avoids the 'switch' and encapsulates all of the business rules and methods within each class. So the Model exposes only what the ViewModel needs to know in order to present the various payment methods to the user in an items control.

When the user changes their age, your VM can refresh the list.

A code snippet for the VM looks like...

public class ViewModel :INotifyPropertyChanged
{
    public ObservableCollection<PaymentMethod> Methods { get; set; }
    public ViewModel()
    {
        Model m = new Model();
        Methods = new ObservableCollection<PaymentMethod>(m.GetPaymentMethods(22));
    }
    #region INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        var handler = System.Threading.Interlocked.CompareExchange
               (ref PropertyChanged, null, null);
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

Whichever approach you use (either enumeration or polymorphism), the rule of thumb is "Does the VM absolutely need to know about this? Can I leverage the OO strengths of inheritance and polymorphism into my architecture?"