Customization: Setting Position of a Shape When Its Created

There have been several questions on the DSL Tools forum about how to set shapes at specific locations when they're create. That type of custom placement that is definitely possible to do in our diagrams. Our modeling system has a feature called Rules that are fired within, or at the end of, a transaction (but before it's complete), this allows you to respond to changes in the model and make other changes in response because you're still within the transaction context. Therefore, to do that custom placement of shapes, you need to create a Rule.

 

A Rule is a class that derives from one of the Rules defined in the modeling system (in this case the AddRule), and overrides one or more of its methods (ElementAdded). It also needs a RuleOn attribute placed on the class. The attribute allows you to say what type the Rule works one and when the rule should fire (inline, local commit, and top level commit). Here's some code for such a rule:

       

      /// <summary>

      /// Rule to call programmatically set the position of a Shape when

      /// it's added to the diagram (directly or indirectly). This rule

      /// gets called when the transaction's top level commit is called.

      /// </summary>

      [RuleOn(typeof(ParentShapeContainsNestedChildShapes), FireTime = TimeToFire.TopLevelCommit)]

      public class ShapeAddedToDiagramRule : AddRule

      {

            private double offset = 0.25;

            private PointD location = new PointD(0.25, 0.25);

            public override void ElementAdded(ElementAddedEventArgs e)

            {

                  Shape shape = null;

                  ParentShapeContainsNestedChildShapes nestedLink = e.ModelElement as ParentShapeContainsNestedChildShapes;

                  if (nestedLink != null)

                  {

                        shape = nestedLink.NestedChildShapes as Shape;

                  }

                  if (shape != null && shape.Diagram != null)

                  {

                        // Expand the shape and move it to its new position

                        shape.IsExpanded = true;

                        shape.Location = new PointD(location.X, location.Y + offset);

                        // Adjust the height offset for the size of the shape

                        // (I'm assuming that the DefaultContainerMargin

                        // provides for a decent spacing between the shapes)

                        offset += shape.Size.Height + shape.Diagram.DefaultContainerMargin.Height;

                  }

            }

      }

 

The above code starts at the top-left of the diagram and moves additional shapes down the diagram. You'll need to specify the starting offset and location (and probably reset it), because this code just always assumes the top left of the diagram, but it gives an idea of how to get this working.

 

 

 

You also need to the the modeling system know that this rule exists. Rules we generate in code automatically get added to the modeling system, but not custom written ones. To do this, you need to let the modeling system know to add your new rule, like this:

 

namespace MicrosoftCorporation.Language1.Designer

{

   internal static partial class GeneratedMetaModelTypes

   {

      // If you have hand-written rules, you define a

      // partial GeneratedMetaModelTypes and put the types

      // of the rules as internal static fields of the class.

      // For example:

      // internal static Type MyRuleType = typeof(MyRule);

      internal static Type ShapeAddedToDiagramRuleType = typeof(ShapeAddedToDiagramRule);

   }

}

 

Define an internal field for your rule type, make sure the namespace points to the one for your Designer (this class is defined as partial in the DesignerMetaModelTypes.csfile in your project), and the modeling system goes through all of the fields on this class and adds them to the notification list.

 

With all of this custom code, it's best that you put it into a separate code file, sothat it doesn't get deleted whenever you transform all of the templates again.