Creating Data Classes
October 21, 2011
Before you can display data in your Windows Phone UI, you typically organize your data into classes.
You Will Learn
- How to organize your data to create data classes.
- How to create data classes that update when changes occur.
- How to create data collections that update when changes occur.
Applies To
Organizing Your Data
You can create data classes in many different ways. For example, in applications that interact with external data sources, you might use data classes generated through the ADO.NET Entity Framework or WCF Data Services. For simple applications, you can use plain old CLR object (POCO) classes that you create by hand. These classes often contain little more than properties to hold your data and a little change-notification code.
Tip
Unless your application is simple and self-contained, and will not be the subject of continued development, it can benefit from a more advanced architecture. This generally means encapsulating additional kinds of code into separate classes or layers in order to minimize the side-effects of future changes, make debugging easier, and support unit testing.
In XAML-based applications, a common pattern to use is the Model-View-ViewModel (MVVM) pattern. Because the Fuel Tracker application is relatively simple, it does not implement the MVVM pattern, and this documentation does not cover the pattern in further detail. For information about how to use MVVM, see Implementing the Model-View-ViewModel Pattern in a Windows Phone Application. For guidance on how to apply the MVVM pattern to larger-scale phone application development, see Patterns and Practices Windows Phone 7 Developer Guide.
Fuel Tracker is a simple application and uses CLR objects for its data model. The following illustration shows the Car, Fillup, and CarDataStore classes for the Fuel Tracker application. There are other classes in the application, but these are the key classes.
The Car class holds information about the user’s car. The Fillup class holds information about each fill-up. All of the Car and Fillup properties represent simple value types except for the Car.FillupHistory property, which is a collection of fill-ups. The CarDataStore class is a static class that contains methods for saving and loading the Car and Fillup data that the UI pages bind to.
Notice that the Car and Fillup classes implement the INotifyPropertyChanged interface. INotifyPropertyChanged is required for most types of data binding to keep the UI up to date. For more information about data binding, see Displaying Data with Data Bindingearlier in this documentation.
Change Notification
Applications use data classes in many different ways, including displaying their values directly, enabling users to change the values, using the values to calculate other values, or to drive UI state changes. In all of these cases, the application must update itself whenever the property values change. For this reason, it is useful for classes to implement change notification.
To provide change notification, your classes must implement the INotifyPropertyChanged interface. Change notification isn't always necessary, but it is required in so many common scenarios that it is useful to implement INotifyPropertyChanged just in case. The implementation is straightforward, as shown in the following code examples from the Car class.
public class Car : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
NotifyPropertyChanged("Name");
}
}
// ... other properties ...
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Public Class Car
Implements INotifyPropertyChanged
Private _name As String
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
If _name = value Then Return
_name = value
NotifyPropertyChanged("Name")
End Set
End Property
' ... other properties ...
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
The property setter updates the backing field and raises a change notification only when the property value actually changes. This is useful to avoid unnecessary change notifications.
When you bind objects to your UI, the binding engine subscribes to the object's PropertyChanged event so that it can update all bound controls whenever a property value changes.
Change Notification with Collections
To fully support change notification for a collection, for example when items are added or removed from the collection, the collection must implement the INotifyCollectionChanged interface. ObservableCollection<T> is a dynamic data collection provided by the framework that can hold generic types. ObservableCollection<T> implements both INotifyPropertyChanged and INotifyCollectionChanged, so the easiest way to support change notifications for collections is to just put your items in an ObservableCollection<T>. The items that you put in the ObservableCollection<T> must implement INotifyPropertyChanged if you want change notification for properties of objects in the collection too.
The Fuel Tracker application uses one collection named FillupHistory that contains information about all of the fill-ups. FillupHistory is an ObservableCollection<T> of Fillup objects, as shown in the following code examples from the Car class.
private ObservableCollection<Fillup>_fillupHistory;
public ObservableCollection<Fillup> FillupHistory
{
get { return _fillupHistory; }
set
{
if (_fillupHistory == value) return;
_fillupHistory = value;
if (fillupHistory != null)
{
_fillupHistory.CollectionChanged += delegate
{
NotifyPropertyChanged("AverageFuelEfficiency");
NotifyPropertyChanged("LastFillup");
};
}
NotifyPropertyChanged("FillupHistory");
NotifyPropertyChanged("AverageFuelEfficiency");
}
}
Private _fillupHistory As ObservableCollection(Of Fillup)
Public Property FillupHistory As ObservableCollection(Of Fillup)
Get
Return _fillupHistory
End Get
Set(value As ObservableCollection(Of Fillup))
If (_fillupHistory Is Nothing AndAlso value Is Nothing) OrElse
(_fillupHistory IsNot Nothing AndAlso
_fillupHistory.Equals(value)) Then Return
_fillupHistory = value
If _fillupHistory IsNot Nothing Then
AddHandler _fillupHistory.CollectionChanged,
Sub()
NotifyPropertyChanged("AverageFuelEfficiency")
NotifyPropertyChanged("LastFillup")
End Sub
End If
NotifyPropertyChanged("FillupHistory")
NotifyPropertyChanged("AverageFuelEfficiency")
End Set
End Property
This code also demonstrates how to use change notification to affect other properties. Changing the fill-up history or any items within the fill-up history will affect the average fuel efficiency calculation. Therefore, setting the FillupHistory property raises a change notification for itself and for the AverageFuelEfficiency property. Additionally, setting the property attaches a CollectionChanged event handler to the new collection so that it can raise a change notification for AverageFuelEfficiency and LastFillup whenever an item in the collection is added, removed, or changed.