I'm using the article Model View View-Model (MVVM) in Silverlight as a basis to create my own MVVM example in Silverlight.
I've got all the pieces below:
- main page (loads everything)
- View (XAML file with bindings)
- Model (a Customer class generating a fake List collection)
- ModelView (inherits INotifyPropertyChanged and has the PropertyChanged for the two fields that View needs)
In my main page I:
- create the ViewModel
- bind the ViewModel to the DataContext of the View
- create the Model (customers)
But now how do I connect the ModelView to the Model? I feel as though I need to inject my customers Model somehow into the CustomerViewModel, is that right? But how exactly? What is the next step here to finish this MVVM example so that I can start using the advantages of the MVVM pattern, e.g. swapping out the model with a test model, swapping out the view with new views, etc.
MainPage.xaml.cs: creates the ViewModel, attaches View to ViewModel
using System.Windows.Controls;
using System.Collections.Generic;
namespace TestMvvm345
{
public partial class MainPage : UserControl
{
private CustomerViewModel customerData;
public MainPage()
{
InitializeComponent();
customerData = new CustomerViewModel();
customerHeaderView.DataContext = customerData;
List<Customer> customers = Customer.GetCustomers();
}
}
}
MainPage.xaml: displays the View within the context of the main page
<UserControl x:Class="TestMvvm345.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestMvvm345"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel HorizontalAlignment="Left">
<local:CustomerHeaderView x:Name="customerHeaderView" Margin="10"/>
</StackPanel>
</Grid>
</UserControl>
CustomerViewModel.xaml: the ViewModel
using System.ComponentModel;
namespace TestMvvm345
{
public class CustomerViewModel : INotifyPropertyChanged
{
private string firstName;
private string lastName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
RaisePropertyChanged("FirstName");
}
}
public string LastName
{
get { return lastName; }
set
{
lastName = value;
RaisePropertyChanged("LastName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
CustomerHeaderView.xaml the View
<UserControl x:Class="TestMvvm345.CustomerHeaderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel HorizontalAlignment="Left">
<ListBox x:Name="CustomerList" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Text="{FirstName}"/>
<TextBlock
Text="{LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</UserControl>
Customers.cs the Model
using System;
using System.Collections.Generic;
namespace TestMvvm345
{
public class Customer
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int NumberOfContracts { get; set; }
public static List<Customer> GetCustomers()
{
List<Customer> customers = new List<Customer>();
customers = new List<Customer>();
customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
customers.Add(new Customer { FirstName = "Jane", LastName = "Smith", NumberOfContracts = 22 });
customers.Add(new Customer { FirstName = "John", LastName = "Tester", NumberOfContracts = 33 });
customers.Add(new Customer { FirstName = "Robert", LastName = "Smith", NumberOfContracts = 2 });
customers.Add(new Customer { FirstName = "Hank", LastName = "Jobs", NumberOfContracts = 5 });
return customers;
}
}
}
Best Answer
The way I use a ViewModel is somewhat different. In my case it actually wraps the Model class passing most of the data on to the model. That way all standard business rules in the model still work the way they are supposed to right away. The ViewModel only exposes those properties actually needed for data binding or other UI purposes. Additionally the ViewModel might contain other properties/methods used for data binding.
So using your customer example: namespace TestMvvm345 { public class CustomerViewModel : INotifyPropertyChanged {
}
Now in your UI you bind using the following code: customerHeaderView.DataContext = CustomerViewModel.GetCustomers();
The code in the ViewModel does become somewhat large but a lot can be put in a base class and all properties that go to the Model are exactly the same (think T4 template).