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.
PropertyChanged
This 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.
CollectionChanged
This 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.