Creating the Data Model and View Model

As discussed, we are going to pursue an MVVM approach to this application. Recall that this means a separation of concern between the objects that hold our data (the Model), the objects that display our data (the View), and the objects that glue the display and the data together (the ViewModel).

Because our model simply needs to store a few properties that represent the meal data we want to track, this is something safe and simple to add into the Shared project. Likewise, the ViewModel won’t implement any platform-specific logic, so it can live in the Shared project as well. To stay organized, we’ll create two new folders in the Shared project: DataModel and ViewModel. With the folders created, right-click on the DataModel folder, select Add New Item, and add a C# class called Meal. Do the same for our view model class, called MainViewModel. It gets added to the ViewModel folder.

Because we want our views to know when something changes within our model and view model, we will use a pattern typically referred to as INPC (INotifyPropertyChanged). This is an interface that we can implement within our classes that will send a notification to the visual tree and any bound pieces of the view to let them know that they need to redisplay the data.

First we inherit from the interface.

public class Meal : INotifyPropertyChanged

Then we implement the required event and event handler.

public event PropertyChangedEventHandler
    PropertyChanged;

private void NotifyPropertyChanged(String propertyName)
{
    PropertyChangedEventHandler handler =
        PropertyChanged;
    if (null != handler)
    {
        handler(this,
            new PropertyChangedEventArgs(propertyName));
    }
}

Now we are all set to be able to notify any of the views that care when a property changes. From within every property “setter” in our class, we will include a call out to NotifyPropertyChanged, like this:

NotifyPropertyChanged("SubTotal");

We need a handful of read/write properties on the Meal class: SubTotal, Date, Parties, TipPercent. We will also need to expose a few read-only properties. These will be computed internal to the class: GrandTotal, PerPartyGrandTotal, DateString, and Description. Description and DateString are convenience properties; we can generate those internal to the model class and get our UI/view to quickly display the information we are looking for without having to do any necessary conversion activities within our view or the view model. You can see how we will use both of these properties within the view by referring back to Figure 24.9.

Our view model class is nothing more than a holder for a collection of Meal objects. The class can hold an ObservableCollection instance of meals and thus function as our data source for our list of meals.

private ObservableCollection<Meal> _meals;

public ObservableCollection<Meal> Meals
{
    get { return _meals; }
    set
    {
        _meals = value;
        NotifyPropertyChanged("Meals");
    }
}

Because our view model and model objects live in their own, separate project, we have to have a way for something durable to instantiate them and make them available to our other UI projects. To make this happen, we’ll create an app-level field called ViewModel that will create the view model and hold its instance. Because this is an app-level property, it is defined within the Shared project’s App.xaml.cs file.

private static ViewModel.MainViewModel viewModel = null;

/// <summary>
/// A static ViewModel used by the views to bind against.
/// </summary>
/// <returns>The MainViewModel object.</returns>
public static ViewModel.MainViewModel ViewModel
{
    get
    {
        //Delay creation of the view model until necessary
        if (viewModel == null)
            viewModel = new ViewModel.MainViewModel();

        return viewModel;
    }
}

With that final piece in place, we can turn our attention to the UI projects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset