Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / C#

Event Bindings Outside of WPF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
9 Jun 2014CPOL6 min read 6.5K   7  
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 representation, e.g., rows in a table or entries in a ListView.

Many times, there should be an action from a sub-View Model to the one of its ‘ancestors’ 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 ‘ancestor’ level. The simplest example would be a remove button on each of the items within a list view. If implemented view the View Model patterns, 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 overridden by a different collection.

The purpose of the Event Binding is precisely to make it easier to manage the event handlers added at the ‘ancestor’ 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 too much into it since it is explained step by step below:

C#
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 handler 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:

C#
// 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:

C#
// 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:

C#
// 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:

C#
// 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 even though we considered a path containing only one path link – we can use arbitrary 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 implements 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:

C#
#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 overridden 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 in handy for programming using WPF related concepts (whether it used in WPF or outside of WPF).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect AWebPros
United States United States
I am a software architect and a developer with great passion for new engineering solutions and finding and applying design patterns.

I am passionate about learning new ways of building software and sharing my knowledge with others.

I worked with many various languages including C#, Java and C++.

I fell in love with WPF (and later Silverlight) at first sight. After Microsoft killed Silverlight, I was distraught until I found Avalonia - a great multiplatform package for building UI on Windows, Linux, Mac as well as within browsers (using WASM) and for mobile platforms.

I have my Ph.D. from RPI.

here is my linkedin profile

Comments and Discussions

 
-- There are no messages in this forum --