8

I know there are already questions on that topic, but the issues there are somewhat specific to other problems and do not offer conclusive answers.

Especially those here: Question1, Question2 and of course Question3 so please do not close this question too fast. They answer there just say "Do this, do that" and not why!

There are people out there that deny the need for a ViewModel and say that the "standard" way is to bind to the Model directly. This is what I deny and try to prove with technical arguments.

From my background in MVC, MVP, Presentation Model, it just is natural to me to use a ViewModel. Perhaps I miss an important point?

So for me the default is to bind to a ViewModel, no matter what the Model is (and no matter if it implements INotifyPropertyChanged or not).

There are several reasons that I see for binding to ViewModels, including (As mentioned here CodeProject and here on another article)

1. Removing logic from the View

  • Make logic unit testable
  • reduce code redundance (duplication where needed)

2. Security

  • The model contains properties that the user shall not change
  • Automatic, but unwanted updates can happen if Binding to model

3. Loose coupling

  • If binding directly to the model, there will be a coupling between lower layers and View
  • Changing the Model causes changes in all the Views
  • The view is not depending on any given model
  • Model can be easily generated with EF, some DSL, batch files, on so on

4. Speed of development

  • You can start with a Prototype ViewModel hierarchy and bind to that
  • If the model is still under development, you can start with a Prototype Model
  • Model and ViewModel can be developed testdriven, no matter of the View
  • The View can entirely be built by a designer, or developer with strong design background

5. "Tricky synchronizion" is solved

  • There are plenty of solutions to any given "tricky synchronization" problem, e.g.
  • AutoMapper
  • Event system from the model (model fires event, ViewModel subscribes)

6. Equal structure throughout the project

  • There are points where have to a ViewModel, like SelectedItem
  • Mixing Binding to Model and ViewModel is errorprone
  • It is harder for fresh developers to figure out structure of the project
  • starting to bring ViewModel later when there is no way around it is messy

7. Scalability

  • Lets define: If you do not use a ViewModel, it is not MVVM
  • MVVM can be easily adopted to lots of datasources, lots of views
  • If you find any performance issues: Lazy loading and caching goes in the ViewModel

8. Separation of concerns

  • Getting a grip on complexity is the main problem in software
  • The ViewModels sole responsibility is pushing changes
  • It is as easy to send notifications to a view as it is to push it to a different process or machine
  • The ViewModel, not the View register for change notifications on the model / data source
  • the datasource can ignore sending events to the ViewModel that caused the change

On contrary, the guy from the other thread dumps some points, including

  1. If the model is updated directly, the view-model won't know to fire a property changed event. This causes the UI to go out of sync. This severely limits your options for sending messages between parent and child view-models.

  2. If the model has its own property changed notification, #1 and 2 aren't a problem. Instead, you have to worry about memory leaks if the wrapper VM goes out of scope but the model doesn't.

  3. If your models are complex, with lots of children objects, then you have to walk the entire tree and create a second object graph that shadows the first one. This can be quite tedious and error prone.

  4. Wrapped collections are particularly difficult to work with. Any time something (UI or backend) inserts or removes an item from a collection, the shadow collection needs to be updated to match. This kind of code is really hard to get right.

So, the question is: what is the default way to bind and why?

Do I miss points that make it necessary to have a ViewModel?

Are there any real reasons one would like to bind to a model?

Most important is the why, not the how to.

Community
  • 1
  • 1
Mare Infinitus
  • 8,024
  • 8
  • 64
  • 113
  • There is a huge difference between "denying the need for a ViewModel" and never binding directly to a model. – Jonathan Allen Jun 05 '13 at 19:50
  • @JonathanAllen I'm excited to see your point of why you see binding to models directly the "default case" and "the pattern that most of your code is supposed to follow." – Mare Infinitus Jun 05 '13 at 19:54
  • I just posted my example code. If you want to change my mind you have to demonstrate a wrapper for it that isn't a huge mess. To date I have found no one that has managed to accomplish that. – Jonathan Allen Jun 05 '13 at 20:34
  • I would like to add that 3 hours in, I'm still the only one who actually posted any code samples. – Jonathan Allen Jun 05 '13 at 22:06
  • @JonathanAllen Probably that question can not be answered with posting some code samples. Actually this question is not about code, but about organizing your code. – Mare Infinitus Jun 06 '13 at 06:06
  • 1
    Nothing you say has any meaning unless it can be supported with code. Until you actually prove your ideas with actual examples, it is nothing but speculation. – Jonathan Allen Jun 06 '13 at 17:30

6 Answers6

6

View models usually contains members, which are intended to be used with view (e.g., properties like IsSomethingSelected, IsSomethingExpanded, IsSomethingVisible, any implementation of ICommand).

Do you see any reason to bring all those stuff into models? Of course, no. That's why view models exists.

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • Have edited the question to include this point. – Mare Infinitus Jun 05 '13 at 18:30
  • 2
    Right. But that doesn't mean that models are never data-bound to views. That just means models aren't the only thing bound to views. – Jonathan Allen Jun 05 '13 at 21:20
  • @JonathanAllen: of course. Bind to model often convenient. The question can be separated into two parts: 1) why to use view models? 2) should one bind to model? I've answered on part 1. :) – Dennis Jun 06 '13 at 05:12
5

So, the question is: what is the default way to bind and why?

In general, I would argue that having a ViewModel, and binding to it, is the default. There's a reason that "ViewModel" exists and is part of the MVVM pattern.

There are other reasons a ViewModel is necessary other than purely the data. You also typically implement application-specific logic (ie: not part of the model, but required in the application). Any ICommand implementation, for example, really should be on a ViewModel, since it's completely unrelated to the Model.

Are there any real reasons one would like to bind to a model?

It can be simpler in some cases, especially if your model already implements INotifyPropertyChanged. Reducing code complexity is a valuable goal with its own merit.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • The other guy sees ICommand as leaving the MVMM Pattern, which I do not. It is just another extension and encapsultation for me. And: Is Binding to both model and ViewModel not even more complex than having a uniform design throughout the project? – Mare Infinitus Jun 05 '13 at 18:30
  • @MareInfinitus Yes - `ICommand` has always been a core element in MVVM. I'd actually consider designs trying to completely remove `ICommand` as MV-SomethingElse.. ;) – Reed Copsey Jun 05 '13 at 18:33
  • 3
    @MareInfinitus As for binding to both Model and ViewModel - it can be, though it's often simpler. A good example is an ItemsControl - it's often nice to have your ViewModel expose an `ObservableCollection` and bind directly to it instead of trying to build a collection of wrapped models, etc. – Reed Copsey Jun 05 '13 at 18:34
  • Yes, there are points, especially with POCO Models and one-way Binding. But, even in this case, I typically wrap it up. Sometimes I hold a reference to a model then. Do you see points that I did not mention in my question? – Mare Infinitus Jun 05 '13 at 18:37
  • 6
    @MareInfinitus Not really - at the end of the day, it's really up to the architect on the project. There's no "one right way" to do this. – Reed Copsey Jun 05 '13 at 18:37
  • But I see lots of reasons to choose one way over the other. – Mare Infinitus Jun 05 '13 at 18:40
3

Counter-arguments:

  1. Removing logic from the View

Removing logic from the view-model is likewise useful. By pushing logic like validation, calculated fields, etc. into the model you are left with a lighter, cleaner view-model.

•Make logic unit testable

The model itself is really easy to unit test. You don't have to worry about mocking libraries and whatnot like you would with a view-model that deals with external services.

•reduce code redundance (duplication where needed)

Multiple view-models can share the same model, reducing redundancies for validation, calculated fields, etc.

  1. Security •The model contains properties that the user shall not change

Then don't expose them on the UI.

•Automatic, but unwanted updates can happen if Binding to model

None of this one makes any sense. If your VM is just a wrapper around your model, it is just going to push those updates down anyways.

  1. Loose coupling •If binding directly to the model, there will be a coupling between lower layers and View

That coupling doesn't magically disappear when you shove a wrapper VM between them.

•Changing the Model causes changes in all the Views

Changing the Model causes changes in all of the wrapper view-models. Changing the view-model also causes changes in all of views. Thus the Model can still cause changes in all of the views.

•The view is not depending on any given model

That is true with or without the view-model wrapping the model. It only sees properties, not actual classes.

•Model can be easily generated with EF, some DSL, batch files, on so on

Yep. And with a little bit of work those easily generated models can include useful interfaces like INotifyDataErrorInfo, IChangeTracking, and IEditableObject.

  1. Speed of development

Binding to models offers faster development because you don't have to map all the properties.

•You can start with a Prototype ViewModel hierarchy and bind to that

Or I can start with a prototype model. Nothing is gained by adding a wrapper.

•If the model is still under development, you can start with a Prototype Model •Model and ViewModel can be developed testdriven, no matter of the View

Again, nothing is gained by adding a wrapper around the model.

•The View can entirely be built by a designer, or developer with strong design background

Yet again, nothing is gained by adding a wrapper around the model.

  1. "Tricky synchronizion" is solved •There are plenty of solutions to any given "tricky synchronization" problem, e.g. •AutoMapper

If you use automapper to copy the data into the view-model then you aren’t using the MVVM pattern. You are just using Views and Models.

•Event system from the model (model fires event, ViewModel subscribes)

Hello memory leaks. That is, unless you are incredibly careful and give up the ability to share a model across multiple views.

  1. Equal structure throughout the project •There are points where have to a ViewModel, like SelectedItem

Irrelevant. No one is arguing against non-wrapper view-models.

•Mixing Binding to Model and ViewModel is errorprone

Not supported.

•It is harder for fresh developers to figure out structure of the project

Not supported.

•starting to bring ViewModel later when there is no way around it is messy

Irrelevant. Again, no one is arguing against also using non-wrapper view-models.

  1. Scalability •Lets define: If you do not use a ViewModel, it is not MVVM

Irrelevant. For the third time, no one is arguing against also using non-wrapper view-models.

•MVVM can be easily adopted to lots of datasources, lots of views

Irrelevant. We are not arguing about whether or not to use MVVM, we are arguing about how best to use it.

•If you find any performance issues: Lazy loading and caching goes in the ViewModel

Agreed, but irrelevant. No one is suggesting that you shove service calls into the model.

  1. Separation of concerns

This is where the wrapper view-model falls down the hardest.

The view-model already has to deal with UI-data (e.g. modes, selected items) and host the ICommands that call out to external services.

Shoving all of the model data, validation logic, calculated properties, etc. into the view-model makes is even more bloated.

kmote
  • 16,095
  • 11
  • 68
  • 91
Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • 1
    Please see that I do not have the critic badge. Eventually I will answer to all those points later. – Mare Infinitus Jun 06 '13 at 05:40
  • You warn about memory leaks when the VM subscribes to events from the Model. (Likewise in [your related article](https://www.infoq.com/articles/View-Model-Definition).) Please elaborate. In my designs (what I consider to be typical desktop-based applications) the View, Model, and Model View all have the same lifetime. I don't see how this arrangement is susceptible to leaks. Can you enlighten me? – kmote Dec 29 '16 at 22:04
  • Under the MVVM design pattern, a single Model can be shared across multiple views simultaneously. For example, the model may appear in a list view and in a separate window that is showing the details of the selected item in the list. If you are 100% sure that only one VM will ever see a given model, and 100% sure that will never change in the future, then yes you can have the VM listen to events on the model. – Jonathan Allen Jan 02 '17 at 20:06
2

There is no "correct" answer to your question. WPF will, of course, happily allow you to bind to whatever you have declared to yourself to be a "Model" object; the framework simply doesn't care. You don't always have to follow the MVVM pattern just because you're doing an application in WPF. Context is always key to any software you write. If you are hard-pressed for time and need a quick-out for your prototype, by all means bind to the Model and refactor if/when you need to.

So I think what you're really asking is "when should I use the MVVM pattern?"

The answer is, of course, "when your problem fits the pattern".

So what does the MVVM pattern give you? You've already listed several reasons to use MVVM, but the most important one for the pattern is loose coupling -- all the rest sorta come with that.

The entire point of the MVVM pattern is to ensure that your model is a state machine that knows nothing about how data is presented to or obtained from a user. In a way, your model is pure data structured in the format that makes sense for the model rather than structured in a format that makes sense to a human being. Your ViewModel is responsible for translating between that pure data platform (the Model) and a user input platform (the View). The ViewModel is tightly coupled to both the View and the Model, but the important thing is that the Model knows nothing of the View.

Randolpho
  • 55,384
  • 17
  • 145
  • 179
  • 1
    You are right, loose coupling seems to be the central point. – Mare Infinitus Jun 06 '13 at 05:39
  • In addition: Refactoring is no reason to drop all that proved to be good in former projects. I think I can develop faster if I stick to MVVM and use a framework like caliburn micro instead of binding to models. Can you perhaps elaborate on when you see a reason to use MVVM and why you want to keep your model from knowing the view (as proposed in another answer, where the model even makes the multilingual error messages). – Mare Infinitus Jun 06 '13 at 05:51
  • Regarding refactoring, I think you may have misunderstood. What I meant was that if you don't have time for a View Model and can build your app by binding directly to the Model, feel free to do so, you can always refactor later *when* (or even *if*) you need to. I wasn't attempting to claim that you should always refactor just for the sake of refactoring. – Randolpho Jun 06 '13 at 18:16
  • Regarding a framework and development speed, I would say do what works for you, but I would caution against using speed as the only reason to do anything. Sometimes a little extra effort now can pay off a lot 6 months later. – Randolpho Jun 06 '13 at 18:17
  • Regarding when to use MVVM, I would say that I personally prefer a domain-driven design approach, where your Model represents a generalized, "pure", or abstract definition of what your product is attempting to accomplish. Not all problems fit this model, of course, but most of the problems I personally deal with do. In my realm of experience, the UI presents a view into a small fraction of a much larger system. When you have such a situation, MVVM is an extremely useful pattern as it helps reduce the complexity of your larger system -- primarily by pushing that complexity into the ViewModel. – Randolpho Jun 06 '13 at 18:21
  • If you hadn't closed the question, even more aspects would come up. This is not a discussion, but a somewhat complex question. Back on topic: The time pressure argument is what you said is a reason to bind to models. For me the model is what the business aspect of the application is, but in an abstract form. Anything that brings that to the view goes in the viewmodel. And anything that makes the view want to interact with the model goes to the ViewModel (like Commands, SelectedItem, Queries). But this is just what is my taste (with the reasons in the question). Wanted to see other aspects. – Mare Infinitus Jun 06 '13 at 21:04
  • Re: Refactoring. The problem for me is understand the "do not have time for a ViewModel" part. To me this situation is just far-fetched. But okay, in a real world situation things would be talked out in 15 minutes I believe. – Mare Infinitus Jun 06 '13 at 21:16
1

I agree with Reed-- the ViewModel is to what one ought to bind. I always imagine the Model to be a more or less static set of values which might not change as frequently or dynamically as the ViewModel. Generally, I try to put anything with a value which can be assumed at compile-time in the Model, and anything which would be determined at runtime in the ViewModel.

The View itself shouldn't have anything more than the barest of bare-bones logic. The rest should be references to the ViewModel. Security is sometimes the issue, but I like to do it simply for code readability and conciseness. It's a whole lot easier to deal with code when all the aesthetic things are done in the view, all the more mathematical, logical things are hidden away in the ViewModel, and all the hard data is in a separate Model.

MVVM is also very closely related to MVC, with the guiding principle that the Model and the View should never see each other directly. Once again, for me it's a clarity thing. The logic that determines how the Model's values ought to change should also be in the ViewModel/Controller. The View shouldn't think for itself.

Think of the View like a receptionist: it's a friendly face that interfaces ("talks to") the user. The ViewModel is the accountant in the office behind the door next to the front desk, and the Model is his/her set of reference books and notes. If the receptionist starts writing in the margins of the accountant's books, erasing the accountant's notes, and changing things in the records, things start to get confusing.

Community
  • 1
  • 1
Eric Dand
  • 1,106
  • 13
  • 37
  • Nice example. But the model can and will change from whatever reason and source it will be. But the Compile time / runtime thought is what I follow, too. And: If it could be useful to another application (like any datasource), it will be part of the model. – Mare Infinitus Jun 05 '13 at 19:01
  • -1 because the suggestion that the Model is "more or less static" represents an exceptionally narrow understanding of model development. – kmote May 11 '17 at 03:33
1

Here is a simple object graph. Just some really simple models with normal property change and validation events.

Those of you who think models need to be wrapped in view-models show your code.

public class ModelBase : INotifyPropertyChanged, INotifyDataErrorInfo
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

        OnErrorChanged(propertyName);
    }

    protected void OnErrorChanged(string propertyName)
    {
        if (ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
    }

    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

    public virtual IEnumerable GetErrors(string propertyName)
    {
        return Enumerable.Empty<string>();
    }

    public virtual bool HasErrors
    {
        get { return false; }
    }
}

public class Customer : ModelBase
{
    public Customer()
    {
        Orders.CollectionChanged += Orders_CollectionChanged;
    }

    void Orders_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems.Count > 0)
            foreach (INotifyPropertyChanged item in e.OldItems)
                item.PropertyChanged -= Customer_PropertyChanged;

        if (e.NewItems.Count > 0)
            foreach (INotifyPropertyChanged item in e.NewItems)
                item.PropertyChanged += Customer_PropertyChanged;

        OnPropertyChanged("TotalSales");
    }

    void Customer_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Total")
            OnPropertyChanged("TotalSales");
    }

    public decimal TotalSales
    {
        get { return Orders.Sum(o => o.Total); }
    }

    private string _FirstName;
    public string FirstName
    {
        get { return _FirstName; }
        set
        {

            if (_FirstName == value)
                return;
            _FirstName = value;
            OnPropertyChanged();
        }
    }

    private string _LastName;
    public string LastName
    {
        get { return _LastName; }
        set
        {

            if (_LastName == value)
                return;
            _LastName = value;
            OnPropertyChanged();
        }
    }



    private readonly ObservableCollection<Order> _Orders = new ObservableCollection<Order>();
    public ObservableCollection<Order> Orders
    {
        get { return _Orders; }
    }

}

public class Order : ModelBase
{
    public Order()
    {
        OrderLines.CollectionChanged += OrderLines_CollectionChanged;
    }

    void OrderLines_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.OldItems.Count > 0)
            foreach (INotifyPropertyChanged item in e.OldItems)
                item.PropertyChanged -= OrderLine_PropertyChanged;

        if (e.NewItems.Count > 0)
            foreach (INotifyPropertyChanged item in e.NewItems)
                item.PropertyChanged += OrderLine_PropertyChanged;

        OnPropertyChanged("Total");
        OnErrorChanged("");
    }

    public override bool HasErrors
    {
        get { return GetErrors("").OfType<string>().Any() || OrderLines.Any(ol => ol.HasErrors); }
    }

    void OrderLine_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Extension")
            OnPropertyChanged("Total");
    }

    public decimal Total
    {
        get { return OrderLines.Sum(o => o.Extension); }
    }

    private int _OrderNumber;
    private DateTime _OrderDate;

    public DateTime OrderDate
    {
        get { return _OrderDate; }
        set
        {
            if (_OrderDate == value)
                return;
            _OrderDate = value;
            OnPropertyChanged();
        }
    }
    public int OrderNumber
    {
        get { return _OrderNumber; }
        set
        {
            if (_OrderNumber == value)
                return;
            _OrderNumber = value;
            OnPropertyChanged();
        }
    }

    private readonly ObservableCollection<OrderLine> _OrderLines = new ObservableCollection<OrderLine>();
    public ObservableCollection<OrderLine> OrderLines
    {
        get { return _OrderLines; }
    }

}

public class OrderLine : ModelBase
{
    private string _ProductName;
    private decimal _Quantity;
    private decimal _Price;
    public decimal Price
    {
        get { return _Price; }
        set
        {
            if (_Price == value)
                return;
            _Price = value;
            OnPropertyChanged();
        }
    }
    public string ProductName
    {
        get { return _ProductName; }
        set
        {
            if (_ProductName == value)
                return;
            _ProductName = value;
            OnPropertyChanged();
            OnPropertyChanged("Extension");
        }
    }
    public decimal Quantity
    {
        get { return _Quantity; }
        set
        {
            if (_Quantity == value)
                return;
            _Quantity = value;
            OnPropertyChanged();
            OnPropertyChanged("Extension");
        }
    }
    public decimal Extension
    {
        get { return Quantity * Price; }
    }

    public override IEnumerable GetErrors(string propertyName)
    {
        var result = new List<string>();

        if ((propertyName == "" || propertyName == "Price") && Price < 0)
            result.Add("Price is less than 0.");
        if ((propertyName == "" || propertyName == "Quantity") && Quantity < 0)
            result.Add("Quantity is less than 0.");

        return result;
    }

    public override bool HasErrors
    {
        get { return GetErrors("").OfType<string>().Any(); }
    }
}

And here is a typical ViewModel that would go with it:

public class CustomerViewModel : ModelBase
{
    public CustomerViewMode()
    {
        LoadCustomer = null; //load customer from service, database, repositry, etc.
        SaveCustomer = null; //save customer to service, database, repositry, etc.
    }

    private Customer _CurrentCustomer;

    public Customer CurrentCustomer
    {
        get { return _CurrentCustomer; }
        set
        {
            if (_CurrentCustomer == value)
                return;
            _CurrentCustomer = value;
            OnPropertyChanged();
        }
    }
    public ICommand LoadCustomer { get; private set; }
    public ICommand SaveCustomer { get; private set; }

}
Jonathan Allen
  • 68,373
  • 70
  • 259
  • 447
  • A simple example, a simple thought: Would you want to make your Model multilingual? – Mare Infinitus Jun 05 '13 at 20:39
  • Re your comment on the question: This Model is difficult to wrap, because it has some major flaws. 1. language dependent, 2. derived attributes, 3. mixed error handling and data storage 4. not normalized (productname in order?) 5. incomplete (where is the check of your stock?). It is big enough to see some flaws, yet small enough to say "hey, just an example". This is not a really good start for a discussion. I would have made some POCO classes for the model and define a set of rules for the error handling. Any derived attributes and language strings go in the ViewModel. No need to show code! – Mare Infinitus Jun 05 '13 at 20:54
  • Good question. Instead of returning strings I would return error codes. Those codes would then be converted into language-specific strings using an IValueConverter. – Jonathan Allen Jun 05 '13 at 21:02
  • Or I would add multilingual support directly to the model. This would be trivial if I can just use the Thread.CurrentUICulture to leverage the Resource Manager. – Jonathan Allen Jun 05 '13 at 21:02
  • 1
    @MareInfinitus - Just as I thought, you can't do it. Your design only works with simple data transfer objects. Show actual models created using Domain Driven Design practices and you're lost. – Jonathan Allen Jun 05 '13 at 21:15
  • Not at all. My models are POCO and usually get generated. For me your model is just flawed and I would never even think about such an approach. I could write a wrapper for that, but do not see why I should, I would instead rewrite the model and introduce a viewmodel which takes about the same time. But it gets interesting, you presented your way of handling complexity (which seems to be "one model to rule them all"). I think about setting a bounty on that question. – Mare Infinitus Jun 05 '13 at 21:24
  • My models are "plain old C# objects" as well. I think you are confusing that term with "Data Transfer Object", which generally means a class that is nothing but simple properties. – Jonathan Allen Jun 05 '13 at 21:58
  • No, I use DTOs a lot when they make sense. For example if I use WCF. But again, those are generated then. – Mare Infinitus Jun 06 '13 at 05:48