Event Bindings outside of WPF

Event Bindings outside of WPF

Code Location

The code for this blog post can be downloaded from EventBindingTests.zip.

The solution file EventBindingTests.sln is located under EventBindingTests\TESTS\EventBindingTests
folder.

Introduction

Here I continue a series of blog posts about implementing WPF concepts outside of WPF.

In fact here, I am going to describe a concept/pattern that does not exist in WPF but, based on
my experience will be useful in WPF-esq universe – the Event Binding.

WPF creates various hierarchies of objects – there is a logical tree, there is a visual tree. When programming
MVVM there is also unspoken, but widely used hierarchy of the view models – e.g. a top level View Model might
contain some member representing another View Model or it might contain a collection of the view models representiong e.g. rows in a table or entries in a ListView.

Many time there should be an action from a sub-View Model to the one of its ‘anscestors’ in the View Model
hierarchy. This might happen, e.g. when a visual action is invoked on the sub-View Model but should result in
a change on its ‘anscestor’ level. The simplest example would be a remove button on each of the items within a list view. If implemented view the View Model patters, the remove button will have access only to the sub-View Model corresponding to individual item. However, the remove action should happen on the collection that contains the items’ View Models, i.e. a level higher. The way to implement it would be to create a ‘remove action’ event that fires at the item View Model level. When item becomes part of the collection in the higher level View Model – it adds a handler to the event that actually removes the item. The higher level View Model needs to manage adding and removing handlers to the items as they are added or removed to or from the collection or as the collection of items is getting totally overriden by a different collection.

The purpose of the Event Binding is precisely to make it easier to
manage the event handlers added at the ‘anscestor’ level to the ‘descendant’ item events.

Demonstration of what one can do with Event Binding

The main program is in Programs.cs file of EventBindingTests project. It demonstrates
binding an event handler to a single object or to a collection of objects.

Single Object Event Binding

Here is the single object event binding code (do not try to read to much into it since it is explained step by step below:

PopulatedOrganization wonkaFactory = new PopulatedOrganization();

Organization chocolateDepartment = new Organization();

#region SINGLE OBJECT EVENT BINDING

// create the event binding for single object
EventBinding<Organization, string> eventBinding =
    new EventBinding<Organization, string>();

// specify the source object and the path to the source binding property that contains the event
// we want to bind to
eventBinding.SourceObj = wonkaFactory;
eventBinding.SourcePathLinks =
     StringCodePathResolver.ResolveStringPath("TheMostImportantDepartment").ToList();

// do the binding itself
eventBinding.Bind();

// specify the event name (it can be done either before or after bind()
// method is called
eventBinding.EventName = "SomethingHappenedInOrgEvent";

// add the event hanlder whose signature is similar to that of 
// SomethingHappenedInOrgEvent event to the binding
eventBinding.TheEvent += eventBinding_SomethingHappenedInTheDepartment;

// nothing is printed to the console because wonkaFactory.TheMostImportantDepartment
// is still not set to chocolateDepartment object
chocolateDepartment.FireSomethingHappenedEvent("Augustus Gloop went to the chocolate creek. (Before the department added - should not show)" );

// set wonkaFactory.TheMostImportantDepartment 
wonkaFactory.TheMostImportantDepartment = chocolateDepartment;

// this message is printed on the console
chocolateDepartment.FireSomethingHappenedEvent("Augustus Gloop is out of the game (should show)");

// unbind the event
eventBinding.Unbind();

#endregion SINGLE OBJECT EVENT BINDING

Here is the description of what is going on in the code. Organization class
has a property TheMostImportantDepartment which is also of Organization type.
Organization also has an event SomethingHappenedInOrgEvent.
This event is of the type SomethingHappenedDelegate which is similar to
Action<string>. Method FireSomethingHappenedEvent(string message)
fires the event passing the message to it.

We want to bind the SomethingHappenedInOrgEvent on the TheMostImportantDepartment
property of the organization to a handler at the main program level. For this purpose we use the Event Binding:

// create the event binding for single object
EventBinding<Organization, string> eventBinding =
    new EventBinding<Organization, string>();

// specify the source object and the path to the source binding property that contains the event
// we want to bind to
eventBinding.SourceObj = wonkaFactory;
eventBinding.SourcePathLinks =
     StringCodePathResolver.ResolveStringPath("TheMostImportantDepartment").ToList();

// do the binding itself
eventBinding.Bind();

// specify the event name (it can be done either before or after bind()           
// method is called
eventBinding.EventName = "SomethingHappenedInOrgEvent";

The above code does the binding. Note that the binding is already there even though the
TheMostImportantDepartment property has not been set yet.

Now we add the event handler to the Event Binding
and not to the original event:

// add the event handler whose signature is similar to that of 
// SomethingHappenedInOrgEvent event to the binding
eventBinding.TheEvent += eventBinding_SomethingHappenedInTheDepartment;

This event handler will simply print the message argument from the event.

Now if we try to fire the event on the chocolateDepartment object – nothing should
change, because TheMostImportantDepartment property of the wonkaFactory
object is still not set to the chocolateFactory:

// nothing is printed to the console because wonkaFactory.TheMostImportantDepartment
// is still not set to chocolateDepartment object
chocolateDepartment.FireSomethingHappenedEvent("Augustus Gloop went to the chocolate creek. (Before the department added - should not show)" )

Now we set the property and fire an event again and the corresponding message should be printed on the console:

// this message is printed on the console
chocolateDepartment.FireSomethingHappenedEvent("Augustus Gloop is out of the game (should show)");

Note that the Event Binding takes full care of figuring out if
the property is null or not and making the event binding behave accordingly as long as the binding notification
is on (for simple properties – that means firing INotifyPropertyChanged.PropertyChanged
event when the TheMostImportantDepartment property changes. Similar notifications
are available for AProperties or Attached/Dependency properties – but the
SourcePathLinks will have to reflect the corresponding PropertyKind.

Note also that the even though we considered a path containing only one path link – we can
use arbitraty path links of arbitrary length for Event Bindings
as long as each link provides binding notifications.

Collection Event Binding

Collection Event Binding provides even more dramatic refactoring.
Not only it takes case of collection being reset, but also if the collection implmements
INotifyCollectionChanged interface (i.e. ObservableCollection,
it adds or removes proper handlers when the items of the
collection are added or removed correspondingly.

An organization has AllDepartments property of type
ObservableCollection<Organization>. We want to set the collection, add a couple of departments
to it use Event Binding to bind the SomethingHappenedInOrgEvent
on the collection objects to our event handler. Here is the corresponding code:

#region COLLECTION EVENT BINDING

// create the collection AllDepartments
wonkaFactory.AllDepartments = new ObservableCollection();

// add chocolate department to it
wonkaFactory.AllDepartments.Add(chocolateDepartment);

// create collection event binding
CollectionEventBinding<Organization, string> collectionEventBinding =
    new CollectionEventBinding<Organization, string>();

// set the objects that contain the event we want to bind to 
// to be "AllDepartments" collection property of "wonkaFactory" object 
collectionEventBinding.SourceObj = wonkaFactory;
collectionEventBinding.SourcePathLinks =
     StringCodePathResolver.ResolveStringPath("AllDepartments").ToList();

// bind the event
collectionEventBinding.Bind();

// set the event name (can be done before or after the binding)
collectionEventBinding.EventName = "SomethingHappenedInOrgEvent";

// add event handler
collectionEventBinding.TheEvent += collectionEventBinding_TheEvent;

// create gumDepartment
Organization gumDepartment = new Organization();

// fire an event (should not be handled since gumDepartment is not part of the collection yet)
gumDepartment.FireSomethingHappenedEvent("We had great sales (Before the department is added - should not show)");

// Add gum department to the collection
wonkaFactory.AllDepartments.Add(gumDepartment);

// fire the event (should be handled, since now gumDepartment is part of the collection)
gumDepartment.FireSomethingHappenedEvent("We had great sales (After the department is added - should show)");

// remove gum department from All Department collection
// collectionEventBinding should be sufficiently smart to disconnect 
// the event of gumDepartment object from the handler
wonkaFactory.AllDepartments.Remove(gumDepartment);

// fire the event again - (the handler should not run, since gumDepartment has been removed from the collection)
gumDepartment.FireSomethingHappenedEvent("We had great sales (After the department is Removed - should not show)");

#endregion COLLECTION EVENT BINDING  

The binding code is sufficiently similar to the case of a single object so that we do go
over each step again in detail. I’d like to re-iterate, however, that the CollectionEventBinding
will manage the event handlers on each of the members of the collection both in case the whole collection
is re-assigned (if all the path links to the collection have binding notifications)
or in case elements are added or removed to or from it (if the collection implements
INotifyCollectionChanged interface).

Implementation Notes

The central class for both EventBinding and CollectionEventBinding implementation
is NP.Paradigms.EventBindingBase<ObjectType, EventObjectType>. It provided the actual binding
from the actual object in the hierarchy to the Event Binding‘s property
TheObj. This class has two generic parameters: ObjectType and EventObjectType.
ObjectType is the type of the object that we bind to – in case of a single event binding – it is the
same as the type of the object that contains the event (EventObjectType), while in case of a collection
event binding it is a collection of objects of type EventObjectType.

This class contains two important abstract methods
Disconnect() and Reconnect() that control removing or setting the event handler
on the corresponding object(s). These methods are overriden in concrete implementation of EventBinding
and CollectionEventBinding functionality.

This class defines also the name of the method that will be attached to the bound object(s) events: "EventHadler".
This method is also defined in the sub-classes.

The reflection based actual implementation of adding and removing the handlers to the object is located
within NP.Paradigms.EventManager class.

Class SingleObjectEventBindingBase is derived from EventBindingBase it overrides
Disconnect() and Reconnect() methods to act on a single object.

A number of EventBinding classes with various generic parameters specifying different possible
arguments to the event is derived from SingleObjectEventBindingBase class.

CollectionEventBindingBase class is also derived from EventBindingBase by overriding the same
functions Disconnect() and Reconnect() and specifying some handling when items are added or removed
to or from the collection.

A number of CollectionEventBinding classes with various generic parameters is also derived
from CollectionEventBindingBase class.

Conclusion

In this blog post I describe a new concept of Event Binding which is not part
of WPF but should come handy for programming using WPF related concepts (whether it used in WPF or outside of WPF).

 

One Response to “Event Bindings outside of WPF”

  1. ramankingdom Says:

    Nice Article.

Leave a comment