views:

547

answers:

1

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;
        }
    }
}
+1  A: 

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 {

    private Customer _model;
    public Customer Model
    {
        get
        { return _model; }
        set
        {
            if (_model != null)
                Model.PropertyChanged -= Model_PropertyChanged;

            _model = value;

            if (_model != null)
                Model.PropertyChanged += Model_PropertyChanged;
        }
    }

    void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        RaisePropertyChanged(e.PropertyName);
        if (e.PropertyName == "FirstName" || e.PropertyName == "LastName")
            RaisePropertyChanged("FullName");
    }

    public string FirstName
    {
        get { return Model.FirstName; }
        set { Model.FirstName = value; }
    }

    public string LastName
    {
        get { return Model.LastName; }
        set { Model.LastName = value; }
    }

    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }


    public static IList<CustomerViewModel> GetCustomers()
    {
        var result = new List<CustomerViewModel>();
        var customers = Customer.GetCustomers();
        foreach (var customer in customers)
        {
            result.Add(new CustomerViewModel() { Model = customer });
        }
        return result;
    }
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}


public class Customer : INotifyPropertyChanged
{
    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;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

}

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

Maurice