DSL Tools #2 - Preventing Invalid Connections

Considering the model described in the previous post, I have a connector that allows the user to define the flow between FlowElements.

From the domain model, you can see that any given FlowElement can connect to any other FlowElement (any number in fact). A FlowElement - this is important - can be one of these concrete domain classes: Start, End, ActivityElement, or GatewayElement.

Although I modeled it this way - to simplify a number of things - there are certain rules that need to be enforced:

  • Start cannot be the target of any link.
  • End cannot be the source of any link.
  • You can cannot link two gateways.
  • You cannot link any element to itself.

Implementing these validations again requires custom code. But it's quite simple.

1. Assumptions

If you have a reference relation in you model (Flow in the sample) then you need a Connector in place to define the corresponding diagram element. I won't get into this since it's quite well described in the documentation.

Then you will also need a ConnectionTool defined in the Editor Toolbox Tabs designer. In my sample this tool is called FlowTool.

The interesting thing now is that for each ConnectionTool you will see a class generated (in ConnectionBuilders.cs file) that defines that tool's behavior. This class will have a name in the following form: <ToolName>ConnectAction (FlowToolConectAction in the sample).

2. Create a partial class for that FlowToolConnectAction

This generated class enforces the behavior required to implement our specific validation requirements. We'll need to extend it.

/// <summary>
/// Flow tool connection type.
/// </summary>
private partial class FlowToolConnectionType
{
    // ...
}

3. Override the CanCreateConnection function

CanCreateConnection is called whenever the user creates a link with the corresponding connection tool and moves the mouse over any element in the model.

All we need is to inspect which are the source and target elements and return false in those cases where we don't want to allow the connection to be created.

Here's the code to implement our 4 special requirements:

/// <summary>
/// Determines if a connection can be created between two elements.
/// </summary>
/// <param name="sourceShapeElement">Source element of the
///
connection.</param> /// <param name="targetShapeElement">Target element of the
/// connection.</param>
/// <param name="connectionWarning"></param> /// <returns>A boolean that indicates if a connection can be created
/// between two elements.</returns>
public override bool CanCreateConnection(ShapeElement
sourceShapeElement, ShapeElement targetShapeElement,
ref string connectionWarning) { // Validations if ((sourceShapeElement != null) && (targetShapeElement != null)) { // No element can link to itself if (sourceShapeElement == targetShapeElement) { connectionWarning =
Properties.Resources.CannotAddFlowToSelf; return false; } // Validations if the Target is a Start Point Start startTarget = targetShapeElement.ModelElement as Start; if (startTarget != null) { // Start cannot be the target of any link connectionWarning =
Properties.Resources.CannotFlowToStart; return false; } // Validations if the Source is an End Point End endSource = sourceShapeElement.ModelElement as End; if (endSource != null) { // End cannot be the source of any link connectionWarning =
Properties.Resources.CannotFlowFromEnd; return false; } // Validations if the Source is a Gateway GatewayElement gatewayElementSource =
(sourceShapeElement.ModelElement as GatewayElement); if (gatewayElementSource != null) { // Cannot link two gateways if ((targetShapeElement.ModelElement as GatewayElement)
!= null) { connectionWarning =
Properties.Resources.CannotAddFlowBetweenGateways; return false; } } } // Default behavior return base.CanCreateConnection(sourceShapeElement,
targetShapeElement, ref connectionWarning); }

There are a few extra things worth mentioning:

  • Be sure to invoke base.CanCreateConnection in the end of the function.
  • The connectionWarning parameter allows you to provide feedback to user on why the connection is not permitted. It will be presented as a small tooltip in the designer.
  • Notice also that the source and target elements are passed in as shapes. You can implement the validations using shapes or using model elements (as I did).
Published 14 August 07 10:14 by hgr
Filed under: ,

Comments

# Hugo Ribeiro said on August 14, 2007 10:15 AM:

Last weekend I found some time to mess with the Microsoft DSL Tools to learn how this thing really works.

# Hugo Ribeiro said on July 15, 2008 6:43 PM:

Since I've been doing a number of posts on the DSL Tools, I thought it would be helpful to have a list

Anonymous comments are disabled