Customizing Shape with Dynamic Counts control points?

May 7, 2014 at 2:01 AM
hi,KurtHolzinger
Thanks for your help last time at the thread
https://nshape.codeplex.com/discussions/397514#post1237005
And now I encounter a new problem.In the last thread I could create a composite shape programmatically with your help.But now I want to customize a shape named "MyShape" that derived from RectangleBase Class.It looks like the composite shape above but have Dynamic Counts control points.So that I can connect to the childShape programmatically.
I have look at the the NShape document "Programmer Task"->"Developing a new shape class".
I could draw the shape looks like the composite shape in the method "protected override bool CalculatePath()".And I have looked at the source code which contains 9 or 13 control points.These control points are set before running time just like
new public class ControlPointIds
        {
            /// <summary>ControlPointId of the top left control point.</summary>
            public const int TopLeftControlPoint = 1;
            /// <summary>ControlPointId of the top center control point.</summary>
            public const int TopCenterControlPoint = 2;
            /// <summary>ControlPointId of the top right control point.</summary>
            public const int TopRightControlPoint = 3;
            /// <summary>ControlPointId of the middle left control point.</summary>
            public const int MiddleLeftControlPoint = 4;
            /// <summary>ControlPointId of the middle right control point.</summary>
            public const int MiddleRightControlPoint = 5;
            /// <summary>ControlPointId of the bottom left control point.</summary>
            public const int BottomLeftControlPoint = 6;
            /// <summary>ControlPointId of the bottom center control point.</summary>
            public const int BottomCenterControlPoint = 7;
            /// <summary>ControlPointId of the bottom right control point.</summary>
            public const int BottomRightControlPoint = 8;
            /// <summary>ControlPointId of the center control point.</summary>
            public const int MiddleCenterControlPoint = 9;
            /// <summary>ControlPointId of the top left connection point.</summary>
            public const int TopLeftConnectionPoint = 10;
            /// <summary>ControlPointId of the top right connection point.</summary>
            public const int TopRightConnectionPoint = 11;
            /// <summary>ControlPointId of the bottom left connection point.</summary>
            public const int BottomLeftConnectionPoint = 12;
            /// <summary>ControlPointId of the bottom right connection point.</summary>
            public const int BottomRightConnectionPoint = 13;
        }
        protected override int ControlPointCount
        {
            get { return 13; }
        }
So my question is that how can I give a control point to the "childShape" at the running time when customizing shapes.
Coordinator
May 7, 2014 at 11:12 AM
Edited May 7, 2014 at 11:12 AM
You have to
  • override ControlPointCount and return 1 (for the "Reference" point) + the sum of all children's connection points (or what ever you need).
  • override methods GetControlPointId, HasControlPointCapability, Connect, Disconnect, AttachGluePointToConnectionPoint, DetachGluePointFromConnectionPoint and GetConnectionInfos. Delegate the calls to the appropriate child shape.
  • re-introduce the ControlPointIds class (which is only for providing the controlPointId values as constants) and remove all constants from it (you cannot make it dynamic anyway).
I hope I did not forget anything.
May 8, 2014 at 9:05 AM
KurtHolzinger wrote:
You have to
  • override ControlPointCount and return 1 (for the "Reference" point) + the sum of all children's connection points (or what ever you need).
  • override methods GetControlPointId, HasControlPointCapability, Connect, Disconnect, AttachGluePointToConnectionPoint, DetachGluePointFromConnectionPoint and GetConnectionInfos. Delegate the calls to the appropriate child shape.
  • re-introduce the ControlPointIds class (which is only for providing the controlPointId values as constants) and remove all constants from it (you cannot make it dynamic anyway).
I hope I did not forget anything.
Thanks,KurtHolzinger.
I try to follow your steps today.And I encounter a new problem.My code is :
        protected override int ControlPointCount
        {
            get 
            { 
                return 9+this.PortCount; 
            }
        }
        
        //the children 
        public int PortCount
        {
            get;
            set;
        }
myShape = (MyShape)project1.ShapeTypes["MyShape"].CreateInstance();
                myShape.PortCount = 4;
Actually it doesn't work.I debug it and find the problem is that when executing the CreateInstance() method it will execute the
        protected internal override void InitializeToDefault(IStyleSet styleSet) {
            base.InitializeToDefault(styleSet);
            controlPoints = new Point[ControlPointCount];
            FillStyle = styleSet.FillStyles.Blue;
        }
So the controlPoints variable‘s Length is ControlPointCount( 9+PortCount) is 9 due to the PortCount is 0.And this will cause the out of range exception in this method
        public override Point GetControlPointPosition(ControlPointId controlPointId) {
            if (controlPointId == ControlPointId.Reference) {
                Point center = Point.Empty;
                center.X = X;
                center.Y = Y;
                return center;
            } else if (controlPointId == ControlPointId.None)
                throw new NShapeException("NotSupported PointId.");
            if (drawCacheIsInvalid) UpdateDrawCache(); 

            int index = GetControlPointIndex(controlPointId);
            return ControlPoints[index];
        }
And I think if I could construct MyShape based on the parameter PortCount then I could construct a shape with dynamic "child".
So how can I implement a constructor like "MyShape(int PortCount)"?Or how can I set the PortCount before InitializeToDefault method?
Thanks.
Coordinator
May 9, 2014 at 8:15 AM
You could use the ResizeControlPoints() method (defined in PlanarShapeBase) for resizing the ControlPoints array:
    public int PortCount
    {
        get { return _portCount; }
        set {
            if (_protCount != value) {
                _portCount = value;
                ResizeControlPoints(_portCount);
            }
        }
    }
When thinking about resizing the controlpoints array, I remember that you should also override CalcControlPoints where you assign the positions of the (unrotated) control points (the coordinates of the points are relative to shape.X == 0 and shape.Y == 0).