/// <summary> /// Returns a list that contains a port candidate for each of the node's /// ports. Each candidate has the same location as the port. If a port /// already has a connected edge, its port candidate is marked as invalid. /// </summary> /// <remarks> /// Note that the variants of getPortCandidates for target ports are all /// implemented by this class. Therefore, this method is only used for /// source ports. /// </remarks> protected override IEnumerable <IPortCandidate> GetPortCandidates(IInputModeContext context) { List <IPortCandidate> candidates = new List <IPortCandidate>(); bool hasValid = false; var graph = context.GetGraph(); if (graph != null) { // Create a port candidate for each port on the node foreach (IPort port in node.Ports) { DefaultPortCandidate portCandidate = new DefaultPortCandidate(port); bool valid = graph.OutDegree(port) == 0; hasValid |= valid; portCandidate.Validity = valid ? PortCandidateValidity.Valid : PortCandidateValidity.Invalid; candidates.Add(portCandidate); } } // If no valid candidates have been created so far, use the ShapeGeometryPortCandidateProvider as fallback. // This provides a candidate in the middle of each of the four sides of the node. if (!hasValid) { candidates.AddRange(PortCandidateProviders.FromShapeGeometry(node).GetSourcePortCandidates(context)); } return(candidates); }
/// <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. }
internal CustomPortCandidateProvider(INode node) { this.node = node; geometryPortCandidateProvider = PortCandidateProviders.Combine( PortCandidateProviders.FromNodeCenter(node), PortCandidateProviders.FromShapeGeometry(node)); }
/// <summary> /// Called upon loading of the form. /// This method initializes the graph and the input mode. /// </summary> /// <seealso cref="InitializeInputModes"/> /// <seealso cref="InitializeGraph"/> protected override void OnLoad(EventArgs e) { base.OnLoad(e); description.LoadFile(new MemoryStream(Resources.description), RichTextBoxStreamType.RichText); //Keep shared instances of copied node styles graphControl.Clipboard.FromClipboardCopier.Clone = graphControl.Clipboard.FromClipboardCopier.Clone & ~GraphCopier.CloneTypes.NodeStyle; graphControl.Clipboard.ToClipboardCopier.Clone = graphControl.Clipboard.ToClipboardCopier.Clone & ~GraphCopier.CloneTypes.NodeStyle; // write symbolic style name in graphml to make the nodes use the default style when imported, not the specific style instance // otherwise, 'change color' wouldn't work on imported nodes graphControl.GraphMLIOHandler.QueryReferenceId += delegate(object sender, QueryReferenceIdEventArgs args) { if (args.Value == defaultStyle) { args.ReferenceId = "defaultStyle"; } }; // if node style is string "defaultStyle" in graphml, assign defaultStyle graphControl.GraphMLIOHandler.ResolveReference += delegate(object sender, ResolveReferenceEventArgs args) { if (args.ReferenceId == "defaultStyle") { args.Value = defaultStyle; } }; graphControl.Graph.SetUndoEngineEnabled(true); UndoEngine engine = graphControl.Graph.GetUndoEngine(); if (engine != null) { engine.AutoMergeTime = TimeSpan.FromMilliseconds(100); engine.PropertyChanged += delegate { UpdateUndoState(); }; } UpdateUndoState(); // Add some more interesting port candidates. GraphDecorator decorator = graphControl.Graph.GetDecorator(); decorator.NodeDecorator.PortCandidateProviderDecorator.SetFactory( node => PortCandidateProviders.FromShapeGeometry(node)); // add callbacks to the business objects at the nodes in the graph graphControl.Graph.NodeCreated += OnNodeCreated; graphControl.Graph.NodeRemoved += OnNodeRemoved; // initialize the graph InitializeGraph(); // reset the Undo queue so the initial graph creation cannot be undone graphControl.Graph.GetUndoEngine().Clear(); // initialize the input mode InitializeInputModes(); }
/// <summary> /// Returns a central port candidate if the owner node of the source /// candidate is green, and an empty list otherwise. /// </summary> public override IEnumerable <IPortCandidate> GetTargetPortCandidates(IInputModeContext context, IPortCandidate source) { // Check if the source node is green if (Color.Green.Equals(source.Owner.Tag)) { return(PortCandidateProviders.FromNodeCenter(node).GetTargetPortCandidates(context, source)); } else { return(new IPortCandidate[0]); } }
/// <summary> /// Callback used by the decorator in <see cref="CreateEditorMode"/> /// </summary> private IPortCandidateProvider GetPortCandidateProvider(INode forNode) { var model = new MyNodePortLocationModel { Inset = 10 }; return(PortCandidateProviders.FromCandidates( new DefaultPortCandidate(forNode, model.CreateParameter(PortLocation.Center)), new DefaultPortCandidate(forNode, model.CreateParameter(PortLocation.North)), new DefaultPortCandidate(forNode, model.CreateParameter(PortLocation.East)), new DefaultPortCandidate(forNode, model.CreateParameter(PortLocation.South)), new DefaultPortCandidate(forNode, model.CreateParameter(PortLocation.West)))); }
private void CreateSampleGraph() { IGraph graph = graphControl.Graph; CreateNode(graph, 100, 100, 80, 30, Colors.Firebrick, "No Edge"); CreateNode(graph, 350, 100, 80, 30, Colors.Green, "Green Only"); CreateNode(graph, 100, 200, 80, 30, Colors.Green, "Green Only"); CreateNode(graph, 350, 200, 80, 30, Colors.Firebrick, "No Edge"); // The blue nodes have predefined ports var portStyle = new ColorPortStyle(); var blue1 = CreateNode(graph, 100, 300, 80, 30, Colors.RoyalBlue, "One Port"); graph.AddPort(blue1, blue1.Layout.GetCenter(), portStyle).Tag = Colors.Black; var blue2 = CreateNode(graph, 350, 300, 100, 100, Colors.RoyalBlue, "Many Ports"); var portCandidateProvider = PortCandidateProviders.FromShapeGeometry(blue2, 0, 0.25, 0.5, 0.75); portCandidateProvider.Style = portStyle; portCandidateProvider.Tag = Colors.Black; var candidates = portCandidateProvider.GetSourcePortCandidates(graphControl.InputModeContext); foreach (IPortCandidate portCandidate in candidates) { if (portCandidate.Validity != PortCandidateValidity.Dynamic) { portCandidate.CreatePort(graphControl.InputModeContext); } } // The orange node CreateNode(graph, 100, 400, 100, 100, Colors.Orange, "Dynamic Ports"); INode n = CreateNode(graph, 100, 540, 100, 100, Colors.Purple, "Individual\nPort Constraints"); AddIndividualPorts(graph, n); n = CreateNode(graph, 350, 540, 100, 100, Colors.Purple, "Individual\nPort Constraints"); AddIndividualPorts(graph, n); }
/// <summary> /// Creates the sample graph of this demo. /// </summary> private void CreateSampleGraph(GraphControl graphControl) { var graph = graphControl.Graph; var blackPortStyle = new NodeStylePortStyleAdapter(new ShapeNodeStyle { Shape = ShapeNodeShape.Ellipse, Brush = Brushes.Black, Pen = null }) { RenderSize = new SizeD(3, 3) }; graph.SetUndoEngineEnabled(true); CreateSubgraph(graph, Colors.Firebrick, 0); CreateSubgraph(graph, Colors.Orange, 200); CreateSubgraph(graph, Colors.Green, 600); // the blue nodes have some additional ports besides the ones used by the edge var nodes = CreateSubgraph(graph, Colors.RoyalBlue, 400); graph.AddPort(nodes[0], FreeNodePortLocationModel.Instance.CreateParameter(new PointD(1, 0.2)), blackPortStyle); graph.AddPort(nodes[0], FreeNodePortLocationModel.Instance.CreateParameter(new PointD(1, 0.8)), blackPortStyle); var candidateProvider = PortCandidateProviders.FromShapeGeometry(nodes[2], 0, 0.25, 0.5, 0.75); candidateProvider.Style = blackPortStyle; IEnumerable <IPortCandidate> candidates = candidateProvider.GetSourcePortCandidates(graphControl.InputModeContext); foreach (IPortCandidate portCandidate in candidates) { if (portCandidate.Validity != PortCandidateValidity.Dynamic) { portCandidate.CreatePort(graphControl.InputModeContext); } } graph.GetUndoEngine().Clear(); }
/// <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); }