For once and for all: class inheritance in C#

Methods of a class

The methods of a class are stored in a kind of table which maps the method signature to the method implementation. If you then call a method in your code, this table is consulted and the correct implementation of the method is called.
This is illustrated in the following figure:

classinheritancesuperclass

Derive class from another Base class

Deriving a class from another class makes the methods of the latter, the superclass class, available from instances of the former, the subclass. If you have no overlap in the signatures of the methods then nothing special happens. If you have methods in the both classes with the same signature, then without doing anything, you will get a compiler warning:

‘<subclass method signature>’ hides inherited member ‘<superclass method signature>’. Use the new keyword if hiding was intended.

If you want to compile without warnings, a good thing, you must preceed the method in the subclass with the ‘new’ keyword.

Thus in the superclass we have:

public string SomeClassMethod()

and in the subclass we have:

new public string SomeClassMethod()

The result of hiding a member is that when we have a class of the instance of the subclass and cast it to an instance of the superclass, then the method of the superclass is called:

ClassFromClassDefinition inherited = new ClassFromClassDefinition();
ClassDefinition inheritedAsParent = inherited;

Assert.AreEqual(ClassFromClassDefinition.ClassFromClassDefinitionSomeClassMethodReturnValue, inherited.SomeClassMethod());
Assert.AreNotEqual(ClassDefinition.SomeClassMethodReturnValue, inherited.SomeClassMethod());
Assert.AreEqual(ClassDefinition.SomeClassMethodReturnValue, inheritedAsParent.SomeClassMethod());

What happens is illustrated in the following image:

classinheritancevirtualnew

When deriving from a superclass, the methodtable of this superclass (ClassDefinition) is integrated in the methodtable of the subclass (ClassFromClassDefinition). As you can see from the SomeMethod table entries you now have two entries for them: one for the superclass methodtable and one for the subclass methodtable.

If you access an object of the type of the subclass from an instance of this subclass, you use the methodtable for the subclass. If however you cast to the superclass, then the methodtable you use is the one for the superclass and calling SomeMethod results in calling the superclass version of this method. Something which isconfirmed by the above assertions.

If you don’t want this behaviour but instead want the subclass method to be called from superclasses casted from the subclass, you must use the ‘virtual’ and ‘override’ keywords

Thus in the superclass we have:

virtual public string VirtualMethod()

and in the subclass we have:

override public string VirtualMethod()

which results in the desired behaviour:

ClassFromClassDefinition inherited = new ClassFromClassDefinition();
ClassDefinition inheritedAsParent = inherited;
 
Assert.AreEqual(ClassFromClassDefinition.ClassFromClassDefinitionVirtualMethodReturnValue, subclass.VirtualMethod());
Assert.AreEqual(ClassFromClassDefinition.ClassFromClassDefinitionVirtualMethodReturnValue, subclassAsSuper.VirtualMethod());

This is illustrated by following picture:

classinheritancevirtualoverride1

Now, don’t be fooled in thinking the ‘virtual’ keyword resulted in the above behaviour. It is actually a cooperation of the ‘virtual’ and ‘override’ keywords.
If you replace the ‘override’ keyword with the ‘new’ keyword you get the old behaviour of hiding and casting to the supertype will reveale the hidden supertype member.
Thus ‘virtual’ creates the opportunity for users of your class to use overriding or hiding behaviour by choosing the either the ‘new’ or ‘override’ keyword.

However, what happens when using ‘third generation’ inheritance, thus:

public class ClassDefinition
public class ClassFromClassDefinition : ClassDefinition
public class ThirdGenerationClass : ClassFromClassDefinition

There are a few combinations possible here which I will not all explain in this article.
You can download the associated code which provides sample implementations of the combinatons and assertions to see what happens.

The general conclusion we can make is the following:
The ‘virtual’ keyword marks a method as being a candidate for overriding and is generally used in the superclass
The ‘overriding’ keyword marks a method as overriding and is generally used in the subclass
The ‘new’ keyword marks a method as hiding the superclass member
The combination of the ‘new’ and ‘virtual’ keywords in the middle of an inheritance tree marks a method as hiding the implementation of all classes below and make the method a candidate for overriding for all classes above this class in the inheritance tree

Download

You can download the sample code here. To run it you will need MbUnit because that is used to make the assertions. However, just studying the code will also get you a long way. Enjoy…

Links

Superclass (computer science)
Subclass (computer science)
Virtual method table

Exploring Workflow Foundation Part 7: Custom Designers – The FreeformActivityDesigner class

Let’s start with a disclaimer here:
First, this is the result of experimenting and watching the Microsoft dll’s with Redgate’s Reflector. This means the code is for demonstartion purposes only and is not at all production ready.
Second, the FreeFoemActivityDesigner is a huge subject. This post describes some scenario’s of what you can do with it but is by no means complete. As I have an idea for using this designer, the subjects handled are choosen with that idea in mind. There are a lot of other possibilities which I will not cover and to whome I don’t know the answer.
Third, because of point two, if you have any questions: I probably don’t know the answer!! But you can ask them anyway.

So, let’s get started…

Adding, Removing and Editing contained Activities

The FreeFormActivityDesigner has in its inheritance tree the CompositeActivityDesigner. This means it is meant to be used for activities deriving from CompositeActivity and thus you can

add other activities to it which will result in addng other ActivityDesigners to your FreeFormActivityDesigner derived class.

The most important methods for managing the contained activities are:

public virtual bool CanInsertActivities(HitTestInfo insertLocation, 
  ReadOnlyCollection<Activity> activitiesToInsert);
public virtual bool CanMoveActivities(HitTestInfo moveLocation, 

orums

  ReadOnlyCollection<Activity> activitiesToMove);
public virtual bool CanRemoveActivities(
  ReadOnlyCollection<Activity> activitiesToRemove);
 
protected virtual void OnContainedActivitiesChanged(
  ActivityCollectionChangeEventArgs listChangeArgs);
protected virtual void OnContainedActivitiesChanging(
  ActivityCollectionChangeEventArgs listChangeArgs);
protected virtual void OnContainedActivityChanged(
  ActivityChangedEventArgs e);
 
public virtual void InsertActivities(HitTestInfo insertLocation, 
  ReadOnlyCollection<Activity> activitiesToInsert);
public virtual void MoveActivities(HitTestInfo moveLocation, 
  ReadOnlyCollection<Activity> activitiesToMove);
public virtual void RemoveActivities(ReadOnlyCollection<Activity> activitiesToRemove);

The methods starting with “Can” and returning a boolean allow you to specify if activities can be acted upon in the composite activity being designed. What action is clear form the name of the method. Mind however that the Move is “Move between designers” and NOT “Move within a designer”.

However, don’t let these methods and their names fool you: they are there for your use and the framework only uses them in two cases:
The first case is the CanRemoveActivities method which gets called if you have an ActivityDesigner selected inside your FreeFormActivityDesigner inherited object and you press the Delete key. If you return false from the method call, your selected activity will not get removed.
The second case is the CanInsertActivities method which only gets called if you Paste an activity in your FreeFormActivityDesigner inherited object.

Bug or Feature?
You’d think that implementing these methods would offer you some benefit during editing of the composite activity but you’d be wrong.
If you want to prevent an activity from being inserted you have to override the OnDragOver method and call the CanInsertActivities from there. Based on the result you’d set the Effect property of the ActivityDragEventArgs argument to Move|Copy or None if insertion is allowed or not.
What I wonder is why Microsoft didn’t implement this behaviuor in there DragDropManager messagefilter.
Same goes for the CanMoveActivities. This method is supposed to signal weather activities can be moved from one designer to another. Again you’d think it would be used during drag/drop manipulations but it isn’t. You’re again on your own to implement this functionality.

So one wonders: is this a bug or a feature. And if it is a feature, then what can be gained by it?

The “On” prefixed methods are more or less self-documenting: the OnContainedActivities methods get called when the contained activities collection is changing or changed. The OnContainedActivityChanged is called when a contained activity is changed. Mind the nuance: the OnContainedActivities methods are called when the collection of contained activities changes, the OnContainedActivityChanged when an activity itself is changed. This last also means changing a property, moving the activity etc…

The last action oriented methods are called when effectively performing the action. The MoveActivities method is called when moving an activity from another designer inside your designer and NOT when moving an activity from your designer into another designer. Infact, in this last case not even the RemoveActivities method is called!!!

Making Connections

The FreeFormActivityDesigner allows you to make connections as you want it, in contrast with for example the Microsoft supplied SequentialActivityDesigner which makes the connections for you. In the last one, there is a default vertical line representing the connection between the start and the end, and you drop activities on this vertical line.

With the FreeFormActivityDesigner you can drop Activities anywhere you want, and they do not get connected by default. Then if you hover over the activity, connectionponts are made visible. Pressing the left mousebutton on such a point and dragging the mouse creates a connection which you can then attach to another activity.

But what is really happening under the hood, and how can you interfere with it?

There are two stages in making connections between activities:

  1. A graphical stage inside the FreeFormActiviyDesigner in which a graphical representation is created of the connection and in which it is also
    decided if a connection can be made. More on this last further
  2. A “business” stage in which the activities are connected. Allthough there is no built-in support to decide if a connection can be made, you should of course also validate this here, or (actually “and”) in the ActivityValidator inherited class for you activity.

Making connections between activity designers

When you press the left mousebutton on an activities connectionpoint and start moving the mouse (keeping the button pressed), deep down in the bowels of the workflow design experience the CreateConnector method of the FreeFormActivityDesigner to which the activity is a child is called with the ConnectionPoint data of the connection point pressed.
You are now given the chance to return your own custom Connector derived class. If for some reason the source connection point is not allowed to function as a source, you simply return null from this method.

When you continue to move the mouse, two methods are continouosly polled:

  • The CanConnectContainedDesigners method of your FreeFormActivityDesigner derived class
  • The CanConnect of your ActivityDesigner derived class

These two give you the chance to tell the designer environment if the connection between the activities can be made. If you return true, then the connector will snap to the target connection point and if you return false nothing will happen.

Connection points

As stated above, connections are made between connection points and your desicion if connections can be made is based on these connection points.
The question is of course how to specify and identify connection points.

Specifying connection points is also a two stage process:

  1. First, you specify the coordinates of the points on the designers edge which can be used as connection points by overriding the GetConnections(DesignerEdges edges) member of the ActivityDesigner class
  2. Next, you specify which connection points will be made visible when the user hovers the mouse over the ActivityDesigner by overriding the GetConnectionPoints(DesignerEdges edges) member

Identifying connection points is done on a designer-edge basis. As you can see from the signature of the above memtioned members, connection points are asked by specifying the edge along which they reside. Mind you however that this DesignerEdges enumeration has the FlagsAttribute attribute, so combinations of it’s members are allowed. Thus, you typically will have code like this:

if((edges & DesignerEdges.Top) == DesignerEdges.Top)
{
  // Add your points for the top here
}
if ((edges & DesignerEdges.Bottom) == DesignerEdges.Bottom)
{
  // Add your points for the bottom here
}

The second property which identifies the connection points is it’s connection index. It’s what? It’s “connection index”. Remember you specified the possible points on the designer edge that could be used as conneciton points. Remember also that you returned these on an DesignerEdge basis and in a collection. Now this “connection index” is the index in this collection to which the connection points map. Or visually:

So, to get the point coordinates mapping to the provided ConnectionPoint, you get the collection of points for the corresponding DesignerEdge, that is ConnectionEdge, and retrieve the point by index in the collection using ConnectionIndex:

Dictionary<DesignerEdges, List<Point>> pointsByEdge;
ConnectionPoint c;
 
Point p  = pointsByEdge[c.ConnectionEdge][c.ConnectionIndex];

Making connections between activities

Of course, all this makes no sense if these connections somehow eventually don’t show up in tree of activities to execute.

To manage this tree you have these methods to override in the FreeFormActivityDesigner:

protected virtual void OnConnectorAdded (ConnectorEventArgs e)
protected internal virtual void OnConnectorChanged (ConnectorEventArgs e)
protected virtual void OnConnectorAdded (ConnectorEventArgs e)
protected internal virtual void OnContainedDesignersConnected (
    ConnectionPoint source,
    ConnectionPoint target
)

And this single method in the ActivityDesigner:

protected virtual void OnConnected (
  ConnectionPoint source,
  ConnectionPoint target
)

What you do inside these methods is of course dependent on the type of CompositeActivity you are designing. But generaly you will somehow “connect”/”disconnect” the two activities uner design. How you do this is entirely up to you.

Downloads

In the accompening code download you will find some sample code mostly demonstrating making connection.

Links

FreeformActivityDesigner and Connectors question…
Designer wish list?
FlowChartworkflow – Custom FreeformActivityDesigner sample

Exploring Workflow Foundation Part 6.2: Custom Designers – The ActivityDesigner class and customization with drawing

While starting to experiment with the FreeFormActivityDesigner (about which the following post will handle), I did some further exploration of the ActivityDesigner base class and what can be done with custom drawing. That will be the subject of this post.

Overriding the OnPaint event

This one is rather simple and complex at the same time.

Overriding the OnPaint method gives you with the argument the chance to implement your own custom drawing functionality. There is however one caveat: the coordinates for positioning drawing elements on the screen are logical coordinates. Thus, the upper^left corner of your designer is not at coordinates (0,0) but at (ActivityDesigner.Bounds.X,ActivityDesigner.Bounds.Y)

So, to draw a black circle for your activity, you use following code:

protected override void OnPaint(ActivityDesignerPaintEventArgs e)
{
  e.Graphics.FillEllipse(new SolidBrush(Color.Black), Bounds);
}

Now, isn’t this simple? Yes it is.

Then where is the complexity? If you are knowledgeable with the Graphics object I suppose there is no complexity. But if you’re not, you have another subject to study.

Recap: Designer – Activity interaction

In the previous post I allready showed you how to get at the activity associated with the designer. Afterall: the “raison de vivre” of the ActivityDesigner is the Activity it represents.

When setting properties of the Activity, the designer framewotk also triggers a redraw of the associated ActivityDesigner and thus calls the OnPaint method. This allows you to update the visuals of your designer using the properties of the activity.

Glyphs: why do we need them

When starting to experiment with this custom drawing I also started wondering why one would need custom glyphs. The reason however is very simple: the Graphics object you receive in the OnPaint method only allows you to draw inside the rectangular Bounds defined box of the ActivityDesigner. Glyphs however allow you to draw outside of this box.

An application of this could be if you for example want to render connection ports on the boundary of your custom ActivityDesigner.

Glyphs are associated with an ActivityDesigner and can draw relative to this associated designer. You can see this by the signature of the methods:

public override System.Drawing.Rectangle GetBounds(ActivityDesigner designer, bool activated)
protected override void OnPaint(System.Drawing.Graphics graphics, 
  bool activated, 
  AmbientTheme ambientTheme, 
  ActivityDesigner designer)

As you can see from these signatures, they all have an ActivityDesigner parameter passed in. This is the designer associated with the glyph. It allows you to get it’s bounds and position your glyph relative to it.

Downloads

In the associated code you can find sample code for the above explanations. Hope you enjoy it.