5

So let's say I have a view model, representing a student. The view model is corresponding to a student model, from which the data comes. What I am struggling with, is how to populate the fields in the view model. Do I simply forward reference the fields from the model (where possible) or do I have actual fields in the view model, which I then populate?

Example A - Forward referencing

public class StudentViewModel
{
    public StudentModel Student { get; set; }

    public string FirstName 
    {
        get { return Student.FirstName; }
    }

    public string LastName
    {
        get { return Student.LastName; }
    }

    // ...and so on
}

Example B - Using actual fields

public class StudentViewModel 
{
    public string FirstName { get; private set; }

    public string LastName { get; private set; }

    public void SetStudentProperties(StudentModel student) 
    {
        FirstName = student.FirstName;
        LastName = student.LastName;
    }
}

So obviously both of these examples are overly simplified and I have left out stuff like INotifyPropertyChanged, but I think you get the point. Are any of my approaches viable, or have I completely misunderstood the intend of view models?

I also considered using the constructor for input, but this makes me struggle with the depency injection framework (I am using Caliburn.Micro).

3
  • 1
    Just bind to Student.FirstName and Student.LastName directly. Jul 9, 2019 at 21:57
  • 1
    A ViewModel which simply displays some data from a datasource would be much different from a ViewModel which presents parts of a Model to the user for editing. So there's no one single approach.
    – GHP
    Aug 2, 2019 at 19:27
  • 1
    beware that binding directly to classes that don't implement INotifyPropertyChanged will lead to memory leaks in WPF stackoverflow.com/questions/61772492/…
    – Dtex
    Apr 20, 2022 at 10:40

4 Answers 4

1

What problem are you trying to solve by duplicating the StudentModel properties in the StudentViewModel? Adding this type of facade wrapper does nothing except bloat the code base. Just include the StudentModel instance as a (read only) property of the StudentViewModel and bind directly to its properties.

Take a look at my blog posts concerning the division of labour between Model and ViewModel classes.

2
  • 1
    Obviosuly in the real use, there might be some derivative properties. A classic example could be a property for Age, which would be derived from DateOfBirth and only be part of the view model. Jul 3, 2019 at 12:25
  • 1
    My blog posts include a methodology for handling such "non-data" properties.
    – Peregrine
    Jul 3, 2019 at 12:46
1

Focus on Responsibilities.

A view model has a particular job. You've already resolved to depend on the model in order to do that job, but the details of the connection between the two only matter insofar as they support the view model's ability to do what it needs to do.

In your specific examples, one is sensitive to changes in the model and one is not. Will the model change? If so, deciding how or if the viewmodel properties should behave will inform the choice of implementation. If the model won't change, the approaches are effectively identical to a consumer. In that case, focus on other concerns such as simplicity.

1

I favor the "A. Forward referencing."

Its API need not be changed when StudentModel is changed.

With a private getter for the StudentModel though, as otherwise the entire ViewModel becomes moot.

"B. Using actual fields" makes a copy (extra resources) of the StundentModel fields, whether actually used or not. Like depending fields say depending on Student.UsesMensa.

And finally, there are cases when a ViewModel can be the Model itself. Mind, it is a very bad idea to let a customer specified report go against a database Model, as then you tie your database fixed, restricting database changes, and exposing technical fields. But for an internal report the boiler plate code just is senseless; indeed harmful to ease of development. Again it is an other case if in a report the fields are evaluated at run-time: "... ${student.firstname} ...". Then you want some ability to verify all fields used, when StudentModel changes - one way is to have a StudentModelView class.

1

I completely misunderstood the intent of view models?

Ideally, all layers would have a different data model. A model that best meets the layer's needs. So, regardless of the stack, the purpose of the view model is to structure the data in the most optimal way for the view layer. Optimal, according to specific needs and requirements (functional and non-functional) for the view.

For example, the persistence model is designed to take advantage of the underlying storage capabilities and to fulfil the needs for querying, throughput, memory optimization, data access, data life cycles, etc.

These needs and capabilities change when data is in-memory. It's reasonable to load the persistent model and map it to one optimized for in-memory process and allocation. For example, while user is stored as a record with 40 columns and several 1-to-many relationships, it doesn't mean that in-memory representations of user should hold so much data. In some operations, the user can be represented as the tuple id - email. In such cases, user can be modelled as a tuple (2p array), as a Map or something else. The consuming layer dictates.

The view is not an exception. Given a "foreign model", the view model represents the same data in the way that best fits the needs of the view. Usually, it does this by flattening complex data structures, formatting data by types or combining data into new fields. But overall, eliminating waste (unnecessary data). All these things with requirements in mind: memory optimization, fast rendering, effective binding, ergonomy, user-friendly data representation, exportability to other formats, etc.

Are any of my approaches viable

Both are, the question is How do they serve your purpose for the view?. Or How do they meet the requirements and needs of the view layer?. If both are exchangeable, then which is the simplest? or which adds unnecessary complexity?

Be aware of the Delegation pattern.

Option B is known as Delegation pattern. This pattern makes a noticeable impact on the memory footprint (for worse). Careless implementations constrain the scalability of the application. For two reasons: because of memory leaks and because it runs out of memory as load and concurrency increase.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.