Model, View, ViewModel (MVVM)

  1. Overview
    1. MVVM is a design pattern often used in UWP apps that makes use of data binding. Composed of three parts:
      1. Model - Same as MVC: the data, state, and business logic
      2. View - UI; binds the observable variables and actions exposed by the ViewModel
      3. ViewModel - Wraps the Model and prepares observable data needed by the View; manipulates the Model due to actions in the View; not tied to the View

      MVVM diagram

    2. Changes to the ViewModel are automatically propogated to the View (data binding), and the observer pattern is used to inform changes in ViewModel to the Model
    3. MVVM is easier to test than MVC and reduces the amount of code necessary to connect the View and Model
    4. Disadvantage: Very large applications using MVVM may consume considerable amounts of memory
    5. Minimal MVVM UWP example on Microsoft blog (my example is even more minimal)
  2. Models
    1. Classes that know nothing of the ViewModels or Views
    2. Movie
      public class Movie
      {
      	public string Title { get; set; }
      	public string Rating { get; set; }
      }
      
    3. Theater
      public class Theater
      {
      	public string Name { get; set; }
      	public List<Movie> Movies { get; set; }
      
      	public Theater(String name)
      	{
      		Name = name;
      		Movies = new List<Movie>
      		{
      			new Movie { Title = "Doctor Strange", Rating = "PG-13" },
      			new Movie { Title = "Spider-Man", Rating = "PG-13" },
      			new Movie { Title = "Justic League", Rating = "PG-13" }
      		};
      	}
      }
      
  3. ViewModels
    1. Implement INotifyPropertyChanged so View can be notified when the Model changes
    2. Encapsulates Models
    3. MovieViewModel
      public class MovieViewModel : INotifyPropertyChanged
      {
      	public event PropertyChangedEventHandler PropertyChanged;
      
      	private Movie movie;
      
      	public MovieViewModel(Movie movie)
      	{
      		this.movie = movie;
      	}
      
      	public string Title
      	{
      		get { return movie.Title; }
      
      		set
      		{
      			movie.Title = value;
      			OnPropertyChanged(new PropertyChangedEventArgs("Title"));
      		}
      	}
      
      	public string Rating 
      	{
      		get	{ return movie.Rating; }
      
      		set
      		{
      			movie.Rating = value;
      			OnPropertyChanged(new PropertyChangedEventArgs("Rating"));
      		}
      	}
      
      	private void OnPropertyChanged(PropertyChangedEventArgs e)
      	{
      		if (PropertyChanged != null)
      			PropertyChanged(this, e);
      	}
      }
      
    4. TheaterViewModel uses ObservableCollection<T> that provides notifications when items are added or removed
      public class TheaterViewModel : INotifyPropertyChanged
      {
      	public event PropertyChangedEventHandler PropertyChanged;
      
      	private Theater theater;
      
      	private ObservableCollection<MovieViewModel> movies;
      
      	public ObservableCollection<MovieViewModel> Movies
      	{
      		get { return movies; }
      		set
      		{
      			movies = value;
      			OnPropertyChanged(this, new PropertyChangedEventArgs("Movies"));
      		}
      	}
      
      	public string Name
      	{
      		get { return theater.Name; }
      		set { theater.Name = value; }
      	}
      
      	public TheaterViewModel(String name)
      	{
      		theater = new Theater(name);
      
      		movies = new ObservableCollection<MovieViewModel>();
      
      		// Create ViewModels for each Movie
      		foreach (var movie in theater.Movies)
      		{
      			var newMovie = new MovieViewModel(movie);
      			newMovie.PropertyChanged += OnPropertyChanged;
      			movies.Add(newMovie);
      		}
      	}
      
      	private void OnPropertyChanged(Object sender, PropertyChangedEventArgs e)
      	{
      		if (PropertyChanged != null)
      			PropertyChanged(this, e);
      
      	}
      }
      
  4. View
    1. Code-behind creates public properties for ViewModels so XAML can bind to them
      				
      public sealed partial class MainPage : Page
      {
      	public TheaterViewModel Theater { get; set; }
      	public MovieViewModel Movie { get; set; }
      
      	public MainPage()
      	{
      		this.InitializeComponent();
      
      		// Create ViewModels
      		Movie = new MovieViewModel(new Movie { Title = "Thor", Rating = "PG-13"	});
      		Theater = new TheaterViewModel("Rialto");
      	}
      
      	private void button_Click(object sender, RoutedEventArgs e)
      	{
      		// Changes to ViewModel are reflected in the UI
      		Movie.Title = "Spider-Man";
      		Movie.Rating = "PG";
      		Theater.Movies[0].Title = "X-Men";
      		
      		// Add new movie to list
              Theater.Movies.Add(new MovieViewModel(new Movie { Title = "Wonder Woman", Rating = "PG-13" }));
      	}
      }
      
    2. XAML uses x:Bind markup extension, which is converted to code at compile time and sets a property to the specified markup
    3. MainPage.xaml
      <TextBox x:Name="titleTextBox" Text="{x:Bind Movie.Title, Mode=TwoWay}" />
      <TextBox x:Name="ratingTextBox" Text="{x:Bind Movie.Rating, Mode=TwoWay}" />
      		
      <ListView x:Name="movieListView" Height="155" Width="216" 
                        ItemsSource="{x:Bind Theater.Movies, Mode=OneWay}">
          <ListView.ItemTemplate>
              <DataTemplate x:DataType="code:MovieViewModel">
                  <StackPanel>
                      <TextBlock Text="{x:Bind Title, Mode=OneWay}" FontWeight="Bold"/>
                      <TextBlock Text="{x:Bind Rating, Mode=OneWay}"/>
                  </StackPanel>
              </DataTemplate>
          </ListView.ItemTemplate>
      </ListView>