Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should the ViewModel refer to its Models properties?

As the ViewModel has the job to "prepare" the Model's properties to get displayed in the View, what is the best way of referring to the underlying Models properties from the ViewModel?

I could think about two solutions by now:


Option 1 - Duplicate the Model's properties in the ViewModel (wrapper-approach)

Architecture

class Model
{
    public string p1 { get; set; }
    public int p2 { get; set; }
}

class ViewModel : INotifyPropertyChanged
{
    // Model-instance for this ViewModel
    private Model M;

    public string p1 
    {
        get { return M.p1; }
        set 
        { 
            M.p1 = value; 
            // assuming View controls are bound to the ViewModel's properties 
            RaisePropertyChanged("p1");  
        }
    }

    // let's say, I only want to check a Checkbox in the View,
    // if the value of p2 exceeds 10.
    // Raising the property changed notification would get handled
    // in the modifying code instead of the missing setter of this property.
    public bool p2
    {
        get 
        { 
            if (M.p2 > 10)
            { return true; }
            else
            { return false; }
        }
    }

    // Initialize the Model of the ViewModel instance in its c'tor
    public ViewModel()
    { M = new Model(); }
}

Binding

<Textbox Text="{Binding p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding p2, Mode=OneWay}"/>    

Advantages

  • Full control about how the Model's properties are displayed on the View as shown in p2: int gets converted to bool on demand.
  • Changes of the properties of the ViewModel could be raised individual, might be a little performance increase compared to option 2.

Disadvantages

  • Violation of DRY.
  • More Code to write/maintain.
  • Modifications to the Model/ViewModel could easily become shotgun surgery.

Option 2 - Treat the whole Model as property of the ViewModel

Architecture

class Model
{
    public string p1 { get; set; }
    public int p2 { get; set; }
}

class ViewModel : INotifyPropertyChanged
{
    // Model instance for this ViewModel (private field with public property)
    private Model _M;
    public Model M 
    { 
       get { return _M; }
       set 
       {
           _M = value;
           // Raising the changing notification for the WHOLE Model-instance.
           // This should cause ALL bound View-controls to update their values,
           // even if only a single property actually got changed
           RaisePropertyChanged("M");
       } 
    }

    // Initialize the Model of the ViewModel instance in its ctor
    public ViewModel()
    { M = new Model(); }
}

Binding

<Textbox Text="{Binding M.p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding M.p2, Mode=OneWay, Converter={StaticResource InverseBooleanConverter}"/>

Advantages

  • Can save a lot of code.
  • Reduces complexity.
  • Increases maintainability.

Disadvantages

  • In this approach, the ViewModel is nothing more than a continuous-flow water heater for the Models properties, except for some possible interaction logic for the View.
  • No control about how the Model's properties are displayed in the View - which ultimately leads to total needlessness of the ViewModel and implementation of conversion logic in the View.
like image 780
M463 Avatar asked Jul 15 '15 10:07

M463


People also ask

Should ViewModel know about model?

Models are just the plain data, and a ViewModel is something that acts like a padding in between the two, that it should get information from the Model and pass it onto the View, and the View should know how to present it.

What logic should be in ViewModel?

ViewModel: ViewModel is the middle layer between the view and model. ViewModel contains the business logic, which manipulates the row data to show in the view. Any kind of function and methods should be in the view model. The iNotifyPropertyChanged interface is used in the ViewModel to achieve two-way binding.

What is ViewModel and model?

A model is usually more closely related to how your data is stored (database, services, etc.) and the model will closely resemble those. The ViewModel on the other hand is closely related to how your data is presented to the user. It is usually a flatten version of your model, denormalized, etc.


1 Answers

It is the responsibility of your ViewModel to expose the Model to the View, you should not expose the Model's properties as additional properties in the ViewModel, instead, your View should bind directly to the model.

Additionally, it isn't wrong to have logic in your Model, in fact, it makes more sense to contain model related code within the model, as opposed to the ViewModel.

Here is an example:

public class Movie
{
    private string _Name;

    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;

            //Notify property changed stuff (if required)

            //This is obviously a stupid example, but the idea
            //is to contain model related logic inside the model.
            //It makes more sense inside the model.
            MyFavourite = value == "My Movie";
        }
    }

    private bool _MyFavourite;

    public bool MyFavourite
    {
        get { return _MyFavourite; }
        set
        {
            _MyFavourite = value;

            //Notify property changed stuff.
        }
    }
}

So to answer your question a little more directly, you should expose your model in the view model as a property.

public class ViewModel
{
    private Movie _Model;

    public Movie Model
    {
        get { return _Model; }
        set 
        { 
            _Model = value;

            //Property changed stuff (if required)
        }
    }

    ...
}

Therefore, your View will bind to the Model property, like you have already done so.

EDIT

In the example for casting to the type, you can implement a read-only property in your Model, like so:

public bool MyBool
{
    get 
    { 
        return MyInt > 10; }
    }
}

Now the magic here would be that you will need to call the INotifyPropertyChanged for this property whenever MyInt changes. So your other property would look something like this:

public int MyInt
{
   get { ... }
   set 
   {
       _MyInt = value;

       //Notify property changed for the read-only property too.
       OnPropertyChanged();
       OnPropertyChanged("MyBool");
   }
}
like image 144
Mike Eason Avatar answered Nov 15 '22 16:11

Mike Eason