Preventing connection to a given connection point

Oct 11, 2012 at 8:22 AM
Edited Oct 11, 2012 at 11:59 AM

Hi,

I'm trying to prevent connection to connection point #4 (and #5) if this point is already connected to some other shapes. This is the code I'm using:

 

protected override bool IsConnectionPointEnabled(ControlPointId pointId)
{
    if (pointId != 4 && pointId != 5)
        return false;
    bool notConnected = (this.IsConnected(pointId, null) == ControlPointId.None);
    return notConnected;
}

 

However, this brings me an error "Passive shape's point 4 is not a connection point" (in CachedRepository.CanInsert() method) when I finish connecting some active shape to point #4 of my custom shape (using a Polyline).

Is there any workaround?

[UPDATE] I was also thinking about overriding the Connect() method of my Polyline-based line (used for connecting shapes), but in such case all prevention logic will be encapsulated in this method - and this seems to be not correct: I think that it is the passive shape that must contain this logic because this logic is specific for each shape type.

Coordinator
Oct 11, 2012 at 12:04 PM
Edited Oct 11, 2012 at 12:05 PM

The result of IsConnectionPointEnabled has to be consistent because there are checks against it (as you have already noticed). The framework is not designed to toggle control point capabilities at runtime, depending on the state of the shape.

I'm not sure if this works or if it has any side effects, but you can try to implement this behavior in "GetControlPoints":

public override IEnumerable<ControlPointId> GetControlPointIds(ControlPointCapabilities controlPointCapability) {
    foreach (ControlPointId id in base.GetControlPointIds(controlPointCapability)) {
        if ((controlPointCapability & ControlPointCapabilities.Connect) != 0 && IsConnected(id, null) != ControlPointId.None)
            continue;
        yield return id;
    }
}

Oct 11, 2012 at 12:14 PM

Thank you. Seems to work!

However, it is possible that some side effects can occur... maybe it would be better not to prevent connection but instead show some message like "Connection point already occupied. Connection is not valid"? but to do this I need to catch up the moment when a connection is created... but I cannot find neither ConnectionCreated event nor OnConnectionCreated() virtual method :( any workaround?!

Coordinator
Oct 11, 2012 at 12:41 PM

You can throw an exception in the Connect() method and handle it in the application.

Perhaps it is enough to handle the MouseUp event of the display (the event is triggered before calling the tool's ProcessMouseEvent method) or override the OnMouse-Methods of the display.

Finally, you could derive a new SelectionTool and prevent the connection there.

Oct 12, 2012 at 10:06 AM
KurtHolzinger wrote:

You can throw an exception in the Connect() method and handle it in the application.

Perhaps it is enough to handle the MouseUp event of the display (the event is triggered before calling the tool's ProcessMouseEvent method) or override the OnMouse-Methods of the display.

Finally, you could derive a new SelectionTool and prevent the connection there.

Thank you very much!

Regarding the Connect() method (1st sentence in your reply): I guess you're speaking about the Line shape (used for connecting other shapes), right? just to clarify...

Coordinator
Oct 12, 2012 at 11:14 AM

No, I thought about overriding the "AttachGluePointToConnectionPoint" method in your custom shape (which is a planar shape I think) which is called on the passive shape when connecting two shapes. Sorry for confusing you.

Oct 12, 2012 at 1:15 PM
Edited Oct 12, 2012 at 2:51 PM

Unfortunately the AttachGluePointToConnectionPoint() method is marked as internal, so it is not accessible from my custom shape library :(( 

/// <summary>
/// Called upon the passive shape of the connection by the active shape. 
/// If ownPointId is equal to ControlPointId.Reference, the global connection is meant.
/// </summary>
protected internal abstract void AttachGluePointToConnectionPoint(ControlPointId ownPointId, Shape otherShape, ControlPointId gluePointId);

[UPDATE] Is there any other method to override where I can throw exception and thus prevent connection? I think that processing mouse events in the Display component is not the right way to solve the problem...

Coordinator
Oct 15, 2012 at 8:01 AM

It is protected internal, so you can override it:

protected override void AttachGluePointToConnectionPoint(ControlPointId ownPointId, Shape otherShape, ControlPointId gluePointId) {
// ...
}

As you can read in the C# documentation, the protected internal declaration means "protected and internal" which in turn means the base classes can use it as if it is public (because of the internal access modifier) and you can overide it (because of the protected abstract access modifiers).

Oct 15, 2012 at 8:29 AM

Oh, I see: intellisense didn't offer me "AttachGluePointToConnectionPoint" after I typed "override " because the "AttachGluePointToConnectionPoint" method is also marked sealed in one of its predecessors (in ShapeBase class). To avoid this problem, I need to derive my shape directly from Shape class - but this means a lot of work to do...

OK, no problem: I used "service-oriented approach" - implemented a ConnectionValidator "service" and call it from MyConnectorLine.Connect() method passing sourceShape and targetShape as arguments and throwing a ConnectionException when needed.

Thank you very much for your help.

Coordinator
Oct 15, 2012 at 9:49 AM

I overlooked that the method is sealed in ShapeBase, sorry.

Oct 15, 2012 at 10:16 AM

Are there any heavy reasons to have it sealed?!

As you may see, a rather common task "Preventing connection to a given connection point" turned out to be a back breaker! I guess it shouldn't be such...

Coordinator
Oct 15, 2012 at 1:30 PM

We have sealed it because this is a rather basic function and changing it could in some cases screw up the whole connection mechanism. I have to discuss these issues with the NShape architect.

As I wrote in the other post, the designed way for "Preventing connection to a given connection point" would be to prevent the connection by implementing this logic in the appropriate tool classes.