MVVM - How to keep formatted properties up-to-date? - c#

I am working on a Windows Phone app that uses MVVM, but am struggling with the implementation of MVVM for properties that need to be formatted from the model class to show in the view.
Let's say that I have a simple model class called Person.
public class Person {
public string Name { get; set; }
public DateTime Birthday { get; set; }
}
There is a list of Person objects that are loaded from a locally saved file, and I want to show a list of persons on a list page and then let the user tap on a person to navigate to a details page where there are more details about this person.
On the list page, I want to show the person's birthday as "Birthday: 2/22/1980" (where "2/22/1980" is the person's formatted Birthday)
On the details page, I want to show the person's birthday in a different format: "Eric's birthday is 2/22/1980" (where "Eric" is the person's Name and "2/22/1980" is the person's formatted Birthday).
Normally, I would just create a view model that formats the Birthday properly:
public class PersonViewModel {
private Person person;
public PersonViewModel(Person person) {
this.person = person;
}
public string BirthdayForList {
get {
return "Birthday: " + person.Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public string BirthdayForDetails {
get {
return person.Name + "'s birthday is " + person.Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
}
In order to show these values in the UI, I would create a collection of these view model objects (and bind them to the view):
ObservableCollection<PersonViewModel> Items
Now, what do I do when a person's birthday is updated (somewhere on the details page) and make sure that Items has been updated with up-to-date BirthdayForList and BirthdayForDetails properties, while at the same time saving the Person locally?
I want to keep everything simple and not have to update both the saved list of Person objects and list of PersonViewModel objects manually each time a value needs to be updated.
What is the best way to do this? Should I be using an ObservableCollection of PersonViewModel objects? Also, I have read in several places on this website that the model class should not implement NotifyPropertyChanged.
(Note: I have simplified the problem for this question. You should assume that there are many other ways that I need to format the Birthday property throughout the application as well as other properties from the model class that need to be formatted differently on different pages.)

Why don't simply do the whole thing in xaml and don't use the "calculated properties"?
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}'s birthday is {1:ddd}">
<Binding Path="Person.Name">
<Binding Path="Person.BirthDay">
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Then all you need to do is implement INotifyPropertyChanged in the Person class and raise the event in the setter.
EDIT: I would also recommend using a framework like MVVM light so you can use the ViewModel and ObservableObject base classes for your objects and simply be able to use their implementation of INotifyPropertyChanged
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
RaisePropertyChanged(() => FirstName);
}
}
private string _firstName;

Converters and XAML formatting are good solutions when they work, but sometimes you reall just need to do it in the ViewModel. Typically, you'd need to implement INotifyPropertyChanged and raise the PropertyChanged event for the calculated property when any of its dependencies change.
Managing these dependencies is a royal pain in the ... In fact I got so fed up with this very problem that I an MVVM framework called Catwalk that allows you to do these types of calculated properties in your ViewModel. If you use the framework, you can have code like
public string BirthdayForDetails
{
get
{
return Calculated(() => this.Name + "'s birthday is " + this.Birthday.ToString("ddd", CultureInfo.CurrentCulture));
}
}
Where the base class for model will automatically raise a PropertyChanged event for BirthdayForDetails if either Name or Birthday change. You just have to inherit from ObservableModel and Birthday & Name have to be observable properties like
public string Name
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
If you decide to try it out, let me know what you think.

You have two options:
Just format the date in XAML. Here is an example.
For more complex conversions, you can use converters.
What you should NOT do is store the format in the view model. The format of the data is a view/presentation concern only. So, the benefit of the above approach is that you don't need to keep separate lists just because of formatting.

Putting all your PersonViewModels in an ObservableCollection only solves the issue that your UI needs to update when a new PersonViewModel is added / removed from the collection.
That however does not solve the problem, that one object inside your collection changes. So if the Birthdate of the first person in the list changes, the collection stays the same.
So what you need to achive is to notify the UI that one object inside this collection changed.
You can do so either by letting your ViewModel implement INotifyPropertyChanged or deriving it from DependencyObject (discussion on what's the better solution: INotifyPropertyChanged vs. DependencyProperty in ViewModel).
I'd recommend using INotifyPropertyChanged. Implementing that interface will give you an PropertyChanged event. You need to raise that event everytime one of your properties change. Unfortunately this also requires you to create additional properties in the ViewModel so that you get notified when the changes happen.
The simplest (definitly not the best) way would be to just call OnPropertyChanged for every property that is dependent.
public class PersonViewModel : INotifyPropertyChanged
{
private Person person;
public PersonViewModel(Person person)
{
this.person = person;
}
public DateTime Birthday
{
get { return person.Birthday; }
set
{
person.Birthday = value;
OnPropertyChanged("Birthday");
OnPropertyChanged("BirthdayForList");
OnPropertyChanged("BirthdayForDetails");
}
}
public string Name
{
get { return person.Name; }
set
{
person.Name = value;
OnPropertyChanged("Name");
OnPropertyChanged("BirthdayForDetails");
}
}
public string BirthdayForList
{
get
{
return "Birthday: " + Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public string BirthdayForDetails
{
get
{
return Name + "'s birthday is " + Birthday.ToString("ddd", CultureInfo.CurrentCulture);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I already mentioned that this solution is not the best. You are very likely to add another dependent property and you would have to remember adding the OnPropertyChanged to the Property you depend on.
So you could either use converters for your ViewModel (that now notifies on property change) and remove your calculated properties, or you stick to your properties and find an easier way to mark dependent properties.
Thankfully somebody allready solved the issue of dependent / calculated properties.
When googeling "INotifyPropertyChanged dependent properties" a lot of results show up. One that I really like is this one (Handling INotifyPropertyChanged for dependant properties) because it uses clean and readable attributes to mark the dependencies.
Also there are several MVVM Frameworks out there, that include solutions for said problem.
I hope one of the suggested solutions does help you fix your problem.

You can simply call the PropertyChanged method in the setter of your "person" property
like this
private Person myPerson;
public Person MyPerson
{
get { return myPerson; }
set
{
myPerson = value;
PropertyChanged("MyPerson");
PropertyChanged("BirthdayForList");
PropertyChanged("BirthdayForDetails");
}
}

You can use a combination of multiple elements embedded inside a TextBlock element
<TextBlock Foreground="DarkGray" VerticalAlignment="Bottom" Margin="0,0,0,8"><Run Text="total length "/><Run Text="{Binding TotalHours}" FontSize="48"/><Run Text="h "/><Run Text=":" FontSize="48"/><Run Text="{Binding TotalMinutes}" FontSize="48"/><Run Text="m "/></TextBlock>
Somewhat like this sample https://stackoverflow.com/a/8130843/3214300

Related

How to Store a Temporary Unmodified Model in WPF MVVM

I want to store a temporary version of my Company model within WPF MVVM however I am having issues where even though the temp I create isn't bound to my UI elements, it is still being updated.
Here is what happens when the ModifyCompanyViewModel is instantiated:
public ModifyCompanyViewModel(Company passedCompany)
{
SelectedCompany = passedCompany;
_tempCompany = passedCompany;
CloseWindowCommand = new CloseableCommand<Window>(CloseWindow);
}
So I have a readonly Company named _tempCompany. The UI elements are bound like so:
<TextBox Grid.Row="1" Grid.Column="1" x:Name="NameTextBox" Text="{Binding SelectedCompany.Name, Mode=TwoWay}"/>
Clearly they're bound to the SelectedCompany. If I then type something different in the TextBox that contains the Company's Name, but return the _tempCompany the _tempCompany's name reflects that which I have typed.
How can I store the _tempCompany in a way that no matter what is typed it's name stays the same? I have started using this approach which works:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
But this seems very cumbersome and that I am overlooking an easier way.
The SelectedCompany property and the _tempCompany field reference the same Company object. If you want to store "a temporary version" of the Company object you need to create a temporary version, i.e. you need to create another instance of the Company class like you are currently doing:
_tempCompany = new Company
{
Id = passedCompany.Id,
Name = passedCompany.Name
//Other properties..
};
This is not cumbersome.
As suggested in the comments you could implement the ICloneable interface but this just moves the creation of the other instance to a method within the class that implements the interface. You still need to create another instance somewhere.
How can I implement ICloneable when the model is generated by EF?
Create a partial class and implement the Clone method in this one.
You have to create a viewmodel. Currently Company is a model. Attempting to use it as viewmodel (to bind to its properties) sooner or later will cause you problem, since you mention it's generated.
Consider a simple viewmodel wrapping Company:
public class CompanyViewModel: INotifyPropertyChanged
{
readonly Company _company;
public CompanyViewModel(Company company)
{
_company = company;
}
// now expose something
public string Address
{
get { return _company.Address }
set
{
// tracking changes
// note: you aren't tracking changes made to model instance!
_company.Address = value;
OnPropertyChanged();
}
}
// and here is what you actually want, read-only name
public string Name => _company.Name;
// you can optinally expose model and bind to it properties
// but that wouldn't let you track the changes
// unless model implements INotifyPropertyChanged
public Company Company => _company;
...
}
If you want to edit company name, then just make another property (call it NewName), set its initial value in constructor and decide for yourself when its value will replace _company.Name (e.g. in some method AcceptChanges() which will be called when user finish editing). You will be able to access both: NewName and not yet changed _company.Name to compare them and display confirmation button.

Correct way to implement MVVM Design pattern when using SQLite

So lets say I have a SQLite database of Person's with property Name
Public Class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
And now I have a view with a ListBox Displaying those names
<ListBox ItemSource={Binding People}>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my Views DataContext is PeopleViewVM
Public Class PeopleViewVM
{
Public PeopleViewVM
{
//Do my SQLite stuff,
// Get IEnumerable Person's
// Create Observable Collection of People
}
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people = value;
RaisePropertyChanged();
}
}
}
Now I understand this is a simple example. But I am unsure whether this is the correct implementation of the MVVM design pattern.
If Person my model this means that the view is binding directly to the model when it is binding to the property name. If I change the name of a person in code behind this won't be reflected in the view. What is the correct way to do this example using the MVVM design pattern?
That can be the "correct" implementation, based on your requirements. I wouldn't say that there's a "correct", and "incorrect" for this issue. More like: would it be better for my scenario, or not?
People choose to bind models against view directly based on their requirements, and how they feel. Sometimes I like to simplify my models, and wrap them into "PersonViewModel", in order to expose more relevant properties, and not pollute the Model.
If that doesn't suit you, you can download ReSharper(takes care of "trying" to keep the View & viewmodel synchronized), or alternatively you can encapsulate your model further, and create a "proxy" object, as such:
Public Class PersonViewModel
{
readonly Person _person;
Public PersonViewModel(Person person)
{
_person = person;
}
public string Name
{
get { return _person.Name; }
set
{
_person.Name = value;
RaisePropertyChanged();
}
}
which seems to be pointless, but helps to keep the view and model even more separate, in case of model entities that change often. ReSharper does take care of most cases, in my experience.

Databinding Entity Framework navigation properties - handling change

So I'm building my first larger application and I'm using WPF for Windows and stuff and Entity Framework for retrieving, updating and storing data.So far using a pattern similar to the MVVM pattern, I had a couple of issues but was able to resolve them and am quite far into design.
Also, I'm using database first approach.
But I have just ran into a brick wall that I should have anticipated. It has to do with nested properties in entities and the way changes to them are handled. Let's explain.
For the purpose of simplicity I will not be using my actual class names.
So let's say I have three entities in my EF Model: Department, Manager and PersonalInfo.
I modified my *.tt Template file so that all my entities also implement INotifyPropertyChanged interface, but only for their NON NAVIGATION properties since Navigation properties are declared as virtual and WILL be overridden by EF when their date gets set.
So let's say my generated classes look like this:
public partial class Department : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Department() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
int _someproperty;
public int SomeProperty { get { return _someproperty; } set { _someproperty= value; OnPropChange("SomeProperty"); } }
int _managerid;
public int ManagerID { get { return _managerid; } set { _managerid = value; OnPropChange("ManagerID"); } }
public virtual Manager Manager { get; set; }
}
public partial class Manager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public Manager() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
public virtual PersonalInfo PersonalInfo { get; set; }
}
public partial class PersonalInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChange(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public PersonalInfo() { }
int _id;
public int ID { get { return _id; } set { _id = value; OnPropChange("ID"); } }
string _firstname;
public string FirstName { get { return _firstname; } set { _firstname = value; OnPropChange("FirstName"); } }
string _lastname;
public string LastName { get { return _lastname; } set { _lastname = value; OnPropChange("LastName"); } }
}
Now this works pretty well if I want to let's say display a list of Departments with their Managers. First I load the data into the EF Context like so
Context.Departments.Include(d => d.Manager.PersonalInfo).Load();
Departments = Context.Deparments.Local;
And than in the XAML I can do:
<DataGrid ItemsSource="{Binding Departments}" SelectedItem="{Binding CurrentDepartment, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ID}"/>SomeProperty
<DataGridTextColumn Binding="{Binding SomeProperty }" Header="Property"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.FirstName}" Header="FirstName"/>
<DataGridTextColumn Binding="{Binding Manager.PersonalInfo.LastName}" Header="LastNameName"/>
</DataGrid.Columns>
</DataGrid>
And all of this works wonderfully. I can add and remove items with no problems by simply removing them from Context and saving changes. Since entity sets are ObservableCollections any additions or removal from them automatically raises appropriate events which update the datagrid. I can also modify any nonnavigation property of the Department and can refresh the data in CurrentDepartment like so:
Context.Entry(CurrentDepartment).Refresh();
and it automatically refreshes the data in the datagrid.
Problems start when I change one of the navigation properties. Let's say that I opened a window in which I edited the Department, where I changed the current manager from Bob Bobington to Dave Daveston. When I return to this window calling:
Context.Entry(CurrentDepartment).Refresh();
It will only refresh non navigation properties, and First and Lastname columns will still say Bob Bobington. But that is Refresh function working as intended. But if I load the correct data into the context like this:
Context.Entry(CurrentDepartment).Reference(d=>d.Manager);
Context.Entry(CurrentDepartment.Manager).Reference(m=>m.PersonalInfo);
is still won't change the contents of the first and last name columns since they are still bound to the OLD manager. They will only refresh if the change happens on Bob Bobington instance of PersonalInfo.
I can sort of solve this level of problem by binding the column directly to Manager property, and converting Manager to text either via a ValueConverter or by overriding ToString for Manager. But that won't help since WPF won't ever be notified that Manager property has changed since changes to that property don't raise PropertyChanged event.
Navigation properties can not raise that event since even if I edited the tt template so it generates the code for the navigation property like so:
Manager _manager;
public virtual Manager Manager { get{return _manager;}
set{
_manager=value;
OnPropChange("Manager");
}
}
all this code will likely be overridden by the EF framework itself.
Sooo, what is the best thing to do in these cases? Please don't tell me that conventional wisdom is to copy the data from EF Poco classes into your own and use them. :(
UPDATE:
Here goes a potentially stupid solution for this problem. But it works.
ObservableCollection<Department> tempd = Departments;
Department temp = CurrentDepartment;
Departments = null;
CurrentDepartment = null;
Context.Entry(temp).Refresh();
Context.Entry(temp).Reference(d=>d.Manager).Load();
Context.Entry(temp.Manager).Reference(m=>m.PersonalInfo).Load();
Departments = tempd;
CurrentDepartment = temp;
As you can clearly see the key is in forcing the DataGrid to rebind itself from scratch. This way it will use no shortcuts and will rebind itself properly. BUT this method is quite silly. I shiver at the thought of having to do this to datagrids with hundreds of rows.
So I'm still waiting for a proper solution, but I'll be continuing onwards using this. Something is better than nothing.
Well, conventional wisdom is to copy the data across to another POCO, or at least make your ViewModel class peek through to an underlying Model class. You have combined your Model and ViewModel classes such that Model-based constraints (virtual methods required by your ORM) are interfering with your ViewModel-based constraints (to allow databinding, you must be able to raise events from property setters).
If your Model and ViewModel were properly separated (Separation of Concerns) then you could have your virtual methods and database-required fields on your Model (a DB persistable object) and your purely View-based functions (PropertyChanged events) on your ViewModel. Your DB code should never care about your PropertyChanged events anyway.
You can make it easier by making the ViewModel a look-through class so every property getter-setter looks like:
public string PropertyThing
{
get { return _myModel.PropertyThing; }
set { _myModel.PropertyThing = value; PropChanged("PropertyThing"); }
}
If you're already doing code generation this shouldn't be a major chore.
Alternatively, you could duplicate all the values with something like AutoMapper to separate out your Model and ViewModel to separate classes.
It's not what you wanted to hear, but your ORM and your UI are clashing and that's the sort of thing that MVVM architecture (specifically separating the Model and ViewModel) are supposed to make better.

Update UI when Entity Model changes in ViewModel

I am trying to come up with a good way of implementing the MVVM pattern using Entity-Framework where my entities are my models. My DataContext is my viewmodel. This is a small reproduction of the problem.
View
<TextBox Text="{Binding MyText}" />
ViewModel:
I have the requirement of needing to navigate record by record from my DB. When a button is clicked in the View a command is sent to the Viewmodel that executes nextRecord(). EF does its magic and _myObject is the next row/record from the database
public class myViewModel: INotifyPropertyChanged
{
private MyEntityObject _myObject;
public string MyText
{
get { return _myObject.MyText; }
set
{
if (_myObject.MyText != value)
{
_myObject.MyText = value;
OnPropertyChanged("MyText");
}
}
}
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord() //pseudocode
}
}
Autogenerated Entity Model
public partial class MyEntityObject
{
public string MyText { get; set; }
}
Since the View has no knowledge of _myObject changing, it doesn't update when _myObject changes. A few approaches I have thought of.
I haven't tested wrapping my entities in a INotifyPropertyChanged wrapper class but am wary to do this as I have a lot of entity objects.
I could call OnPropertyChanged("...") for all properties, but some of my entities have a lot of properties to them, which would be ugly. Possible to use reflection to make it cleaner, but I may have properties that aren't databound.
I might be able to defer this to the UI, somehow refreshing the bindings when I click "Next Record", but this breaks MVVM and looks dirty
How can I get the UI to recognize changes from _myObject?
As I've mentioned in the comments, calling OnPropertyChanged("") or OnPropertyChanged(null) invalidates all properties and is equivalent to calling OnPropertyChanged for each and every property. This behavior is also documented here:
The PropertyChanged event can indicate all properties on the object
have changed by using either null or String.Empty as the property name
in the PropertyChangedEventArgs.
This means that you can simply add a call to OnPropertyChanged("") when you update your object to force WPF to reevaluate all bindings to your view model:
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord();
OnPropertyChanged("");
}
That being said, I'd still go with #Anand's solution (+1). There's an ongoing debate on whether it's OK or not for the viewmodel to expose the model as a property, and I tend to go with exposing it until you need to introduce some view model specific logic. Most of the time you won't have to and it's not worth the trouble of wrapping model properties.
The problem with your code is that when _myObject changes the MyText property changed event is not fired. A work around would be to create a new property to hold you entity
and make this property as your Grids DataContext in your view as shown below. Now when this line is executed MyObject = myEntitiesObject.NextRecord() your view will be notified about the change.
public class myViewModel : INotifyPropertyChanged
{
private MyEntityObject _myObject;
public MyEntityObject MyObject
{
get { return _myObject; }
set {
if (_myObject != value)
{
_myObject = value;
OnPropertyChanged("MyObject");
}
}
}
private void _nextRecord()
{
MyObject = myEntitiesObject.NextRecord() //pseudocode
}
}
View:
<Grid DataContext="{Binding MyObject}">
<TextBlock Text="{Binding MyText}"/>
</Grid>
An extremely simple but not very elegant solution that I believe would meet needs: upon switching records, set the DataContext to null, then back to the ViewModel.
However, there are arguably more elegant alternatives that require more work to meet all requirements. See Anand's answer for an improvement upon this.
The tag in View should have the mode and UpdateSourceTrigger attribute set with values.

What is the model in MVVM for?

I have read several articles, tutorials and blog posts about the MVVM pattern. However there is one thing I don't understand. Taking the three "layers":
Model
View
View Model
As far as I have understood MVVM the model contains the "raw" data, e.g. a name and address in case of a Student class. The view model exposes properties to the view which represent data of the model.
Example for a property in the view model
public string Name {
get { return model.Name; }
set { model.Name = value; }
}
Example for the model
private string name;
public string Name {
get { return name; }
set { name = value; }
}
This might sound a bit stupid but doesn't this create a redundancy? Why do I have to keep the name in the model and in the view model? Why should one not handle the name on the view model completely?
In such a simple example, this answer would be yes (it is unreasonably redundant). But, presumably, a page will contain more than just a single Model object. You may have the page state as well as multiple other Model objects which must all be tracked. This is done in the ViewModel.
For example, you may have additional information about the logged in user displayed in a status bar, as well as a service running to detect changes to a text file.
You may also have a form for editing the Student object. If you intend to validate those changes, then you wouldn't want to directly edit the Student object until after the modifications have been verified. The ViewModel can act as a temporary storage location in such a case.
Note on the above: It is not uncommon for validation to occur in the Model, but even then you will probably want the user to be able to enter invalid values while in the process of editing a form. For example, if your Model does not allow a zero-length value in a field, you still want to enable your user to delete the value, move to another field (say, for example, to copy it) then return to the field and finish editing (paste). If you are tied directly to the Model, then your validation logic may not handle this "in-between", "not-yet-finished" state as you'd like. For example, you might not want to accost your user with validation errors until they've finished and clicked 'Save'.
You will also probably have Command objects in the ViewModel to handle button clicks and the like. These would be domain-specific objects that would be useless in a Model.
ViewModels are also useful when you need to filter or somehow temporarily "modify" Model objects to get something useful on the screen. For example, you may want to display a list of all the Users in a system along with a real-time list of the top ten performers among them (updated every 10 seconds). Or you may want to show a list of Reports and a graph showing the overall usage rate, etc. Filtering, sorting and customizing that data would take place within the ViewModel.
The Model, on the other hand, is typically as pure as possible. Ideally, you want only POCOs that (usually) model exactly what's in your persistent storage (database, or what have you). If your persistent storage has FirstName and LastName fields, then so would your Model. Only in your ViewModel would you combine them to get a Name field (either "First Last" or "Last, First" depending on the View's needs).
For example:
namespace Model
{
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Class
{
public string Name { get; set; }
public float Score { get; set; }
}
}
namespace ViewModel
{
public class EditStudentRecordViewModel
{
private Model.Student _student;
private IEnumerable<Model.Class> _studentClasses;
/* Bind your View to these fields: */
public string FullName
{
return _student.LastName + ", " + _student.FirstName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
public IEnumerable<Model.Class> PassingClasses
{
get
{
return _studentClasses.Where( c => c.Score >= 78 );
}
}
public IEnumerable<Model.Class> FailingClasses
{
get
{
return _studentClasses.Where( c => c.Score < 78 );
}
}
public void Save()
{
List<string> l_validationErrors = new List<string>();
if ( string.IsNullOrEmpty( this.FirstName ) )
l_validationErrors.Add( "First Name must not be empty." );
if ( string.IsNullOrEmpty( this.LastName ) )
l_validationErrors.Add( "Last Name must not be empty." );
if ( l_validationErrors.Any() )
return;
_student.FirstName = this.FirstName;
_student.LastName = this.LastName;
Model.Utilities.SaveStudent( _student );
}
}
}
The model is the object graph that contains your business logic.
That's where you hold the behaviour (validation, calculation and such).
The ViewModel is something that models the UI and its interactions.
These are different and have different reasons for existing - the point of the pattern is to separate your display logic to the VVM (View and ViewModel) and have your business logic completely separated.
The view model is where you would keep track of properties that are specific to the view and not necessary to the model.
Let's take your model, assume it's called Person.
And then you create a view model for Person called PersonViewModel, which looks like this:
public class PersonViewModel
{
public Person Person { get; set; }
}
(Note, you might not want to expose the model like this directly, but that's another story)
Now let's say that you have an button in the view which is used to save the Person instance. To provide a better user experience (UX), you want to enable the button only if your model has actually changed. So you implement the INotifyPropertyChanged interface on the Person class:
public class Person : INotifyPropertyChanged
{
...
Now, you could expose a HasUnsavedChanges property from your Person which the Enabled property on the save button would bind to, but that logic has nothing to do with the person.
This is where the view model comes in. You would define this view-specific property on the view model, like so:
public class PersonViewModel
{
public Person Person { get; set; }
public bool HasUnsavedChanges { get; set; }
}
Then, your view model would subscribe to the PropertyChanged event of the INotifyPropertyChanged interface, and toggle the HasUnsavedChanges property on the view model.
Then, if the binding is set up correctly, the save button would enable/disable when any change happens on your model, but your model doesn't have any logic tying it to the view.
Note that you'd have to also implement INotifyPropertyChanged on the view model as well for your view to pick up when changes are made to the view model it is bound to.
Again, the point is acting as a bridge to contain the logic that is a combination of model properties and view properties that don't belong on the model.
Model in MVVM is exactly the same as in MVP or Model2 MVC. It is the one part of MVC-inspired patterns that is not affected by variations on the theme.
Model is the layer which contains repositories, units of work, domain/model objects, data mappers, services and some other structures. All they combined create the model layer, which contains all of the domain business logic for the particular application.
Model is not any single instance. Anyone who tels you otherwise is full of it.
The specific usecases, for which MVVM has been designed, are situation, when you are unable to modify either the model layer or view instances, or both.
P.S. Though, if you are using ViewModel instances as per ASP.NET MVC documentation, then you actually are NOT using MVVM. It is just Model2 MVC with different names for things (where "viewmodels" are actually views and "views" are templates). They kinda messed up when they marketed Rails-like architecture as "MVC".
I've always viewed Models as the "Building Blocks" of the application. They are usually self-contained classes with some properties and perhaps some rudimentary validation or logic for its own properties only.
View Models on the other hand are my actual application classes that end up using the "building blocks" (Models) when building and running the application. They do things like perform advanced validation, process commands, handle events, any kind of business logic, etc.
It should be noted that you don't have to expose your Model's properties in your ViewModel like you have in your example code. Doing so is the "MVVM purist" approach as it completely separates your Model layer from the View layer, however it's also perfectly acceptable to expose the entire Model to the View instead. This is what I typically use in most small projects due to it's simplicity and lack of code-duplication.
public MyModel CurrentModel
{
get { return _model; }
set
{
if (_model != value)
{
_model = value;
RaisePropertyChanged("CurrentModel");
}
}
}
However if there are cases where only a few properties from the Model is needed in the View, or if the project is large enough where I'll want to keep the layers totally separate, then I expose my Model's properties to the View through the ViewModel like you have in your example code.

Categories

Resources