public class Movie { public string Title { get; set; } public string Rating { get; set; } }
public class Theater { public string Name { get; set; } public List<Movie> Movies { get; set; } public Theater(string name) { Name = name; // Load some initial movies Movies = new List<Movie> { new Movie { Title = "Wonder Woman", Rating = "PG-13" }, new Movie { Title = "Spider-Man", Rating = "PG-13" }, new Movie { Title = "Justice League", Rating = "PG-13" } }; } }
INotifyPropertyChanged
so View can be notified when the Model
changes
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("Title"); } } public string Rating { get { return movie.Rating; } set { movie.Rating = value; OnPropertyChanged("Rating"); } } private void OnPropertyChanged(string property) { // Notify any controls bound to the ViewModel that the property changed PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property)); } }
ObservableCollection
that provides notifications to ListView when items are added or removed
public class TheaterViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private Theater theater; public ObservableCollection<MovieViewModel> Movies { get; set; } public string Name { get { return theater.Name; } set { theater.Name = value; OnPropertyChanged(this, new PropertyChangedEventArgs("Name")); } } public TheaterViewModel(Theater theater) { this.theater = theater; 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) { // Theater name or MovieViewModel changed, so let UI know PropertyChanged?.Invoke(sender, e); } }
<Page x:Class="UwpMovieMvvm.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:UwpMovieMvvm" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBox x:Name="titleTextBox" Text="{x:Bind Movie.Title, Mode=TwoWay}" /> <TextBox x:Name="ratingTextBox" Text="{x:Bind Movie.Rating, Mode=TwoWay}" /> <Button x:Name="addButton" Content="Add Movie" Click="addButton_Click" /> <ListView x:Name="movieListView" Height="155" Width="216" HorizontalAlignment="Left" ItemsSource="{x:Bind Theater.Movies, Mode=OneWay}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:MovieViewModel"> <StackPanel> <TextBlock Text="{x:Bind Title, Mode=OneWay}" FontWeight="Bold"/> <TextBlock Text="{x:Bind Rating, Mode=OneWay}"/> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> <Button x:Name="deleteButton" Content="Delete Movie" Click="deleteButton_Click" /> </StackPanel> </Page>
public sealed partial class MainPage : Page { // XAML binds to these properties public TheaterViewModel Theater { get; set; } public MovieViewModel Movie { get; set; } public MainPage() { this.InitializeComponent(); // Create ViewModels Movie = new MovieViewModel(new Movie()); Theater = new TheaterViewModel(new Theater("Rialto")); } private void addButton_Click(object sender, RoutedEventArgs e) { // Add new movie to list Theater.Movies.Add(new MovieViewModel( new Movie { Title = Movie.Title, Rating = Movie.Rating })); } private void deleteButton_Click(object sender, RoutedEventArgs e) { // Delete selected movie from ObservableCollection to update UI if (movieListView.SelectedIndex > -1) { Theater.Movies.RemoveAt(movieListView.SelectedIndex); // WARNING: Deleting directly from ListView throws an exception! // movieListView.Items.RemoveAt(movieListView.SelectedIndex); } } }