You now have the underlying navigation domain language. In this case, the graphical presentation of this language is nearly a one-to-one mapping — that is, you expect the user to create pages and create links between them so there are no additional concepts to provide.
This isn't the case in all scenarios. For example, the graphical language for a given user can be completely different from the domain language. Also, a graphical shape dragged onto a designer could (under the covers) have to build a very complex language model instead of a simple one-to-one mapping. For example, if you had a domain model that represented a car and its various component parts, such as Wheels, Engine, and Windows, you could allow the user to just drag a Car onto the designer and automatically create the components of the car in the language model without requiring the user to manually create them all.
The Designer Definition (.dsldd) file defines the graphical language for your designer and specifies the mapping between the graphical and domain language elements. That way, when you drag a graphical element onto the design surface, the corresponding domain language elements will be created automatically and merged into your domain-specific language domain model.
The Designer Definition file is currently implemented as an XML file, but a later release of the DSL Tools will have the two files combined, and one graphical designer will be provided. This will make the development experience far simpler, as you won't need to manually provide mapping between your graphical and domain language elements, with all the obvious brittleness this presents.
The outline of the designer definition is shown here:
<?xml version="1.0"?> <designerDefinition namespace="CompanyName.ProjectName.NavigationDesigner.Designer" name="NavigationDesigner" xmlns="http://schemas.microsoft.com/dsltools/dsldd"> <explorer> <notation> <objectModels> <propertiesWindow> <validationBehavior> </designerDefinition>
The first step is to define the graphical shapes that will be used by the diagram. In the case of the Navigation Designer, we only have one graphical shape to represent a page. We don't have a graphical shape to represent a site because the designer will only be used to model one site at a time, so there's no need to provide a graphical shape on the designer, although you could provide one if required.
All designers have one diagram, which you can see defined here:
<notation> <diagramMaps/> <diagrams> <diagram name="NavigationDesignerDiagram"> <connectors/> <shapes/> <toolbox/> </diagram> </diagrams> </notation>
We start by defining the shapes under the Shape element. A number of shapes are supported out of the box, such as Geometry, Image, and Compartment. For the purposes of this designer, we will just use the GeometryShape.
We define one Shape called PageShape, as shown below. Apart from the name, this shape is identical to the sample shapes defined in the Designer Definition file. Also defined on the Shape is a decorator that will be used to display the name of the Page:
<geometryShape name="PageShape" initialWidth="2.0" initialHeight="0.75" geometry="Rectangle"> <decorators> <shapeText name="Name" position="Center" defaultText/> </decorators> <fillColor color="blue" variability="User"/> <outlineColor color="black" variability="User"/> </geometryShape>
Now that you have your Page shape, you need to define the connector that will link Page shapes together to form the navigation. You do this under the connectors element.
The key parts of the connector section are the source and target elements that define what shapes a connector can be drawn from and to. The designer will then enforce these rules when the user selects the connector. In our case, the connector is identical to the sample connector, apart from the permittedShapes sections, which we change to PageShape to specify that Page shapes can only be linked to other Page shapes, as shown below, and the connector name property, which we set to TransitionConnector to represent a transition between Pages:
<connectors> <connector name="TransitionConnector"> <color variability="User" color="black" /> <dashStyle variability="User" dashStyle="dash"/> <decorators> <connectorText name="Label" position="TargetBottom" defaultText/> </decorators> <source> <permittedShapes> <shape>CompanyName.ProjectName.NavigationDesigner.Designer. NavigationDesignerDiagram/Shapes/PageShape</shape> </permittedShapes> </source> <target arrowStyle="EmptyArrow"> <permittedShapes> <shape>CompanyName.ProjectName.NavigationDesigner.Designer. NavigationDesignerDiagram/Shapes/PageShape</shape> </permittedShapes> </target> </connector> </connectors>
Now that you have your Shape and Connecter defined, you need to display these on the designer toolbox to enable a user to select them. You do this under the toolbox element as shown below.
The properties iconId, captionId, and contextSensitiveHelpId are all keys to resources held within the project. The wizard generates a sample resources file called <YourDesignerName>.Resource.resx located under the Diagram Solution folder.
If you open this resource file, you will see a number of sample resources that you can rename and modify, or replace with your own bitmaps. For the purposes of the designer, we rename the items and reuse the bitmaps as shown in the following code. Under the ShapeTool element, you specify the graphical shapes that the toolbox item will be representing, which in our case are PageShape and TransitionConnector, as defined above:
<toolbox> <items> <shapeTool icon caption contextSensitiveHelp order="0"> <shape>CompanyName.ProjectName.NavigationDesigner.Designer. NavigationDesignerDiagram/Shapes/PageShape</shape> </shapeTool> <connectorTool icon contextSensitiveHelp caption order="1"> <connector>CompanyName.ProjectName.NavigationDesigner.Designer. NavigationDesignerDiagram/Connectors/TransitionConnector</connector> </connectorTool> </items> </toolbox>
The next step is to provide a Shape Map, which maps Domain Model classes to graphical shapes on your designer. This enables the DSL Tools framework to create the appropriate object graph for a given graphical shape and merge it into the domain model.
The class element of the Shape Map identifies the class in your domain model that you want to map to a graphical element. In this case, it's the Page class.
The next section of the Shape Map, called melCollectionExpression, specifies the domain model role to which the new Page class will be added. In our case, this is the left-hand role of the SiteHasPages relationship, which is called SitePages. This will cause a new Page to be added to the SitePages collection of the Site class.
The Shape element defines the graphical shape you wish to map to, which is a PageShape in this case. The following sections refer to the decorators used on the graphical shape. All Shapes have a standard decorator called Name, and you specify the value to be the value of the Name property on the Page class, thus displaying a property held in the ObjectModel on the graphical shape:
<shapeMaps> <shapeMap> <class>CompanyName.ProjectName.NavigationDesigner. DomainModel.NavigationDesigner/Page</class> <iconMaps> </iconMaps> <melCollectionExpression> <roleExpression> <role>CompanyName.ProjectName.NavigationDesigner. DomainModel.NavigationDesigner/Site/SitePages</role> </roleExpression> </melCollectionExpression> <shape>CompanyName.ProjectName.NavigationDesigner.Designer. NavigationDesignerDiagram/Shapes/PageShape</shape> <textMaps> <shapeTextMap> <textDecorator>CompanyName.ProjectName.NavigationDesigner. Designer.NavigationDesignerDiagram/Shapes/PageShape/ Decorators/Name</textDecorator> <valueExpression> <valuePropertyExpression> <valueProperty>CompanyName.ProjectName. NavigationDesigner.DomainModel.NavigationDesigner /Page/Name</valueProperty> </valuePropertyExpression> </valueExpression> </shapeTextMap> </textMaps> </shapeMap> </shapeMaps>
The final step is to define a Connector Map, which maps the graphical connector onto a domain model relationship, in the same way as a Shape Map. This enables the domain model to construct the relationship when a connection is made between two shapes.
The connector section defined previously enforces the rules regarding which shapes can be connected to, so this section purely defines the mapping.
The class element of the Connector Map identifies the relationship in our Domain Model that we want to map to a graphical connector — in this case, it's the Transition relationship.
The connectors element defines the graphical connector to which you are mapping, which is called TransitionConnector as defined in the connectors element previously.
The next two sections, SourceMap and TargetMap, define what domain model roles are updated when a connection is made between two pages.
The SourceMap expression refers to the Role on the right-hand side of the relationship (modeled as a rectangle), which in this case is the ReferringPages role; therefore, the TargetMap expression refers to the Role on the left-hand side of the relationship (modeled as a triangle), which is called ChildPages.
The remaining sections refer to the decorators used on the graphical connector. All connectors have a standard decorator called Name, and you specify the value to be the value of the Name property on the Transition class, thus displaying a property held in the ObjectModel on the graphical connector:
<connectorMaps> <connectorMap> <class>CompanyName.ProjectName.NavigationDesigner. DomainModel.NavigationDesigner/Transition</class> <connector>CompanyName.ProjectName.NavigationDesigner. Designer.NavigationDesignerDiagram/Connectors/ TransitionConnector</connector> <sourceMap> <modelNavigationExpression> <roleExpression> <role>CompanyName.ProjectName.NavigationDesigner. DomainModel.NavigationDesigner/ Page/ReferringPages</role> </roleExpression> </modelNavigationExpression> </sourceMap> <targetMap> <modelNavigationExpression> <roleExpression> <role>CompanyName.ProjectName.NavigationDesigner. DomainModel.NavigationDesigner/Page/ChildPages</role> </roleExpression> </modelNavigationExpression> </targetMap> <textMaps> <connectorTextMap> <textDecorator>CompanyName.ProjectName.NavigationDesigner. Designer.NavigationDesignerDiagram/Connectors/ TransitionConnector/Decorators/Label</textDecorator> <valueExpression> <valuePropertyExpression> <valueProperty>CompanyName.ProjectName. NavigationDesigner.DomainModel. NavigationDesigner/Transition/ Name</valueProperty> </valuePropertyExpression> </valueExpression> </connectorTextMap> </textMaps> </connectorMap> </connectorMaps>
We've now provided all of the information needed for our designer to be built. The navigation language has been defined, and you've detailed the graphical language that users will interact with to indirectly build this navigation language.