Answered Create or remove ports per each dynamically added/removed compartmentItem

  • Monday, April 30, 2012 1:36 PM
     
     

    I would like to dynamically create a port per each CompartmentItem added to a compartment.  In my scenario I will be dynamically adding the underlying compartmentitems and visually would like a port per each compartmentitem that a user can add a link to connect to relating shapes.

    More likely than not the links will be reference relationships. 

    I do not want the user to be able to remove the port nor add new ones this must be controlled programatically.

    How would I accomplish this task?


    Johnny Larue

All Replies

  • Thursday, May 03, 2012 1:27 PM
    Owner
     
     Answered Has Code

    Adding and removing ports automatically as compartment items changed is just a case of adding a new Port shape to your DSL and mapping it to the model element that is contained in the compartment. You shouldn't need to write any custom code to do this - the generated code and diagram fix-up rules will take care of adding and removing the port shapes.

    However, you would need to write custom code if you wanted to align the ports next to the corresponding compartment item. I haven't tried this, but I think you would need to write a custom BoundsRule. There are examples of how to do this int the Circuits sample. Normally, the positions of ports are set using the PortMovementRule.

    By default, users will be able to delete the port shapes, but if they do then the undelying compartment item will also be deleted (similarly, deleting the compartment item will remove the corresponding port shape). If you want to prevent the user from being able to delete the compartment item/port, you would need to provide a custom ElementOperations implementation.

    The sample code below shows how to do this.

    Regards,

    Duncan

    	/* Dsl project.
    	 * This code prevents stops elements of type ClassOperation from being
    	 * deleted by the user unless the parent ModelClass is also deleted.
    	 */
    	/// <summary>
    	/// Custom element operations class that changes the delete behaviour
    	/// </summary>
    	public class CustomElementOperations : DesignSurfaceElementOperations
    	{
    		// Required constuctor
    		public CustomElementOperations(IServiceProvider serviceProvider, Diagram diagram)
    			: base(serviceProvider, diagram)
    		{
    		}
    
    		// Required constuctor
    		public CustomElementOperations(IServiceProvider serviceProvider, Partition modelPartition)
    			: base(serviceProvider, modelPartition)
    		{
    		}
    
    		public override bool CanDelete(ModelElement element, params Guid[] domainRolesToNotPropagate)
    		{
    			// Can't delete an ClassOperation on its own
    			if (element is ClassOperation)
    			{
    				return false;
    			}
    
    			return base.CanDelete(element, domainRolesToNotPropagate);
    		}
    
    		public override bool CanDelete(IEnumerable<ModelElement> elements, params Guid[] domainRolesToNotPropagate)
    		{
    			// Can only delete a ClassOperation if the owning ModelClass is being deleted too.
    			IEnumerable<ClassOperation> ops = elements.OfType<ClassOperation>();
    			if (ops != null && ops.Count() > 0)
    			{
    				return ops.All(o => o.ModelClass == null || elements.Contains(o.ModelClass));
    			}
    
    			return base.CanDelete(elements, domainRolesToNotPropagate);
    		}
    
    	}
    
    	partial class ForumCompartmentLanguage1Diagram
    	{
    		//Replace the standard element operations instance used by the diagram
    		private DesignSurfaceElementOperations elementOps;
    		public override Microsoft.VisualStudio.Modeling.Diagrams.DesignSurfaceElementOperations ElementOperations
    		{
    			get
    			{
    				if (this.elementOps == null)
    				{
    					this.elementOps = new CustomElementOperations(this.Store, this);
    				}
    				return this.elementOps;
    			}
    		}
    	}
    

    	/* DslPackage project.
    	 * Ensures the explorer tool window uses the custom element operations class.
    	 */
    	partial class ForumCompartmentLanguage1Explorer
    	{
    		//Replace the standard element operations instance used by the explorer
    		protected override Microsoft.VisualStudio.Modeling.ElementOperations ElementOperations
    		{
    			get
    			{
    				Microsoft.VisualStudio.Modeling.ElementOperations elOps = base.ElementOperations;
    				if (!(elOps is CustomElementOperations))
    				{
    					return new CustomElementOperations(this.ModelingDocData, (this.ModelingDocData.RootElement != null) ? this.ModelingDocData.RootElement.Partition : this.ModelingDocData.Store.DefaultPartition);
    
    				}
    				return elOps;
    			}
    		}
    	}
    

  • Thursday, May 03, 2012 9:18 PM
     
     

    Awesome and thanks...

    I am having one issue though which I can't figure out.  I am getting this error

    Error 4 A Port may not be parented on a Diagram. In the ShapeMap mapping ApplicationNavigationContainer to ApplicationNavContainerPortShape, the parent element path leads to ApplicationNavigation, whose mapped shape is LightSwitchNavigationLayoutDiagram.

    I am mapping my port shape to a Model Element (called ApplicationNavigationContainer) whose collection is assigned to a Compartment Map associated to another Model Element (which is calld RootShell). 

    I also have a second mapping on the Model Element ApplicationNavigationContainer to a geometry shape.

    I fail to see why I getting the above error as the Model Element ApplicationNavigationContainer is mapped to Geometry Shape as well is within a Compartment Shape (as a Compartment).

    The Parent element path = ApplicationNavigationHasApplicationNavigationContainers.ApplicationNavigation/!ApplicationNavigation

    What am I doing wrong?

    Johnny Larue








  • Thursday, May 03, 2012 10:44 PM
     
     

    I think I figured out the issue I was having,  all my model elements were/are related through embedded relationships from the root model element and the this works fine as my ApplicaitonNavigationContainer model element also has a referenced relationship to the Model element that was mapped to the Compartment Geometry shape and which ApplicaitonNavigationContainer is a Compartment. 

    When I remove the Embeded relationship from the root Model Element on ApplicationNavigationContainer and remove the Referenced relationship and replace with an embeded relationship the error is resolved.

    I am new to the DSL modeling framework and I am glad to have discovered this one earlier rather than later as I was tending to model most of my Elements to the root element which would have caused me much grief sooner than later!

    Cheers


    Johnny Larue

  • Friday, May 04, 2012 5:15 AM
    Owner
     
     

    Yep; ultimately all domain classes will be embedded under the model root, although often this relationship will be indirect (e.g. in a model of code you might embed "ModelClass" under the root, and embed "Operation" and "Property" under the ModelClass).

    Have a look at the DSL models created using the Class Diagrams and Component Models templates for more complex examples of domain models.

    Regards,

    Duncan

  • Friday, May 04, 2012 1:55 PM
     
     

    Hey Duncan thanks for your insight and support, it is very much appreciated !!

    Athough I am new to this space and still have lots to learn I am already doing things that would have taken a vast amount of time to accomplish otherwise as the DSL Tool Framework within Visual Studio is incredibly powerful and there is lots of documentation available.  The integration within Visual Studio is also very cool, also new to me as well.

    One of the most in-depth resouces I have found so far is the book Domain-Specific Developement With Visual Studio DSL Tools authoured by Steve Cook, Gareth Jones and Stuart Kent and Allan Cameron Willis.  I highly recommend this book to others that are new to this space like I am. It's an easy read and gives a solid subject matter footing.


    Johnny Larue

  • Friday, May 04, 2012 4:47 PM
     
      Has Code

    Duncan great advice on the BoundsRule, very much what I need to do.

    So now I have my port shapes being automatically added to my Compartment shape based upon the number of ApplicationNavigationContainers I add to the RootShell model element (shown as ArcherShellRoot).

    Now I need to figure out the x and y position of each port shape so that each port shape is aligned to the respective Compartment Item which I know now (thanks to Duncan's advice) is accomplished within the BoundRule.

    This how it looks when the Model Element is first placed onto the Model...

    ... and below (manually arranged for illustrative purposes) is what I am hoping to achieve within my BoundsRule logic  ...

    What I need assistance with now, is how to determine the x and y for my port with respect to each compartment item. 

    How do I get the position of the CompartmentItem TextDecorator (shapeField ??) and then how do I ensure that my port is to the right side, middle aligned?

    I am can see two ShapeFields on the componentShape one is a Microsoft.VisualStudio.Modeling.Diagrams.CompartmentShapeAreaField the other is the text decorator for the name.

    What I think I need to do is get the ShapeFields within the CompartmentShapeAreaField  so I can acquire the related TextDecorator ShapeField but I am really not sure.

    Heres the code so far which isolates the port shape and the compartment shape (which is called per each port)

     #region ApplicationNavContainerPortShape 
        public partial class ApplicationNavContainerPortShape
        {
            public override BoundsRules BoundsRules
            {
                get
                {
                    return new ApplicationNavContainerPortShapeBoundsRule();
                }
            }
        }
        public class ApplicationNavContainerPortShapeBoundsRule : BoundsRules
        {
            public override RectangleD GetCompliantBounds(ShapeElement shape, RectangleD proposedBounds)
            {
               
                var applicationNavigationContainer = shape.ModelElement as ApplicationNavigationContainer;
                if (applicationNavigationContainer == null) return proposedBounds;
                var applicationNavContainerPortShape = shape as ApplicationNavContainerPortShape;
                if (applicationNavContainerPortShape != null)
                {
                    var componentShape = applicationNavContainerPortShape.ParentShape as IDockShellRootShape;
                    if (componentShape == null) return proposedBounds;
                    // ToDo :   Locate the respective Compartment Item's current x/y position 
                    // ToDo :   Return RectangleD corresponding to new position of ApplicationNavContainerPortShape
                }
                return proposedBounds;
            }
        }
        #endregion


    Johnny Larue