Masamune provides base classes for view models. The base class for simple objects is called NotifyingObject and the base class for collections is called NotifyingCollection. These classes provide facilities for property changes notifications and their management throug the IUpdatable interface.
IUpdatable interface and related typesThis interface is designed to support bulk updates and declares two methods: BeginUpdate() and EndUpdate(). When a type implements it, the first method should stop rising notification events and the second - resume them. Thus the type should support a bit flag signalling if the notifications enabled or not.
It may be a good idea to send a notification that the object has been completely changed in the EndUpdate() method implementation.
To simplify use of the interface Masamune provides two utility types: the UpdateLock<T> struct and the UpdateLock class.
UpdateLock<T> structThis struct implements the System.IDisposable interface, calls the IUpdatable.BeginUpdate() in it's constructor and the IUpdatable.EndUpdate() in it's Dispose() method.
Suppose you have a class MyUpdatable that implements the IUpdatable interface. To bulk update the instance of the class use the following code:
MyUpdatable instance = GetMyUpdatable();
using(UpdateLock<MyUpdatable> l = new UpdateLock<MyUpdatable>(instance))
{
// Update the 'instance'
}
UpdateLock classThis static utility class helps one to simplify the code for bulk updates:
MyUpdatable instance = GetMyUpdatable();
using(var l = UpdateLock.Acquire(instance))
{
// Update the 'instance'
}
It uses C# type inference to make the using statement clearer.
NotifyingObject classThe NotifyingObject is an abstract base for classes that support notifications. It implements both System.ComponentModel.INotifyPropertyChanged and IUpdatable interfaces and defines following members:
PropertyChanged eventShouldRaiseEvents protected boolean propertyVerifyPropertyName() conditional methodBeginUpdate()/EndUpdate() method pairOnPropertyChanged()SetPropertyValue<T> generic helper methodLet's review these members.
The constructor just sets the ShouldRaiseEvents property to true - a newly created NotifyingObject notifies it's subscribers about property changes.
PropertyChangedThis event is supposed to be raised when one or more properties change their values. This is the implementatio of the INotifyPropertyChanged interface.
ShouldRaiseEvents propertyThis protected property allows the inheriting code to enable/disable property change notifications or check if they are enabled. It is a good idea to check the state of this property in inheriting classes that add more events.
NB: At the moment the property is implemented as a simple bit flag. However, it migh be better to use a counter instead to allow nested bulk updates. This change might be introduced in the future but it will be a breaking change.
VerifyPropertyName() methodThis conditional method checks if the inheriting class has a property with provided name. It may save the developer from typos.
The method is marked with [Conditional("DEBUG")] attribute and gets removed in release builds.
BeginUpdate()/EndUpdate() methodsThese two virtual methods implement the IUpdatable interface and manipulate the value of the ShouldRaiseEvents property.
OnPropertyChanged() overloadsThese two protected overloads raise the PropertyChanged event. The one that accepts a property name as a parameter is sealed, the other, that accepts an instance of the System.ComponentModel.PropertyChangedEventArgs class, is virtual. If you need to intercept the propety changed events in derived classes, override it but call the base implementation.
Here is an example of these methods use:
string key;
public string Key
{
get
{
return key;
}
set
{
if(key != value)
{
key = value;
OnPropertyChanged("Key");
}
}
}
SetPropertyValue<T>() methodThis helper method has following signature:
protected bool SetPropertyValue<T>(ref T backingField, T newValue, [CallerMemberName] string propertyName = null)
The backingField is the reference to the field that contains the value of the proerty that has been changed. It's a ref parameter because backing fields may be value types that are passed by value. The newValue is, obviosely, the new value of the property. The propertyName is the name of the property that has been changed. The way this argument is declared allows the developer to ommit it, the compiler will generate the correct value automatically.
Here is an example of this method (a simplified previous example):
string key;
public string Key
{
get
{
return key;
}
set
{
SetPropertyValue(ref key, value);
}
}
You should stick to use this method. One clear exception would be a property that depends on the value of another property:
string firstName;
string lastName;
public string FirstName
{
get
{
return firstName;
}
set
{
if(firstName != value)
{
firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
}
public string LastName
{
get
{
return firstName;
}
set
{
if(lastName != value)
{
lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return firstName + " " + lastName;
}
}
NotifyingCollection classThis class is a base class for collections with notifications. It inherits from the NotifyingObject and implements the System.Collections.Specialized.INotifyCollectionChanged interface. In addition to the members of the NotifyingObject it defines or overrides the following methods:
CheckReentrancy() protected methodCollectionChanged eventOnCollectionChanged() protected virtual methodOnPropertyChanged() protected overloaded methodIsReadOnly abstract propertyCheckReadOnly() protected methodLet's review these members.
The constructor initializes the object. The ShouldRaiseEvents property is set to true by the base class constructor.
CheckReentrancy() methodThe collection cannot be modified when subscribers handle notifications. You should call the CheckReentrancy() method in any method that potentially modifies the collection. It throws the System.InvalidOperationException if the collection sends the notifications.
CollectionChangedThis complex event is raised when the collection is changed - items being added or removed. Thorougly read the documentation on this event when implementing your observable collections!
OnCollectionChanged() methodThis method raises the CollectionChanged event. It sets an internal flag during this.
OnPropertyChanged() overloaded methodThis method is overloaded to set the internal flag during the PropertyChanged event.
IsReadOnly propertyThis abstract property must be implemented by a derived class. It is checked by the CheckReadOnly() method.
CheckReadOnly() methodThis method throws an InvalidOperationException if the collection is read-only and the user cannot modify it.
These two classes introduce rather flexible boilerplate code for WPF view models.