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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With