Here are the notes from Exchanging Software Design Knowledge group meetup on Apr 11, 2018: Multiple Implementation Inheritance and Roxy Code Generation
Archive for the ‘Uncategorized’ Category
Exchanging Software Design Knowledge Meetup 03/13/2018 notes
March 14, 2018Here are the notes from Exchanging Software Design Knowledge group meetup Pluggin Based Patterns on 03/13/2018
Event Bindings outside of WPF
June 8, 2014Event 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).
Composite Path Bindings outside of WPF
June 27, 2013Composite Path Bindings outside of WPF
Introduction
In AProperties and Bindings outside of WPF Revisited I presented implementation of binding concepts using IPropGetter
and IPropSetter
interfaces. This implementation allowed binding a source property on a source object to a target property on a target object. Both plain properties and AProperties could be used as source and target.
The binding dicussed at the link above, has a limitation, though, in that it only deals with immediate properties of the source or the target. You cannot bind to a property with a complex path to it. This blog entry aims to resolve this problem. Not only it will provide the functionality for binding using a complex path to the source property (something the WPF binding functionality also allows), but it will show how to use a complex path at the target side as well (something that WPF does not permit).
When dealing with complex path to the source property, there is always a posibility that the such path simply does not exist. In that case the binding can provide a default value to the target property. This default value is similar to WPF binding’s FallbackValue
property.
The source code for the article is located under CompositePathTests.zip file.
Sample that Uses Composite Path Bindings
The main project is CompositePathToTargetTest
. Most of the sample code is located within Program.cs file.
Both source and target object of this sample are of ParentDataClass
type. ParentDataClass
has a property TheData
of type DataClass
. DataClass
in turn has a property MyStringProp
of type string. The sample shows how to bind MyStringProp
property of the TheData
property of the source object to the same path within the target object.
The class that was called BindingPath
in the previous articles is renamed to BindingPathLink
. This class chooses correct property “getter” and “setter” for the binding. The composite paths consist of a collection of the BindingPathLink
objects. Here is how such collection is created for the source object:
CompositePathGetter sourcePathGetter = new CompositePathGetter ( new BindingPathLink<object>[] { new BindingPathLink<object>("TheData"), new BindingPathLink<object>("MyStringProp"), }, "A Default String" );
CompositePathGetter
requires a collection of path links and a default value that will be sent to the target if the path to the source does not exist (remember it is similar to the FallbackValue
of the WPF binding).
The setter for the binding’s target is created in a similar way:
CompositePathSetter targetPathSetter = new CompositePathSetter ( new BindingPathLink<object>[] { new BindingPathLink<object>("TheData"), new BindingPathLink<object>("MyStringProp") } );
only here we do not need to pass the default value parameter.
Then we set the source and target objects of the source getter and target setter:
sourcePathGetter.TheObj = sourceDataObj; targetPathSetter.TheObj = targetDataObject;
We set the corresponding getter and setter properties of the binding:
binding.SourcePropertyGetter = sourcePathGetter; binding.TargetPropertySetter = targetPathSetter;
Then after we call function Bind()
on the binding object, the binding becomes operational and the source value is set on the target: calling Console.WriteLine(targetDataObject.TheData.MyStringProp);
will print “Hello World”.
Then if we change the source property: sourceDataObj.TheData.MyStringProp = "Hi World";
the target property will also change to “Hi World”. If we change the TheData
property of the source object, the target will also reflect the change:
sourceDataObj.TheData = new DataClass { MyStringProp = "bye bye" };
will set the target property to “bye bye”.
If TheData
property of sourceDataObj
is set to null, the default binding value “A Default String” will be set to the target property.
If TheData
property of the targetDataObj
is set to null, the old binding value is retained and will be set to the MyStringProp
property of the new TheData
object, if at some point it becomes non-null.
Notes on Implementation
Instead of IPropGetter<PropertyType>
and IPropSetter<PropertyType>
interfaces, used in the previous articles for implmenting the binding’s getter and setter, we use IObjWithPropGetter<PropertyType>
and IObjWithPropSetter<PropertyType>
interfaces that also allow setting the object for which the properties are read or set. This is done in order not to recreate the getter and setters every time the path’s objects are created or destroyed.
CompositeClassGetter
represents a chain of IObjWithPropGetter<PropertyType>
objects built from a list of BindingPathLink
objects that represent the path to the source property from the source object. The PropertyChangedEvent
of each IObjWithPropGetter
is handled by setting the corresponding object for the next property getter. The handler for the last object is set to call the PropertyChangeEvent
on the CompositeClassGetter
object:
IObjWithPropGetter<object> previousPropGetter = null; foreach (var pathLink in _pathLinks) { IObjWithPropGetter<object> propGetter = pathLink.GetPropertyGetter(); _propGetters.Add(propGetter); if (previousPropGetter != null) { previousPropGetter.PropertyChangedEvent += (obj) => { propGetter.TheObj = obj; }; } previousPropGetter = propGetter; } previousPropGetter.PropertyChangedEvent += (obj) => { if (this.PropertyChangedEvent == null) return; if (!LastPropGetter.HasObj) { PropertyChangedEvent(_defaultValue); } else { PropertyChangedEvent(obj); } };
CompositePathSetter
consists of IObjWithPropSetter
object corresponding to the last link of the target property path and a chain of IObjWithPropertyGetter
objects corresponding to the rest of the links. The PropertyChangedEvent
of each of the property getters sets the object on the next property getter (or setter):
IObjWithPropGetter<object> previousPropGetter = null; foreach (var propGetter in _propGetters) { if (previousPropGetter != null) { previousPropGetter.PropertyChangedEvent += (obj) => { propGetter.TheObj = obj; }; } previousPropGetter = propGetter; } // set the last property getter to the set the setter previousPropGetter.PropertyChangedEvent += (obj) => { _theSetter.TheObj = obj; };
Attached Properties outside of WPF
April 29, 2013
Here I continue talking about re-implementing and improving WPF concepts outside of WPF and in a way that is not necessarily connected to GUI development. The first article in the series was discussing implementation of property and collection bindings outside of WPF and is available at Binding without WPF or at Codeproject: Binding without WPF. The final goal of these series is to implement most of the concepts that WPF introduced outside of WPF without dependency on any MS visual libraries and, perhaps, even in different languages, like JavaScript, Java and Objective-C.
This article discusses re-implementing WPF Attached Properties.
As a reminder – WPF Attached Properties serve the same purpose as the usual properties: they allow to get a property value from an object. They are, however, implemented very differently from the usual properties: instead of being part of the object, they are defined outside of it. If an Attached Property was set on an object, it can be retrieved from some memory store outside of the object with the object serving as a key. If an Attached Property was never defined on an object, the Attached Property’s default value will be returned. This value is defined per Attached Property and will be the same for any object.
WPF’s Attached Properties provide a number of very useful features:
- Attached Properties were used in WPF to reduce the storage required for all the numerious
WPF properties. Indeed, if an object uses default Attached Property value, it does not
require any additional storage. This type of storing property values
is called sparse storage. - WPF Attached Properties virtually allow adding new data to an object
without modifying the object’s type. - WPF Attached Properties allow setting callbacks that fire when a property
changes on an object. - In WPF only Attached (and Dependency) Properties can be a binding’s target.
(Dependency Properties are very similar to the AttachedProperties but can
only be defined within the type to which they can be attached). - Attached Properties propagate down the visual tree.
- WPF’s built-in animation framework can only animate Attached and Dependency Properties.
Here we show how to build a framework providing capabilities very similar to the WPF Attached Properties. In order to differentiate between the WPF Attached Properties and this framework’s properties I call them AProperties or AProps. Here are the AProps capabilties that match those of the Attached Properties:
- AProps provide sparse storage.
- AProps allow to add external data to the objects without changing
the objects’ type or class. - AProps allow to add a callback to be fired when a value changes on an object.
On top of the features above that are also available for the WPF attached properties it will also provide the following nice features:
- AProps can be attached to a C# entity of any type, not only to those descended from
DependencyObject
. - Unlike WPF Attached Properties, the mechanism of operating with AProps is strongly typed.
- In WPF one can specify an Attached Propertie’s callback when it is created or registered. This callback fires after an object’s property changes and it is the same for any object that has the Attached Property. AProps allow to specify also a callback to be fired before a property is changed on an object. If this callback returns
false
, the property changed is cancelled. Moreover, the framework allows adding callback to the individual objects. This callbacks are fired on when the AProperty is changed on the object to which the callback was added. Other objects are not affected by the callback. - Unlike Attached Properties, AProps do not have to be defined as static variables (even though they can be defined static).
Now I would like to list the functionality that the Attached Properties have, but AProps (at this point yet) do not.
- The binding framework from the previous article is not made to bind to or from AProps. Even though this functionality is coming soon.
- Attached Properties change the property value within UI thread, while the AProps change in the thread of the caller. Perhaps, at some point, I’ll add another degree of freedom to the AProps that would allow to specify property change thread.
- There is no (yet) visual framework built around AProps, so there is no propagation down the visual tree or animation classes that use AProps.
- There is no value coercion mechanism for AProps (which anyways not used very frequently in WPF).
The AProps code together with the test project that shows how to use them is can be downloade from APropsCode.zip.
The main (virtually the only) class for dealing with AProps is AProperty
under NP.AProps
project.
It provides a functionality for creating AProperty
object. AProperty
contains a Dictionary
(map) that maps the objects to their property values, or rather to some entities that contain their corresponding property values. If the object does not exist in the Dictionary
, the default value gets returns as its AProperty
value.
The central public methods of AProperty
class are the following:
public PropertyType GetProperty(ObjectType obj)
Given an object returns itsAProperty
value.
public void SetProperty(ObjectType obj, PropertyType newPropertyValue)
SetsAProperty
value on the passed object.-
public void AddOnPropertyChangedHandler ( ObjectType obj, OnPropertyChangedDelegate propChangedHandler )
Adds object’s individual property change handler (other objects won’t be affected by it).
-
public void RemoveOnPropertyChangedHandler ( ObjectType obj, OnPropertyChangedDelegate propChangedHandler )
Removes object’s individual property change handler.
public void ClearAProperty(ObjectType obj)
Clears AProperty value from the object (essentially removes the object from theAProperty
‘sDictionary
.
The constructor of AProperty
class has the following signature:
public AProperty ( PropertyType defaultValue = default(PropertyType), Func beforePropertyChangedFn = null, OnPropertyChangedDelegate onPropertyChangedFn = null )
As you can see, it allows to pass the default value of the AProperty
and two delegates: beforePropertyChangedFn
and onPropertyChangedFn
. The first of the delegates executes before AProperty
changes on some object. If it returns false
the property change is cancelled. The second delegate executes after the property change. Unlike individual object property change handlers these delegates execute for any object whose corresponding AProperty
changes. The provided API does not allow modifying these delegate once they were set; otherwise all the objects that had their corresponding AProperty
set might be affected. The OnPropertyChangedFn
delegate is similar to OnPropertyChanged delegate that can be passed to the Attached Property’s metadata as the second argument.
The code that shows how to use AProperty
API is under APropertyTest
project. MyTestClass
is a class with one property Name
of type string
. We want to add some integer index to MyTestClass
object by using AProperty
functionality.
Here is how we create indexAProperty
object:
AProperty<MyTestClass, int> indexAProperty = new AProperty ( -1, // default indexAProperty value null, // no function is called before the property is set. (obj, oldVal, newVal) =>// to be called after the property is set { Console.WriteLine("This is a generic (not individual) property change event handler, oldValue: {0}, newValue: {1}", oldVal, newVal); } );
We set the default value for indexAProperty
to -1
, the generic delegate to fire before the property change is not set (null
), and the generic post-property-change delegate prints a message with old and new value.
After that, we create a list of MyTestClass
object and populate it with 3 objects. The indexAProperty
for those 3 objects is set from is set to 1
, 2
and 3
correspondingly. The object with index 2
is assigned an individual property change event handler:
// list to populate with objects List myTestObjList = new List(); for (int i = 1; i < 4; i++) { // create the object MyTestClass myTestObj = new MyTestClass { Name = "Obj " + i }; // set an individual property change event handler for the second object if (i == 2) { indexAProperty.AddOnPropertyChangedHandler ( myTestObj, (obj, oldVal, newVal) => // individual object delegate { Console.WriteLine("This is individual property change event handler, oldValue: {0}, newValue: {1}", oldVal, newVal); } ); } // add an object to the list myTestObjList.Add(myTestObj); // set the indexAProperty on the myTestObj object to i indexAProperty.SetProperty(myTestObj, i); }
Finally we iterate through the list of objects an print the indexAProperty
value for each of the objects:
// print indexAProperty values for every object in the list foreach (MyTestClass myTestObj in myTestObjList) { int objNumber = indexAProperty.GetProperty(myTestObj); Console.WriteLine(objNumber); }
Restarting the blog.
March 15, 2013Hi All,
This blog has been dead for a while and I was posting technical articles mainly on codeproject.com.
I plan to restart this blog posting programming tit-bits and software-related ideas on it while still publishing longer articles on codeproject.com.
New articles on Silverlight and Prism
February 21, 2011It has been a while and I kind of neglected the blog, but I did publish a bunch of new articles on code project:
Prism for Silverlight/MEF in Easy Samples. Part 1 – Prism Modules
Prism for Silverlight/MEF in Easy Samples. Part 2 – Prism Navigation
Prism for Silverlight/MEF in Easy Samples. Part 3 – Communication between the Modules
Silverlight Animations along Arbitrary Mathematical Paths via Easing
Why I started this blog
September 19, 2008I have a lot of thoughts, sketches and projects that I would like to share with the world.