Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.)

like image 541
epaps Avatar asked Mar 21 '23 11:03

epaps


2 Answers

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;
like image 71
Staeff Avatar answered Mar 23 '23 06:03

Staeff


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.

like image 37
Matt Dotson Avatar answered Mar 23 '23 07:03

Matt Dotson