/// <summary>
        /// Configures custom port handling with the help of <see cref="ILookup"/>.
        /// </summary>
        /// <remarks>
        /// When a user interacts with edges and their endpoints,
        /// node.Lookup(IPortCandidateProvider) is called for the nodes in that graph,
        /// and the framework returns the implementation of IPortCandidateProvider which
        /// supplies the list of available ports.
        ///
        /// Instead of the default, we'll register a custom lookup for type IPortCandidateProvider.
        ///
        /// Note: we'll update this method in a future tutorial step to work with folding.
        /// </remarks>
        private void CustomizePortHandling()
        {
            // Sets auto cleanup to false, since we don't want to remove unoccupied ports.
            Graph.NodeDefaults.Ports.AutoCleanUp = false;

            // First we create a GraphDecorator from the IGraph.
            // GraphDecorator is a utility class that aids in decorating model items from a graph instance.

            // Here, we call NodeDecorator.PortCandidateProviderDecorator
            // to access the lookup decorator for ports - the thing we want to change.

            // One way to decorate the graph is to use the factory design pattern.
            // We set the factory to a lambda expression which
            // returns instances that implement the IPortCandidateProvider interface.

            // Here we can create a CompositePortCandidateProvider that combines various port candidate providers.
            // The ExistingPortsCandidateProvider provides port candidates at the locations of the already existing ports.
            // The NodeCenterPortCandidateProvider provides a single port candidate at the center of the node.
            // The ShapeGeometryPortCandidateProvider provides several port candidates based on the shape of the node.
            Graph.GetDecorator().NodeDecorator.PortCandidateProviderDecorator.SetFactory(
                node => PortCandidateProviders.Combine(
                    PortCandidateProviders.FromExistingPorts(node),
                    PortCandidateProviders.FromNodeCenter(node),
                    PortCandidateProviders.FromShapeGeometry(node)));

            // To modify the existing lookup for a graph element, typically we decorate it with the help
            // of one of graph's Get...Decorator() extension methods,
            // which allows to dynamically insert custom implementations for the specified types.
            // Doing this can be seen as dynamically subclassing
            // the class in question (the INode implementation in this case), but only
            // for the node instances that live in the graph in question and then
            // overriding just their Lookup(Type) method. The only difference to traditional
            // subclassing is that you get the "this" passed in as a parameter.
            // Doing this more than once is like subclassing more and more, so the order matters.
        }
Beispiel #2
0
 internal CustomPortCandidateProvider(INode node)
 {
     this.node = node;
     geometryPortCandidateProvider =
         PortCandidateProviders.Combine(
             PortCandidateProviders.FromNodeCenter(node),
             PortCandidateProviders.FromShapeGeometry(node));
 }
Beispiel #3
0
        /// <summary>
        /// Creates an <see cref="IPortCandidateProvider"/> that considers the node's shape and rotation.
        /// </summary>
        private static IPortCandidateProvider CreatePortCandidateProvider(INode node)
        {
            var rotatedPortModel = RotatablePortLocationModelDecorator.Instance;
            var freeModel        = FreeNodePortLocationModel.Instance;

            var rnsd    = (RotatableNodeStyleDecorator)node.Style;
            var wrapped = rnsd.Wrapped;
            var sns     = wrapped as ShapeNodeStyle;

            if (wrapped is ShinyPlateNodeStyle || wrapped is BevelNodeStyle ||
                sns != null && sns.Shape == ShapeNodeShape.RoundRectangle)
            {
                return(PortCandidateProviders.Combine(
                           //Take all existing ports - these are assumed to have the correct port location model
                           PortCandidateProviders.FromUnoccupiedPorts(node),
                           //Provide explicit candidates - these are all backed by a rotatable port location model
                           PortCandidateProviders.FromCandidates(
                               //Port candidates at the corners that are slightly inset
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(freeModel.CreateParameter(new PointD(0, 0), new PointD(5, 5)))),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(freeModel.CreateParameter(new PointD(0, 1), new PointD(5, -5)))),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(freeModel.CreateParameter(new PointD(1, 0), new PointD(-5, 5)))),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(freeModel.CreateParameter(new PointD(1, 1), new PointD(-5, -5)))),
                               //Port candidates at the sides and the center
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeLeftAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeBottomAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeCenterAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeTopAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeRightAnchored))
                               )));
            }
            if (sns != null && sns.Shape == ShapeNodeShape.Rectangle)
            {
                return(PortCandidateProviders.Combine(
                           PortCandidateProviders.FromUnoccupiedPorts(node),
                           PortCandidateProviders.FromCandidates(
                               //Port candidates at the corners
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeTopLeftAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeTopRightAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeBottomLeftAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeBottomRightAnchored)),
                               //Port candidates at the sides and the center
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeLeftAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeBottomAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeCenterAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeTopAnchored)),
                               new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(FreeNodePortLocationModel.NodeRightAnchored))
                               )));
            }
            if (sns != null)
            {
                // Can be an arbitrary shape. First create a dummy node that is not rotated
                var dummyNode = new SimpleNode
                {
                    Style  = sns,
                    Layout = node.Layout
                };
                var shapeProvider      = PortCandidateProviders.FromShapeGeometry(dummyNode, 0);
                var shapeCandidates    = shapeProvider.GetTargetPortCandidates(null);
                var rotatingCandidates =
                    shapeCandidates.Select(c => new DefaultPortCandidate(node, rotatedPortModel.CreateWrappingParameter(c.LocationParameter)));
                return(PortCandidateProviders.Combine(
                           PortCandidateProviders.FromUnoccupiedPorts(node),
                           PortCandidateProviders.FromCandidates(rotatingCandidates)));
            }
            return(null);
        }