Compartilhar via


Customizing Element Creation and Movement

You can allow an element to be dragged onto another, either from the toolbox or in a paste or move operation. You can have the moved elements linked to the target elements using the relationships that you specify.

An element merge directive specifies what happens when one model element is merged into another model element. This happens when:

  • The user drags from the toolbox onto the diagram or a shape.

  • The user creates an element using an Add menu in the explorer or a compartment shape.

  • The user moves an item from one swimlane to another.

  • The user pastes an element.

  • Your program code calls the element merge directive.

Although the creation operations might seem different from the copy operations, they actually work in the same way. When an element is added, for example from the toolbox, a prototype of it is replicated and is merged into the model in the same manner as elements that have been copied from another part of the model.

The responsibility of an element merge directive is to decide how an object or group of objects should be merged into another group. In particular, it decides what relationships should be instantiated between the two groups, and what changes of names should be made.

Element merge directives are generated automatically when you create an embedding relationship. You can find them in DSL Explorer under the root domain class, and under other classes that are the source of an embedding relationship.

You can add your own element merge directives in the DSL definition, and you can modify the existing directives by adding custom code.

There are two kinds of merge directive, a process merge directive and a forward merge directive. A process merge directive specifies that the element to be merged, also known as the indexing element, is linked to the target element by a domain relationship. A forward merge directive specifies that the indexing element is forwarded to a different target element. Merge directives can be set on domain classes, domain relationships, shapes, connectors, and diagrams.

You can add custom code to merge directives. By using the Uses custom accept option, you can write code to determine whether a particular instance of the indexing element should be merged into the target element. By using the Uses custom merge option, you can write code to customize the merge itself. For example, you may want to set fields in the target element when an indexing element has been merged. By using the Applies to subclasses option, you can program the merge to apply not only to the indexing element but also to its subclasses.

Adding a Process Element Merge Directive

A process merge directive adds a domain relationship between a target element and an indexing element. An example of a process merge directive is the Comment merge directive in the ComponentModel domain class in the Component Model solution template. The Comment merge directive specifies that a ComponentModelHasComments embedding relationship is created when a Comment domain class is merged with a ComponentModel domain class. The DSL Details window displays the following domain path: ComponentModelHasComments.Comments

Observação

Whenever you create an embedding relationship between two model elements on the diagram, a process element merge directive is created.

The following procedure shows how to add a process element merge directive that adds an ExampleElementHasComments embedding relationship to the ExampleElement domain class.

To add a process element merge directive to the ExampleElement domain class

  1. Create a Minimal Language solution.

    For more information about how to open a Domain-Specific Language Tools solution, see How to: Create a Domain-Specific Language Solution.

  2. Display the diagram by opening DslDefinition.dsl.

  3. Add a domain class and name it Comment.

  4. Create an embedding relationship between the ExampleElement domain class and the Comment domain class by clicking Add new Domain Relationship at the root of the model in the DSL Explorer and then filling in the source and target roles.

    Observação

    If you draw the relationship on the diagram, the element merge directive is set up automatically.

  5. In the DSL Explorer, right-click the ExampleElement domain class (under the Domain Classes node), and then click Add New Element Merge Directive.

    A node named Element Merge Directive is added under the Element Merge Directives node of ExampleElement.

  6. Select the new node and open the DSL Details window.

    The DSL Details - Merge Directive window appears.

  7. In the Indexing class list, select Comment.

  8. Select Process merge by creating links at paths.

  9. In the <add path> box, select the path of the Comment domain class. This path should resemble ExampleElement HasComments.Comments.

  10. Select Uses custom accept to specify custom code.

    If you select this option, a method named CanMergeComments will appear in the generated ExampleElement.CanMerge method. You must implement this method to determine whether a given instance of the Comment class should be accepted for merging into the ExampleElement class.

  11. In Solution Explorer, create a folder in the Dsl project and name it CustomCode (or another name). Add a class file that has the following code, and change the namespace name to a name that is appropriate for you.

    using DslModeling = global::Microsoft.VisualStudio.Modeling;
    namespace YourNamespace
    {
        public partial class ExampleElement
        {
            protected bool CanMergeComment(DslModeling::ProtoElementBase rootElement, DslModeling::ElementGroupPrototype elementGroupPrototype)
            {
                //if there are no other comments, add it
                if (this.Comments.Count == 0)
                   return true;
            }
        }
    }
    

    For more information about customizing the model by using program code, see Navigating and Updating a Model in Program Code.

  12. Select Uses custom merge to specify that you want to create custom code for the merge.

    If you select this option, the path is ignored, and you must implement the ExampleElement.MergeRelate and ExampleElement.MergeDisconnect methods in the partial class that you added earlier. In the following example, when a Comment is added to an ExampleElement.Comments collection, the Comment.Date domain property is set to the current date. If a Comment is deleted, the link is also deleted.

        private void MergeRelate(DslModeling::ModelElement sourceElement, DslModeling::ElementGroup elementGroup)
        {
            Comment comment = sourceElement as Comment;
            if (comment != null)
            {
                // add to the collection. 
                this.Comments.Add(comment);
    
                comment.Date = DateTime.Now.ToString();
            }
        }
        private void MergeDisconnect(DslModeling::ModelElement sourceElement)
        {
            Comment comment = sourceElement as Comment;
            if (comment != null)
            {
                foreach (DslModeling::ElementLink link in global::YourNamespace.ExampleElementHasComments.GetLinks((global::YourNamespace. ExampleElement)this, comment))
                {
                    //Delete the link
                    link.Delete(global::YourNamespace.ExampleElementHasComments.ExampleElementDomainRoleId, global::YourNamespace.ExampleElementHasComments.CommentDomainRoleId);
                }
                return;
            }
        }
    

    For more information about customizing the model by using program code, see Navigating and Updating a Model in Program Code.

  13. Select Applies to subclasses so that any subclasses of Comment can be merged automatically with ExampleElement.

Adding a Forward Merge Directive

A forward merge directive changes the target of a domain relationship between an indexing element and a target element. Instead of embedding Class A in Class B, a forward merge directive can embed Class A to the parent of Class B. For example, ports are embedded to components, but cannot be embedded to other ports. You can create a forward merge directive that embeds a port to the component of another port.

You can create a forward merge directive in the Component Model solution. If you compile and run the original solution, you should see that users can drag any number of Input Port or Output Port elements from the Toolbox to a Component element, but they cannot drag a port to an existing port. The Unavailable pointer alerts them that this move is not enabled. However, you can create a forward merge directive so that a port that is inadvertently dropped on an existing Input Port is forwarded to the Component element.

To create a forward merge directive

  1. Create a Domain-Specific Language Tools solution by using the Component Model template.

  2. Display the DSL Explorer by opening DslDefinition.dsl.

  3. In the DSL Explorer, expand Domain Classes.

  4. The ComponentPort abstract domain class is the base class of both InPort and OutPort. Right-click ComponentPort and then click Add New Element Merge Directive.

    A new Element Merge Directive node appears under the Element Merge Directives node.

  5. Select the Element Merge Directive node and open the DSL Details window.

  6. In the Indexing class list, select ComponentPort.

  7. Select Forward merge to a different domain class.

  8. In the path selection list, expand ComponentPort, expand ComponentHasPorts, and then select Component.

    The new path should resemble this one:

    ComponentHasPorts.Component/!Component

  9. Save the solution, and then transform the templates by clicking the rightmost button on the Solution Explorer toolbar.

  10. Build and run the solution. A new instance of Visual Studio appears.

  11. In Solution Explorer, open Sample.mydsl. The diagram and the ComponentLanguage Toolbox appear.

  12. Drag an Input Port from the Toolbox to another Input Port. Next, drag an OutputPort to an InputPort and then to another OutputPort.

    You should not see the Unavailable pointer, and you should be able to drop the new Input Port on the existing one. Select the new Input Port and drag it to another point on the Component.

See Also

Concepts

Navigating and Updating a Model in Program Code

Customizing Tools and the Toolbox