You are on page 1of 669

Table of Contents

Analyze and model your architecture


What's new for design in Visual Studio
Scenario: Change your design using visualization and modeling
Visualize code
Map dependencies across your solutions
Use code maps to debug your applications
Map methods on the call stack while debugging in Visual Studio
Find potential problems using code map analyzers
Browse and rearrange code maps
Customize code maps by editing the DGML files
Directed Graph Markup Language (DGML) reference
Create dependency diagrams from your code
Create models for your app
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Share models and exporting diagrams
Use models in your development process
Model user requirements
Model your app's architecture
Generate and configure your app from models
Structure your modeling solution
Visual Studio Architecture Tooling Guidance
Validate your system during development
Develop tests from a model
Validate code with dependency diagrams
Extend dependency diagrams
Add commands and gestures to dependency diagrams
Add custom architecture validation to dependency diagrams
Add custom properties to dependency diagrams
Navigate and update dependency models in program code
Deploy a dependency model extension
Troubleshoot extensions for dependency diagrams
Modeling SDK for Visual Studio - Domain-Specific Languages
Getting Started with Domain-Specific Languages
Understanding Models, Classes and Relationships
How to Define a Domain-Specific Language
Customizing and Extending a Domain-Specific Language
Writing Code to Customise a Domain-Specific Language
Validation in a Domain-Specific Language
Writing User Commands and Actions
Responding to and Propagating Changes
Generating Code from a Domain-Specific Language
Customizing File Storage and XML Serialization
Deploying Domain-Specific Language Solutions
Creating a Windows Forms-Based Domain-Specific Language
Creating a WPF-Based Domain-Specific Language
Working with Domain-Specific Language Solutions
Supported Visual Studio Editions for Visualization & Modeling SDK
How to: Migrate a Domain-Specific Language to a New Version
API Reference for Modeling SDK for Visual Studio
Code Generation and T4 Text Templates
Design-Time Code Generation by using T4 Text Templates
Run-Time Text Generation with T4 Text Templates
Writing a T4 Text Template
Debugging a T4 Text Template
Guidelines for Writing T4 Text Templates
How to ... with Text Templates
Customizing T4 Text Transformation
API Reference for T4 Text Templates
Analyze and model your architecture
11/7/2017 3 min to read Edit Online

Make sure your app meets architectural requirements by using Visual Studio architecture and modeling tools to
design and model your app.
Understand existing program code more easily by using Visual Studio to visualize the code's structure,
behavior, and relationships.
Educate your team in the need for respecting architectural dependencies.
Create models at different levels of detail throughout the application lifecycle as part of your development
process.
See Scenario: Change your design using visualization and modeling.

To

Visualize code: - Visualize code


- Working with Classes and Other Types (Class Designer)
- See the code's organization and relationships by creating - Video: Understand design from code with Visual Studio
code maps. Visualize dependencies between assemblies, 2015 code maps
namespaces, classes, methods, and so on. - Video: Validate your architecture dependencies in real time
- See the class structure and members for a specific project by
creating class diagrams from code.
- Find conflicts between your code and its design by creating
dependency diagrams to validate code.

Define the architecture: - Video: Validate architecture dependencies with Visual Studio
(Channel 9)
- Define and enforce constraints on dependencies between
the components of your code by creating dependency
diagrams.

Validate your system with the requirements and - Video: Validate architecture dependencies with Visual Studio
intended design: (Channel 9)

- Validate code dependencies with dependency diagrams that


describe the intended architecture and prevent changes that
might conflict with the design.

Share models, diagrams, and code maps using Team


Foundation version control:

- Put code maps, projects, and deoendency diagrams under


Team Foundation version control so you can share them.

Customize models and diagrams: - Modeling SDK for Visual Studio - Domain-Specific
Languages
- Create your own domain-specific languages.
Generate text using T4 templates: - Code Generation and T4 Text Templates

- Use text blocks and control logic inside templates to


generate text-based files.
- T4 template build with MSBuild included in Visual Studio

To see which versions of Visual Studio support each feature, see Version support for architecture and modeling
tools

Types of Models and Their Uses


MODEL TYPE AND TYPICAL USES

Code maps

Code maps help you see the organization and relationships in your code.

Typical uses:

- Examine program code so you can better understand its structure and its dependencies, how to update it, and estimate the
cost of proposed changes.

See:

- Map dependencies across your solutions


- Use code maps to debug your applications
- Find potential problems using code map analyzers

Dependency diagram

Dependency diagrams let you define the structure of an application as a set of layers or blocks with explicit dependencies. You
can run validation to discover conflicts between dependencies in the code and dependencies described on a dependency
diagram.

Typical uses:

- Stabilize the structure of the application through numerous changes over its life.
- Discover unintentional dependency conflicts before checking in changes to the code.

See:

- Create dependency diagrams from your code


- Dependency Diagrams: Reference
- Validate code with dependency diagrams

Domain-specific language (DSL)

A DSL is a notation that you design for a specific purpose. In Visual Studio, it is usually graphical.

Typical uses:

- Generate or configure parts of the application. Work is required to develop the notation and tools. The result can be a better fit
to your domain than a UML customization.
- For large projects or in product lines where the investment in developing the DSL and its tools is returned by its use in more
than one project.

See:

- Modeling SDK for Visual Studio - Domain-Specific Languages


Where can I get more information?
Visual Studio Visualization & Modeling Tools Forum

See Also
What's new
DevOps and Application Lifecycle Management
What's new for design in Visual Studio
10/18/2017 2 min to read Edit Online

Live dependency validation


Removing unwanted dependencies is an important part of managing your technical debt. Live validation of
dependencies is now included, providing precise information about issues, and benefitting fully from the new
features in the Error list and the editor.

The authoring experience has changed to make dependency validation more discoverable and more accessible,
changing the terminology from "Layer diagram" to "Dependency diagram".
The Architecture menu now contains a command to directly create a Dependency diagram:

... and the property names of a Layer in a Dependency diagram, and their descriptions, have been changed to make
them more meaningful:
You now see the impact of your changes immediately in the analysis results for the current code in the solution
each time you save the diagram. You don't have to wait any longer for the completion of the "Validate
Dependencies" command.
For more details, see this blog post.

UML designers have been removed


The UML designers have been removed from this version of Visual Studio Enterprise.
UML diagrams are now presented as XML files
The UML Model Explorer no longer exists
Modeling project references are no longer used for dependency validation
The "Layer References" node in Solution Explorer is no longer displayed
The "Validate" build action on a Dependency (Layer) diagram is no longer used - the Build task has been
removed
The project structure is maintained for round-tripping between versions
You can still open, create, edit, and save a Dependency (Layer) diagram as XML
TFS work items linked to a Dependency (Layer) diagram are not accessible on the design surface
Back linking from to DSL or a Layer is no longer supported
UML extensibility in the Modeling SDK is no longer supported
However, support for visualizing the architecture of .NET and C++ code is available through code maps, and the
significant improvements to dependency validation described above.
If you are a significant user of the UML designers, you can continue to use Visual Studio 2015 or earlier versions
while you decide on an alternative tool for your UML needs.
For more details, see this blog post.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Version support for architecture and modeling tools


Visual Studio is available in several versions. Not all of these provide support for the architecture and modelling
tools. The following table shows the availability of each tool.

FEATURE ENTERPRISE PROFESSIONAL COMMUNITY EXPRESS

Code maps Yes See Note (1) - -

Dependency Yes See Note (2) See Note (2) -


diagrams

Directed graphs Yes Yes Yes -


(DGML diagrams)

Code clone Yes - - -

Note (1): Only supports reading code maps, filtering code maps, adding new generic nodes, and creating a new
Directed Graph from a selection.
Note (2): Only supports reading dependency diagrams.
Scenario: Change your design using visualization and
modeling
10/18/2017 21 min to read Edit Online

Make sure that your software system meets users' needs by using the visualization and modeling tools in Visual
Studio. Use tools such as code maps, dependency diagrams, and class diagrams to:
To see which versions of Visual Studio support each tool, see Version support for architecture and modeling tools.
Clarify users' requirements and business processes.
Visualize and explore existing code.
Describe changes to an existing system.
Verify that the system meets its requirements.
Keep code consistent with the design.
This walkthrough:
Describes how these tools can benefit your software project.
Shows how you might use these tools, regardless your development approach, with an example scenario.
To find out more about these tools and the scenarios that they support, see:
Analyzing and Modeling Architecture
Visualize code

Scenario Overview
This scenario describes episodes from the software development lifecycles of two fictitious companies: Dinner Now
and Lucerne Publishing. Dinner Now provides a Web-based meal delivery service in Seattle. Customers can order
meals and pay for them on the Dinner Now Web site. The orders are then sent to the appropriate local restaurant
for delivery. Lucerne Publishing, a company in New York, runs several businesses both off and on the Web. For
example, they run a Web site where customers can post restaurant reviews.
Lucerne recently acquired Dinner Now and wants to make the following changes:
Integrate their Web sites by adding restaurant review capabilities to Dinner Now.
Replace Dinner Now's payment system with Lucerne's system.
Expand the Dinner Now service across the region.
Dinner Now uses SCRUM and eXtreme Programming. They have very high test coverage and very little
unsupported code. They minimize risks by creating small but working versions of a system and then adding
functionality incrementally. They develop their code over short and frequent iterations. This lets them
embrace change confidently, refactor code frequently, and avoid "big design up front".
Lucerne maintains a vastly larger and complex collection of systems, some of which are more than 40 years
old. They are very cautious about making changes because of the complexity and scope of legacy code. They
follow a more rigorous development process, preferring to design detailed solutions and to document the
design and changes that occur during development.
Both teams use modeling diagrams in Visual Studio to help them develop systems that meet the users'
needs. They use Team Foundation Server alongside other tools to help them plan, organize, and manage
their work.
For more information about Team Foundation Server, see:
Planning and tracking work
Testing, validating, and checking in updated code

Roles of Architecture and Modeling Diagrams in Software Development


The following table describes roles that these tools can play during multiple and various stages of the software
development lifecycle:

USER BUSINESS SYSTEM CODE


REQUIREMENTS PROCESS ARCHITECTURE & VISUALIZATION &
MODELING MODELING DESIGN EXPLORATION VERIFICATION

Domain-Specific Yes Yes Yes


Language (DSL)
diagram

Dependency Yes Yes Yes


diagram, layer
validation

Code map Yes Yes Yes

Class Designer Yes


(code-based)

To draw dependency diagrams, you must create a modeling project as part of an existing solution or a new one.
These diagrams must be created in the modeling project. Items on dependency diagrams are located in the
modeling project, but they are not stored in the common model. Code maps and .NET class diagrams created from
code exist outside the modeling project.
See:
Create dependency diagrams from your code
Map dependencies across your solutions
How to: Add Class Diagrams to Projects (Class Designer)
Modeling SDK for Visual Studio - Domain-Specific Languages

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Both teams also use dependency validation to make sure that code under development remains consistent with the
design.
See:
Keeping Code Consistent with the Design
Describe the Logical Architecture: Dependency Diagrams
Validate code with dependency diagrams

NOTE
Some versions of Visual Studio support dependency validation and read-only versions of code maps for visualization
and modeling. To see which versions of Visual Studio support this feature, see Version support for architecture and
modeling tools.

Understanding and Communicating Information about the System


There is no prescribed order for using the Visual Studio modeling diagrams, so you can use them as they fit with
your needs or approach. Usually, teams revisit their models iteratively and frequently throughout a project. Each
diagram offers particular strengths to help you understand, describe, and communicate different aspects of the
system under development.
Dinner Now and Lucerne communicate with each another and with project stakeholders by using diagrams as their
common language. For example, Dinner Now uses diagrams to perform these tasks:
Visualize existing code.
Communicate with Lucerne about new or updated user stories.
Identify changes that are required to support new or updated user stories.
Lucerne uses diagrams to perform these tasks:
Learn about the Dinner Now business process.
Understand the design of the system.
Communicate with Dinner Now about new or updated user requirements.
Document updates to the system.
The diagrams are integrated with Team Foundation Server so the teams can plan, manage, and track their
work more easily. For example, they use models to identify test cases and development tasks and to
estimate their work. Lucerne links Team Foundation Server work items to model elements so that they can
monitor progress and make sure that the system meets the users' requirements. For example, they link use
cases to test case work items so they can see that use cases are fulfilled when all the tests pass.
Before teams check in their changes, they validate the code against the tests and the design by running
builds that include dependency validation and automated tests. This helps make sure that the updated code
does not conflict with the design and break previously working functionality.
See:
Identifying changes to the existing system
Keeping code consistent with the design
General tips for creating and using models
Planning and tracking work
Testing, validating, and checking in updated code
Identifying Changes to the Existing System
Dinner Now must estimate the cost of meeting the new requirement. This depends partly on how much this change
will affect other parts of the system. To help them understand this, one of the Dinner Now developers creates these
maps and diagrams from existing code:
MAP OR DIAGRAM SHOWS

Code map Dependencies and other relationships in code.

See: For example, Dinner Now might start by reviewing assembly


code maps for an overview of the assemblies and their
- Map dependencies across your solutions dependencies. They can drill into the maps to explore the
- Browse and rearrange code maps namespaces and classes in those assemblies.
- Customize code maps by editing the DGML files
Dinner Now can also create maps to explore particular areas
and other kinds of relationships in the code. They use Solution
Explorer to find and select the areas and relationships that
interest them.

Code-based class diagram Existing classes in code

See How to: Add Class Diagrams to Projects (Class Designer).

For example, the developer creates a code map. She adjusts its scope to focus on the areas that will be affected by
the new scenario. These areas are selected and highlighted on the map:

Namespace code map


The developer expands the selected namespaces to see their classes, methods, and relationships:
Expanded namespace code map with visible cross-group links
The developer examines the code to find the affected classes and methods. To see the effects of each change as you
make them, regenerate code maps after each change. See Visualize code.
To describe changes to other parts of the system, such as components or interactions, the team might draw these
elements on whiteboards. They might also draw the following diagrams in Visual Studio so that the details can be
captured, managed, and understood by both teams:

DIAGRAMS DESCRIBES

Code-based class diagram Existing classes in code.

See How to: Add Class Diagrams to Projects (Class Designer).

Keeping Code Consistent with the Design


Dinner Now must make sure that the updated code stays consistent with the design. They create dependency
diagrams that describe the layers of functionality in the system, specify the permitted dependencies between them,
and associate solution artifacts to those layers.

DIAGRAM DESCRIBES

Dependency diagram The logical architecture of the code.

See: A dependency diagram organizes and maps the artifacts in a


Visual Studio solution to abstract groups called layers. These
- Create dependency diagrams from your code layers identify the roles, tasks, or functions that these artifacts
- Dependency Diagrams: Reference perform in the system.
- Dependency Diagrams: Guidelines
- Validate code with dependency diagrams Layer diagrams are useful for describing the intended design
of the system and validating evolving code against that
design.

To create layers, drag items from Solution Explorer, code maps,


Class View, and Object Browser. To draw new layers, use the
toolbox or right-click the diagram surface.

To view existing dependencies, right-click the layer diagram


surface, and then click Generate Dependencies. To specify
intended dependencies, draw new dependencies.

For example, the following dependency diagram describes dependencies between layers and the number of
artifacts that are associated with each layer:

Dependency Diagram
To make sure that conflicts with the design do not occur during code development, the teams uses dependency
validation on builds that are run on Team Foundation Build. They also create a custom MSBuild task to require
dependency validation in their check-in operations. They use build reports to collect validation errors.
See:
Define your build process
Use a gated check-in build process to validate changes
Customize your build process template
General Tips for Creating and Using Models
Most diagrams consist of nodes that are connected by lines. For each diagram type, the toolbox provides
different kinds of nodes and lines.
To open the toolbox, on the View menu, click Toolbox.
To create a node, drag it from the toolbox to the diagram. Certain kinds of nodes must be dragged onto
existing nodes. For example, on a component diagram, a new port must be added to an existing component.
To create a line or a connection, click the appropriate tool in the toolbox, click the source node, and then click
the target node. Some lines can be created only between certain kinds of nodes. When you move the pointer
over a possible source or target, the pointer indicates whether you can create a connection.
Planning and Tracking Work
Visual Studio modeling diagrams are integrated with Team Foundation Server so that you can plan, manage, and
track work more easily. Both teams use models to identify test cases and development tasks and to estimate their
work. Lucerne creates and links Team Foundation Server work items to model elements, such as use cases or
components. This helps them monitor their progress and trace their work back to the users' requirements. This
helps them make sure that their changes continue to meet those requirements.
As their work progresses, the teams update their work items to reflect the time that they spent on their tasks. They
also monitor and report status on their work by using the following Team Foundation Server features:
Daily burndown reports that show whether they will complete the planned work in the expected time. They
generate other similar reports from Team Foundation Server to track the progress of bugs.
An iteration worksheet that uses Microsoft Excel to help the team monitor and balance the workload
between its members. This worksheet is linked to Team Foundation Server and provides focus for discussion
during their regular progress meetings.
A development dashboard that uses Office Project to keep the team informed about important project
information.
See:
Track work using Visual Studio Team Services or Team Foundation Server
Charts, dashboards, and reports for Visual Studio ALM
Create your backlog and tasks using Project
Testing, Validating, and Checking In Code
As the teams complete each task, they check their code into Team Foundation version control and receive
reminders from Team Foundation Server, if they forget. Before Team Foundation Server accepts their check-ins, the
teams run unit tests and dependency validation to verify the code against their test cases and the design. They use
Team Foundation Server to run builds, automated unit tests, and dependency validation regularly. This helps make
sure that the code meets the following criteria:
It works.
It does not break previously working code.
It does not conflict with the design.
Dinner Now has a large collection of automated tests, which Lucerne can reuse because almost all still apply.
Lucerne can also build on these tests and add new ones to cover new functionality. Both also use Visual
Studio to run manual tests.
To make sure that the code conforms to the design, the teams configure their builds in Team Foundation
Build to include dependency validation. If any conflicts occur, a report is generated with the details.
See:
Testing the application
Validate your system during development
Use version control
Build the application

Updating the System Using Visualization and Modeling


Lucerne and Dinner Now must integrate their payment systems. The following sections show the modeling
diagrams in Visual Studio help them perform this task:
Visualize Existing Code: Code Maps
Define a Glossary of Types: Class Diagrams
Describe the Logical Architecture: Dependency Diagrams
See:
Visualize code
Use models in your development process
Model your app's architecture
Visualize Existing Code: Code Maps
Code maps show the current organization and relationships in the code. Items are represented by nodes on the
map, and relationships are represented by links. Code maps can help you perform the following kinds of tasks:
Explore unfamiliar code.
Understand where and how a proposed change might affect existing code.
Find areas of complexity, natural dependencies or patterns, or other areas that might benefit from
improvement.
For example, Dinner Now must estimate the cost of updating the PaymentProcessing component. This
depends partly on how much this change will affect other parts of the system. To help them understand this,
one of the Dinner Now developers generates code maps from the code and adjusts the scope focus on the
areas that might be affected by the change.
The following map shows the dependencies between the PaymentProcessing class and other parts of the
Dinner Now system, which appear selected:
Code map for Dinner Now payment system
The developer explores the map by expanding the PaymentProcessing class and selecting its members to
see the areas that are potentially affected:

Methods inside PaymentProcessing class and their dependencies


They generate the following map for the Lucerne Payment System to inspect its classes, methods, and
dependencies. The team sees that the Lucerne system might also require work to interact with the other
parts of Dinner Now:
Code map for Lucerne Payment System
Both teams work together to determine the changes that are required to integrate the two systems. They
decide to refactor some of the code so that it will be easier to update. The PaymentApprover class will move
to the DinnerNow.Business namespace and will require some new methods. The Dinner Now classes that
handle transactions will have their own namespace. The teams create and use work items to plan, organize,
and track their work. They link the work items to model elements where it is useful.
After reorganizing the code, the teams generate a new code map to see the updated structure and
relationships:

Code map with reorganized code


This map shows that the PaymentApprover class is now in the DinnerNow.Business namespace and has
some new methods. The Dinner Now transaction classes now have their own PaymentSystem namespace,
which makes it easier to deal with that code later.
Creating a Code Map
For a quick overview of source code, follow these steps to generate a code map:
On the Architecture menu, click Generate Code Map For Solution.
For a quick overview of compiled code, create a blank code map, and then drag assembly files or binary files
to the map surface.
To explore specific code or solution items, use Solution Explorer to select items and relationships that you
want to visualize. You can then either generate a new map or add selected items to an existing map. See
Map dependencies across your solutions.
To help you explore the map, rearrange the layout so that it suits the kinds of tasks that you want to
perform.
For example, to visualize layering in the code, select a tree layout. See Browse and rearrange code maps.
Summary: Strengths of Code Maps
Code maps help you:
Learn about the organization and relationships in existing code.
Identify areas that might be affected by a proposed change.
Find areas of complexity, patterns, layers, or other areas that you could improve to make the code easier to
maintain, change, and reuse.
Relationship to Other Diagrams

DIAGRAM DESCRIBES

Dependency diagram The logical architecture of the system. Use dependency


validation to make sure that the code stays consistent with
the design.

To help you identify existing dependencys or intended


dependencys, create a code map and group related items. To
create a dependency diagram, see:

- Create dependency diagrams from your code


- Dependency Diagrams: Guidelines

Class diagram (code-based) Existing classes in code for a specific project.

To visualize and modify an existing class in code, use Class


Designer.

See How to: Add Class Diagrams to Projects (Class Designer).

Define a Glossary of Types: Class Diagrams


Class diagrams define the entities, terms, or concepts that participate in the system and their relationships with one
another. For example, you can use these diagrams during development to describe the attributes and operations
for each class, regardless of their implementation language or style.
To help Lucerne describe and discuss the entities that participate in the Process Payment use case, they draw the
following class diagram:
Process Payment entities on a class diagram
This diagram shows that a Customer can have many orders and different ways to pay for orders. BankAccount and
CreditCard both inherit from Payment.
During development, Lucerne uses the following class diagram to describe and discuss the details of each class:

Process Payment details on the class diagram


Drawing a Class Diagram
A class diagram has the following major features:
Types such as classes, interfaces, and enumerations:
A class is the definition of objects that share specific structural or behavioral characteristics.
An interface defines a part of the externally visible behavior of an object.
An enumeration is a classifier that contains a list of literal values.
Attributes are values of a certain type that describe each instance of a classifier. A classifier is a general name
for types, components, use cases, and even actors.
Operations are methods or functions that instances of a classifier can perform.
An association indicates some kind of relationship between two classifiers.
An aggregation is an association that indicates a shared ownership between classifiers.
A composition is an association that indicates a whole-part relationship between classifiers.
To show aggregations or compositions, set the Aggregation property on an association. Shared
shows aggregations and Composite shows compositions.
A dependency indicates that changing the definition of one classifier might change the definition of another
classifier.
A generalization indicates that a specific classifier inherits part of its definition from a general classifier. A
realization indicates that a class implements the operations and attributes offered by an interface.
To create these relationships, use the Inheritance tool. Alternatively, a realization can be represented as a
lollipop.
Packages are groups of classifiers, associations, lifelines, components, and other packages. Import
relationships indicate that one package includes all definitions of another package.
As a starting point to explore and discuss existing classes, you can use Class Designer to create class
diagrams from code.
How to: Add Class Diagrams to Projects (Class Designer)
Summary: Strengths of Class Diagrams
Class diagrams help you define:
A common glossary of terms to use when discussing the users' needs and the entities that participate in the
system. See Model user requirements.
Types that are used by parts of the system, such as components, regardless of their implementation. See
Model your app's architecture.
Relationships, such as dependencies, between types. For example, you can show that one type can be
associated with multiple instances of another type.
Relationship to Other Diagrams

DIAGRAM DESCRIPTION

Dependency diagram Define the logical architecture of the system as it relates to


classes.

Use dependency validation to make sure that the code stays


consistent with the design.

See:

- Create dependency diagrams from your code


- Dependency Diagrams: Reference
- Dependency Diagrams: Guidelines
- Validate code with dependency diagrams

Code map Visualize the organization and relationships in existing code.

To identify classes, their relationships, and their methods,


create a code map that shows those elements.

See:

- Map dependencies across your solutions

Describe the Logical Architecture: dependency Diagrams


Dependency diagrams describe the logical architecture of a system by organizing the artifacts in your solution into
abstract groups, or layers. Artifacts can be many things, such as namespaces, projects, classes, methods, and so on.
Layers represent and describe the roles or tasks that the artifacts perform in the system. You can also include layer
validation in your build and check-in operations to make sure that the code stays consistent with its design.
To keep the code consistent with the design, Dinner Now and Lucerne use the following dependency diagram to
validate their code as it evolves:
Dependency diagram for Dinner Now integrated with Lucerne
The layers on this diagram link to the corresponding Dinner Now and Lucerne solution artifacts. For example, the
Business layer links to the DinnerNow.Business namespace and its members, which now include the
PaymentApprover class. The Resource Access layer links to the DinnerNow.Data namespace. The arrows, or
dependencies, specify that only the Business layer can use the functionality in the Resource Access layer. As the
teams update their code, layer validation is performed regularly to catch conflicts as they occur and to help the
teams resolve them promptly.
The teams work together to incrementally integrate and test the two systems. They first make sure that
PaymentApprover and the rest of Dinner Now work with one another successfully before they deal with
PaymentProcessing.
The following code map shows the new calls between the Dinner Now and PaymentApprover:

Code map with updated method calls


After they confirm that the system works as expected, Dinner Now comments out the PaymentProcessing code. The
layer validation reports are clean, and the resulting code map shows that no more PaymentProcessing
dependencies exist:
Code map without PaymentProcessing
Drawing a Dependency Diagram
A dependency diagram has the following major features:
Layers describe logical groups of artifacts.
A link is an association between a layer and an artifact.
To create layers from artifacts, drag items from Solution Explorer, code maps, Class View, or Object Browser.
To draw new layers and then link them to artifacts, use the toolbox or right-click the diagram surface to
create the layers, and then drag items to those layers.
The number on a layer shows the number of artifacts that are linked to the layer. These artifacts can be
namespaces, projects, classes, methods, and so on. When you interpret the number of artifacts on a layer,
remember the following:
If a layer links to an artifact that contains other artifacts, but the layer does not link directly to the
other artifacts, then the number includes only the linked artifact. However, the other artifacts are
included for analysis during layer validation.
For example, if a layer is linked to a single namespace, then the number of linked artifacts is 1, even if
the namespace contains classes. If the layer also has links to each class in the namespace, then the
number will include the linked classes.
If a layer contains other layers that are linked to artifacts, then the container layer is also linked to
those artifacts, even though the number on the container layer does not include those artifacts.
To see the artifacts that are linked to a layer, right-click the dependency, and then click View Links to
open Layer Explorer.
A dependency indicates that one layer can use the functionality in another layer, but not vice versa. A
bidirectional dependency indicates that one layer can use the functionality in another layer, and vice versa.
To display existing dependencies on the dependency diagram, right-click the diagram surface, and then click
Generate Dependencies. To describe intended dependencies, draw new ones.
See:
Create dependency diagrams from your code
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Validate code with dependency diagrams
Summary: Strengths of Dependency Diagrams
Dependency diagrams help you:
Describe the logical architecture of a system according to the functionality of its artifacts.
Make sure that code under development conforms to the specified design.
Relationship to Other Diagrams

DIAGRAM DESCRIPTION

Code map Visualize the organization and relationships in existing code.

To create layers, generate a code map, and then group items


on the map as potential layers. Drag the groups from the map
to the dependency diagram.

See:

- Map dependencies across your solutions


- Browse and rearrange code maps

External Resources
CATEGORY LINKS

Forums - Visual Studio Visualization & Modeling Tools


- Visual Studio Visualization & Modeling SDK (DSL Tools)

See Also
Visualize code
Use models in your development process
Use models in Agile development
Validate your system during development

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Visualize code
10/18/2017 1 min to read Edit Online

You can use the visualization and modeling tools in Visual Studio to help you understand existing code and
describe your application. This lets you visually learn how your changes might affect the code and help you assess
the work and risks that result from those changes. For example:
To understand the relationships in your code, map those relationships visually.
To describe your system's architecture and keep the code consistent with its design, create dependency
diagrams and validate code against these diagrams.
To describe class structures, create class diagrams.
These tools also help you communicate more easily with the people involved with your project.
To see which versions of Visual Studio support each feature, see Version support for architecture and
modeling tools

What do you want to do?

Understand code and its relationships: - Map dependencies across your solutions
- Use code maps to debug your applications
Map relationships between specific pieces of code. - Find potential problems using code map analyzers
- Map methods on the call stack while debugging
See an overview of the relationships in your code for the
entire solution.

Note: In this release of Visual Studio, the term code map is


used in place of dependency graph.

Understand class structures: How to: Add Class Diagrams to Projects (Class Designer)

Visualize the structure of classes in a project by creating class


diagrams from code.

Describe the high-level system design and validate code - Create dependency diagrams from your code
against this design: - Dependency Diagrams: Reference
- Dependency Diagrams: Guidelines
Describe the high-level system design and its intended - Validate code with dependency diagrams
dependencies by creating dependency diagrams. Validate
code against this design to make sure dependencies in code
remain consistent with the design.

External Resources
CATEGORY LINKS

Forums - Visual Studio Visualization & Modeling Tools


- Visual Studio Visualization & Modeling SDK (DSL Tools)

Blogs Visual Studio ALM + Team Foundation Server Blog


CATEGORY LINKS

Technical Articles and Journals MSDN Architecture Forum

See Also
Scenario: Change your design using visualization and modeling
Analyzing and Modeling Architecture
Model your app's architecture
Use models in your development process

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
Map dependencies across your solutions
10/18/2017 16 min to read Edit Online

When you want to understand dependencies across your code, visualize them by creating code maps. This helps
you see how the code fits together without reading through files and lines of code.

Here are some videos:


Understand your code dependencies through visualization
Visualize the impact of a change
Understanding complex code with code maps

Get started with code maps


To use code maps you'll need either:
Visual Studio Enterprise: Create code maps from the code editor, Solution Explorer, Class View, or Object
Browser.
Visual Studio Professional: Open code maps, make limited edits, and navigate code.

WARNING
Before you share maps created in Visual Studio Enterprise with others who use Visual Studio Professional, make sure that
all the items on the map (such as hidden items, expanded groups, and cross-group links) are made visible.

You can map dependencies for code in these languages:


Visual C# .NET or Visual Basic .NET in a solution or assemblies (.dll or .exe)
Native or managed C or C++ code in Visual C++ projects, header files (.h or #include ), or binaries
X++ projects and assemblies made from .NET modules for Microsoft Dynamics AX
Note: For projects other than C# or Visual Basic .NET, there are fewer options for starting a code map or
adding items to an existing code map. For example, you cannot right-click an object in the text editor of a
C++ project and add it to a code map. However, you can drag and drop individual code elements or files
from Solution Explorer, Class View, and Object Browser.
To see the overall dependencies across your solution
1. Open the Architecture menu.
2. If you just opened the solution and haven't yet built it, or if your code has changed since the last time you
built it, choose Generate Code Map for Solution.
3. If your code hasn't changed since the last time you built it, choose Generate Code Map for Solution
Without Building to get faster performance when creating the map.
4. See overall dependencies to understand how you can use code maps to view the overall dependencies
across your solution.
To see specific dependencies within your solution
1. With your solution loaded, open Solution Explorer.
2. Select all the projects, assembly references, folders, files, types, or members that you want to map.
3. On the Solution Explorer toolbar, choose Show on Code Map . Or open the shortcut menu and
choose Show on Code Map. You can also drag items from Class View or Object Browser into a new or
an existing code map.
4. See specific dependencies to understand how you can use code maps to view specific dependencies
within your solution.
To add a new empty code map to your solution
1. In Solution Explorer, open the shortcut menu for your top-level solution node. Choose Add then
choose New Item.
2. Under Installed, choose General.
3. In the right pane, choose Directed Graph Document and then choose Add.
You now have a blank map, which appears in your solution's Solution Items folder.
To create a new empty code map without adding it to your solution
1. Open the Architecture menu and choose New Code Map.
- or -
2. Open the File menu and choose New then choose File.
3. Under Installed, choose General.
4. In the right pane, choose Directed Graph Document and then choose Open.
You now have a blank map, which does not appear in your solution's folders.

See overall dependencies


See dependencies across your solution
1. On the Architecture menu, choose Generate Code Map for Solution.
You get a map that shows the top-level assemblies and aggregated links between them. The wider the
aggregate link, the more dependencies it represents.
2. Use the Legend button on the code map toolbar to show or hide the list of project type icons (such as
Test, Web, and Phone Project), code items (such as Classes, Methods, and Properties), and relation types
(such as Inherits From, Implements, and Calls).

This example solution contains Solution Folders (Tests and Components), Test Projects, Web Projects,
and assemblies. By default, all containment relationships appear as groups, which you can expand and
collapse. The Externals group contains anything outside your solution, including platform dependencies.
External assemblies show only those items that are used. By default, system base types are hidden on the
map to reduce clutter.
3. To drill down into the map, expand the groups that represent projects and assemblies. You can expand
everything by pressing CTRL+A to select all the nodes and then choosing Group, Expand from the
shortcut menu.
4. However, this may not be useful for a large solution. In fact, for complex solutions, memory limitations
may prevent you from expanding all the groups. Instead, to see inside an individual node, expand it.
Move your mouse pointer on top of the node and then click the chevron (down arrow) when it appears.

Or use the keyboard by selecting the item then pressing the plus key (+). To explore deeper levels of
code, do the same for namespaces, types, and members.

TIP
For more details of working with code maps using the mouse, keyboard, and touch, see Browse and rearrange
code maps.

5. To simplify the map and focus on individual parts, choose Filters on the code map toolbar and select just
the types of nodes and links you are interested in. For example, you can hide all the Solution Folder and
Assembly containers.
You can also simplify the map by hiding or removing individual groups and items from the map, without
affecting the underlying solution code.
6. To see the relationships between items, select them in the map. The colors of the links indicate the types
of relationship, as shown in the Legend pane.

In this example, the purple links are calls, the dotted links are references, and the light blue links are field
access. Green links can be inheritance, or they may be aggregate links that indicate more than one type
of relationship (or category).
TIP
If you see a green link, it might not mean there's just an inheritance relationship. There might also be method
calls, but these are hidden by the inheritance relationship. To see specific types of links, use the checkboxes in the
Filters pane to hide the types you aren't interested in.

7. To get more information about an item or link, move the pointer on top of it until a tooltip appears. This
shows details of a code element or the categories that a link represents.

8. To examine items and dependencies represented by an aggregate link, first select the link and then open
its shortcut menu. Choose Show Contributing Links (or Show Contributing Links on New Code
Map). This expands the groups at both ends of the link and shows only those items and dependencies
that participate in the link.
9. To focus in on specific parts of the map, you can continue to remove items you aren't interested in. For
example, to drill into class and member view, simply filter all the namespace nodes in the Filters pane.
10. Another way to focus in on a complex solution map is to generate a new map containing selected items
from an existing map. Hold CTRL while selecting the items you want to focus on, open the shortcut
menu, and choose New Graph from Selection.
11. The containing context is carried over to the new map. Hide Solution Folders and any other containers
you don't want to see using the Filters pane.

12. Expand the groups and select items in the map to view the relationships.
See also:
Browse and rearrange code maps
Customize code maps by editing the DGML files
Find potential problems in your code by running an analyzer.
See dependencies across assemblies or binaries
1. Create an empty code map, or open an existing code map (.dgml file).
2. Drag the assemblies or binaries you want to map from outside Visual Studio onto the map. For example,
drag assemblies or binaries from Windows Explorer or File Explorer.

NOTE
You can drag assemblies or binaries from Windows Explorer or File Explorer only if you are running it and Visual Studio at
the same User Access Control (UAC) permissions level. For example, if UAC is turned on and you are running Visual
Studio as Administrator, Windows Explorer or File Explorer will block the dragging operation. To work around this, make
sure that both are running with the same permission level, or turn UAC off.

See specific dependencies


For example, suppose you have a code review to perform in some files with pending changes. To see the
dependencies in those changes, you can create a code map from those files.
See specific dependencies in your solution
1. Open Solution Explorer. Select the projects, assembly references, folders, files, types, and members that
interest you. To find items that have dependencies on types or members, open the type or member's
shortcut menu from Solution Explorer. Choose the dependency type, and then select the results.
2. Map your items and their members. On the Solution Explorer toolbar click Show on Code Map .
3. The map shows the selected items within their containing assemblies.

You can also drag items from Solution Explorer, Class View, or Object Browser to a blank or an existing
code map. To create a blank map, see Create an empty code map. To include the parent hierarchy for
your items, press and hold the CTRL key while you drag items, or use the Include Parents button on the
code map toolbar to specify the default action.
NOTE
When you add items from a project that's shared across multiple apps, like Windows Phone or Windows Store,
those items appear on the map with the currently active app project. If you change context to another app
project and add more items from the shared project, those items now appear with the newly active app project.
Operations that you perform with an item on the map apply only to those items that share the same context.

4. To explore items, expand them. Move the mouse pointer on top of an item, then click the chevron (down
arrow) icon when it appears.

To expand all items, select them using CTRL+A, then open the shortcut menu for the map and choose
Group, Expand. However, this option isn't available if expanding all groups creates an unusable map or
memory issues.
5. Continue to expand items you are interested in, right down to the class and member level if required.

To see members that are in the code but don't appear on the map, click the Refetch Children icon in
the top left corner of a group.
6. To see more items related to those on the map, select one and choose Show Related on the code map
toolbar, then select the type of related items to add to the map. Alternatively, select one or more items,
open the shortcut menu, and then choose the Show... option for the type of related items to add to the
map. For example:
For an assembly, choose:

Show Assemblies This References Add assemblies that this assembly references. External
assemblies appear in the Externals group.

Show Assemblies Referencing This Add assemblies in the solution that reference this
assembly.

For a namespace, choose Show Containing Assembly, if it's not visible.


For a class or interface, choose:

Show Base Types For a class, add the base class and the implemented
interfaces.

For an interface, add the base interfaces.

Show Derived Types For a class, add the derived classes.

For an interface, add the derived interfaces and the


implementing classes or structs.

Show Types This References Add all classes and their members that this class uses.

Show Types Referencing This Add all classes and their members that use this class.

Show Containing Namespace Add the parent namespace.

Show Containing Namespace and Assembly Add the parent container hierarchy.

Show All Base Types Add the base class or interface hierarchy recursively.

Show All Derived Types For a class, add all the derived classes recursively.

For an interface, add all the derived interfaces and


implementing classes or structs recursively.

For a method, choose:

Show Methods This Calls Add methods that this method calls.

Show Fields This References Add fields that this method references.

Show Containing Type Add the parent type.


Show Containing Type, Namespace, and Assembly Add the parent container hierarchy.

Show Overridden Methods For a method that overrides other methods or


implements an interface's method, add all the abstract
or virtual methods in base classes that are overridden
and, if any, the interface's method that is implemented.

For a field or property, choose:

Show Containing Type Add the parent type.

Show Containing Type, Namespace, and Assembly Add the parent container hierarchy.

7. The map shows the relationships. In this example, the methods called by the Find method, and their
location in the solution or externally.
8. To simplify the map and focus on individual parts, choose Filters on the code map toolbar and select just
the types of nodes and links you are interested in. For example, turn off display of Solution Folders,
Assemblies, and Namespaces.
See dependencies between C and C++ source files and header files
If you want to create more complete maps for C++ projects, set the browse information compiler option (/FR)
on those projects. Otherwise, a message appears and prompts you to set this option. If you select OK, this sets
the option for just the current map. You can choose to hide the message for all later maps. If you hide this
message, you can make it appear again. Set the following registry key to 0 or delete the key:
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\14.0\NativeProvider : AutoEnableSbr
When you open a solution that contains Visual C++ projects, it might take some time to update the IntelliSense
database. During this time, you might not be able to create code maps for header (.h or #include ) files until the
IntelliSense database finishes updating. You can monitor the update progress in the Visual Studio status bar. To
resolve issues or messages that appear because certain IntelliSense settings are disabled, see Troubleshoot
maps for C and C++ code.
To see dependencies between all source files and header files in your solution, on the Architecture
menu, choose Generate Graph of Include Files.
To see dependencies between the currently open file and related source files and header files, open
either the source file or the header file. Open the file shortcut menu anywhere inside the file. Choose
Generate Graph of Include Files.

Troubleshoot maps for C and C++ code


These items aren't supported for C and C++ code:
Base types don't appear on maps that include the parent hierarchy.
Most Show menu items aren't available for C and C++ code.
These issues might happen when you create code maps for C and C++ code:

ISSUE POSSIBLE CAUSE RESOLUTION

The code map failed to generate. No projects in the solution were built Fix the build errors that occurred and
successfully. then regenerate the map.

Visual Studio becomes unresponsive The program database (.pdb) file might Rebuild the solution and then try
when you try to generate a code map be corrupted. again.
from the Architecture menu.
A .pdb file stores debugging
information, such as type, method,
and source file information.

Certain settings for the IntelliSense Certain IntelliSense settings might be Turn on the settings to enable them.
browsing database are disabled. disabled in the Visual StudioOptions
dialog box. See Options, Text Editor, C/C++,
Advanced.

The message Unknown Methods The binary file might not have a base Turn on the /FIXED:NO option in the
appears on a method node. relocation table. linker.

This issue occurs because the name of


the method cannot be resolved.
ISSUE POSSIBLE CAUSE RESOLUTION

The program database (.pdb) file might Turn on the /DEBUG option in the
not be built. linker.

A .pdb file stores debugging


information, such as type, method,
and source file information.

Cannot open or find the .pdb file in Make sure that the .pdb file exists in
the expected locations. the expected locations.

Debug information has been stripped If the /PDBSTRIPPED option was used
from the .pdb file. in the linker, include the complete .pdb
file instead.

The caller is not a function and is When the caller is a thunk, try using
either a thunk in the binary file or a _declspec(dllimport) to avoid the
pointer in the data section. thunk.

Make code maps render more quickly


When you generate a map for the first time, Visual Studio indexes all the dependencies that it finds. This process
might take some time, especially for large solutions, but will improve performance later. If your code changes,
Visual Studio re-indexes just the updated code. To minimize the time taken for the map to finish rendering,
consider the following:
Map only the dependencies that interest you.
Before you generate the map for an entire solution, reduce the solution scope.
Turn off automatic build for the solution with the Skip Build button on the code map toolbar.
Turn off automatic adding of parent items with the Include Parents button on the code map toolbar.
Edit the code map file directly to remove nodes and links you don't need. Changing the map doesn't
affect the underlying code. See Customize code maps by editing the DGML files.

Although Visual Studio can run with 1 GB of memory, we recommended that your computer have at
least 2 GB of memory to avoid long delays while Visual Studio creates the code index and generates the
map.
It might take more time to create maps or add items to a map from Solution Explorer when a project
item's Copy to Output Directory property is set to Copy Always. This might cause issues with
incremental builds and Visual Studio to rebuild the project each time. To increase performance, change
this property to Copy if newer or PreserveNewest . See Incremental Builds.
The completed map will show dependencies only for successfully-built code. If build errors occur for
certain components, these errors appear on the map. Make sure that a component actually builds and
has dependencies on it before you make architectural decisions based on the map.
Share code maps
Share the map with other Visual Studio users
Use the File menu to save the map.
-or-
To save the map as part of specific project, on the map toolbar, choose Share, Move <CodeMapName>.dgml
into, and then choose the project where you want to save the map.

Visual Studio saves the map as a .dgml file that you can share with other users of Visual Studio Enterprise and
Visual Studio Professional.

NOTE
Before you share a map with those who use Visual Studio Professional, make sure to expand any groups, show hidden
nodes and cross-group links, and retrieve any deleted nodes that you want others to see on your map. Otherwise, other
users won't be able to see these items.
The following error might occur when you save a map that is in a modeling project or was copied from a modeling
project to another location:
"Cannot save fileName outside the project directory. Linked items are not supported."
Visual Studio shows the error, but creates the saved version anyway. To avoid the error, create the map outside the
modeling project. You can then save it to the location that you want. Just copying the file to another location in the
solution, and then trying to save it will not work.

Export the map as an image so you can copy it into other applications, such as Microsoft Word or PowerPoint
1. On the code map toolbar, choose Share, Email as Image or Copy Image.
2. Paste the image into another application.
Export the map as an XPS file so you can see it in XML or XAML viewers like Internet Explorer
1. On the code map toolbar, choose Share, Email As Portable XPS or Save As Portable XPS.
2. Browse to where you want to save the file.
3. Name the code map. Make sure that the Save as type box is set to XPS files (*.xps). Choose Save.
What else can I do?
Use code maps to debug your applications
Map methods on the call stack while debugging
Find potential problems using code map analyzers
Browse and rearrange code maps
Customize code maps by editing the DGML files
Use code maps to debug your applications
10/18/2017 4 min to read Edit Online

Code maps can help you avoid getting lost in large code bases, unfamiliar code, or legacy code. For example,
when you're debugging, you might have to look at code across many files and projects. Use code maps to
navigate around pieces of code and understand the relationships between them. That way, you don't have to keep
track of this code in your head, or draw a separate diagram. So, when your work is interrupted, code maps help
refresh your memory about the code you're working on.

A green arrow shows where your cursor appears in the editor


For details of the commands and actions you can use when working with code maps, see Browse and rearrange
code maps.

Understand the problem


Suppose there's a bug in a drawing program that you're working on. To reproduce the bug, you open the solution
in Visual Studio and press F5 to start debugging.
When you draw a line and choose Undo my last stroke, nothing happens until you draw the next line.
So you start investigating by searching for the Undo method. You find it in the PaintCanvas class.

Start mapping the code


Now start mapping the undo method and its relationships. From the code editor, you add the undo method and
the fields that it references to a new code map. When you create a new map, it might take some time to index the
code. This helps later operations run faster.
TIP
The green highlighting shows the last items that were added to the map. The green arrow shows your cursor's position in
the code. Arrows between items represent different relationships. You can get more info about items on the map by
moving the mouse over them and examining their tooltips.

Navigate and examine code from the map


To see the code definition for each field, double-click the field on the map or select the field and press F12. The
green arrow moves between items on the map. Your cursor in the code editor also moves automatically.
TIP
You can also move the green arrow on the map by moving your cursor in the code editor.

Understand relationships between pieces of code


Now you want to know which other code interacts with the history and paintObjects fields. You can add all the
methods that reference these fields to the map. You can do this from the map or from the code editor.
NOTE
If you add items from a project that's shared across multiple apps, like Windows Phone or Windows Store, then those items
always appear with the currently active app project on the map. So, if you change the context to another app project, then
the context on the map also changes for any newly added items from the shared project. Operations that you perform with
an item on the map apply only to those items that share the same context.

Change the layout to rearrange the flow of relationships and make the map easier to read. You can also move
items around the map by dragging them.

TIP
By default, Incremental Layout is turned on. This rearranges the map as little as possible when you add new items. To
rearrange the entire map every time you add new items, turn off Incremental Layout.
Let's examine these methods. On the map, double-click the PaintCanvas method, or select this method and press
F12. You learn that this method creates history and paintObjects as empty lists.

Now repeat the same steps to examine the clear method definition. You learn that clear performs some tasks
with paintObjects and history . It then calls the Repaint method.
Now examine the addPaintObject method definition. It also performs some tasks with history and
paintObjects . It also calls Repaint .

Find the problem by examining the map


It seems that all the methods that modify history and paintObjects call Repaint . Yet the undo method doesn't
call Repaint , even though undo modifies the same fields. So you think you can fix this problem by calling
Repaint from undo .
If you didn't have a map to show you this missing call, it might have been harder to find this problem, especially
with more complex code.

Share your discovery and next steps


Before you or someone else fixes this bug, you can make notes on the map about the problem and how to fix it.

For example, you can add comments to the map and flag items using colors.
If you have Microsoft Outlook installed, you can email the map to others. You can also export the map as an
image or another format.

Fix the problem and show what you did


To fix this bug, you add the call for Repaint to undo .

To confirm your fix, you restart your debugging session and try to reproduce the bug. Now choosing Undo my
last stroke works as you expect and confirms you made the correct fix.
You can update the map to show the fix you made.

Your map now shows a link between undo and Repaint.


NOTE
When you update the map, you might see a message that says the code index used to create your map was updated. This
means someone changed the code, which causes your map to not match the current code. This doesn't stop you from
updating the map, but you might have to recreate the map to confirm that it matches the code.

Now you're done with your investigation. You successfully found and fixed the problem by mapping the code.
You also have a map that helps you navigate around the code, remember what you learned, and shows the steps
you took to fix the problem.

See Also
Map methods on the call stack while debugging
Visualize code
Map methods on the call stack while debugging in
Visual Studio
10/18/2017 4 min to read Edit Online

Create a code map to visually trace the call stack while you're debugging. You can make notes on the map to track
what the code is doing so you can focus on finding bugs.

You'll need:
Visual Studio Enterprise
Code that you can debug, such as Visual C# .NET, Visual Basic .NET, C++, JavaScript, or X++
See:
Video: Debug visually with Code Map debugger integration (Channel 9)
Map the call stack
Make notes about the code
Update the map with the next call stack
Add related code to the map
Find bugs using the map
Q&A
For details of the commands and actions you can use when working with code maps, see Browse and
rearrange code maps.
Map the call stack
1. Start debugging. (Keyboard: F5)
2. After your app enters break mode or you step into a function, choose Code Map. (Keyboard: Ctrl + Shift +
`)

The current call stack appears in orange on a new code map:

The map will update automatically while you continue debugging. See Update the map with the next call
stack.

Make notes about the code


Add comments to track what's happening in the code. To add a new line in a comment, press Shift + Return.
Update the map with the next call stack
Run your app to the next breakpoint or step into a function. The map adds a new call stack.

Add related code to the map


Now you've got a map - what next? If you're working with Visual C# .NET or Visual Basic .NET, add items, such as
fields, properties, and other methods, to track what's happening in the code.
Double-click a method to see its code definition, or use the shortcut menu for the method. (Keyboard: Select the
method on the map and press F12)
Add the items that you want to track on the map.

NOTE
By default, adding items to the map also adds the parent group nodes such as the class, namespace, and assembly. While this
is useful, you can keep the map simple by turning off this feature using the Include Parents button on the map toolbar, or
by pressing CTRL when you add items.

Here you can easily see which methods use the same fields. The most recently added items appear in green.
Continue building the map to see more code.
Find bugs using the map
Visualizing your code can help you find bugs faster. For example, suppose you're investigating a bug in a drawing
program. When you draw a line and try to undo it, nothing happens until you draw another line.
So you set breakpoints in the clear , undo , and Repaint methods, start debugging, and build a map like this one:
You notice that all the user gestures on the map call Repaint , except for undo . This might explain why undo
doesn't work immediately.
After you fix the bug and continue running the program, the map adds the new call from undo to Repaint :

Q&A
Not all calls appear on the map. Why?
By default, only your own code appears on the map. To see external code, turn it on in the Call Stack
window:
or turn off Enable Just My Code in the Visual Studio debugging options:

Does changing the map affect the code?


Changing the map doesn't affect the code in any way. Feel free to rename, move, or remove anything on the
map.
What does this message mean: "The diagram may be based on an older version of the code"?
The code might have changed after you last updated the map. For example, a call on the map might not exist
in code anymore. Close the message, then try rebuilding the solution before updating the map again.
How do I control the map's layout?
Open the Layout menu on the map toolbar:
Change the default layout.
To stop rearranging the map automatically, turn off Automatically Layout when Debugging.
To rearrange the map as little as possible when you add items, turn off Incremental Layout.
Can I share the map with others?
You can export the map, send it to others if you have Microsoft Outlook, or save it to your solution so you
can check it into Team Foundation version control.

How do I stop the map from adding new call stacks automatically?
Choose on the map toolbar. To manually add the current call stack to the map, press Ctrl + Shift + `.
The map will continue highlighting existing call stacks on the map while you're debugging.
What do the item icons and arrows mean?
To get more info about an item, move the mouse pointer over it and look at the item's tooltip. You can also
look at the Legend to learn what each icon means.

See:
Map the call stack
Make notes about the code
Update the map with the next call stack
Add related code to the map
Find bugs using the map

See Also
Map dependencies across your solutions
Use code maps to debug your applications
Find potential problems using code map analyzers
Browse and rearrange code maps
Find potential problems using code map analyzers
10/18/2017 1 min to read Edit Online

Run analyzers on code maps to help you identify code that might be overly complex or that might need
improvement. For example, you can use these analyzers:

TO FIND CODE THAT HAS EXAMINE THESE AREAS TO SEE WHETHER

Loops or circular dependencies You can simplify them and consider whether you can break
these cycles.

Too many dependencies They are performing too many functions or to determine the
impact of changing these areas. A well-formed code map will
show a minimal number of dependencies. To make code
easier to maintain, change, test, and reuse, consider whether
you can refactor these areas so that they are more clearly
defined, or whether you can merge code that performs
similar functions.

No dependencies They are necessary or whether you should remove this code.

Analyze code maps


1. On the map toolbar, choose Layout, Analyzers, and then analyzer that you want to run:

ANALYZER TO IDENTIFY NODES THAT

Circular References Analyzer Have circular dependencies on each other. Note: Circular
dependencies that are in the Generics group are not
shown on the map when you expand the group.

Find Hubs Analyzer Are in the top 25% of highly-connected nodes

To hide all other nodes on the map

- Open the shortcut menu for the map, choose


Advanced, Select, Hide Unselected.
The map hides the unselected nodes, and the analyzer
identifies new nodes as hubs.

Unreferenced Nodes Analyzer Do not have references from any other nodes. Caution:
Verify each of these cases before assuming that the code
is not used. Certain dependencies such as XAML
dependencies and run-time dependencies cannot be
found statically in the code.

Code map analyzers will continue to run after you apply them. If you change the map, any applied
analyzers will automatically reprocess the updated map. To stop running an analyzer, on the map toolbar,
choose Layout, Analyzers. Turn off the selected analyzer.
TIP
If you have a very large map, running an analyzer might cause an out of memory exception. If this occurs, edit the map to
reduce its scope or generate a smaller one, and then run the analyzer.

See Also
Map dependencies across your solutions
Use code maps to debug your applications
Map methods on the call stack while debugging
Browse and rearrange code maps
10/18/2017 23 min to read Edit Online

Rearrange items on code maps to make them easier to read and improve their performance.
You can customize code maps without affecting the underlying code in a solution. This is useful when you want
to focus on key code elements or communicate ideas about the code. For example, to highlight interesting areas,
you can select code elements on the map and filter them, change the style of code elements and links, hide or
delete code elements, and organize code elements using properties, categories, or groups.
Requirements
To create code maps, you must have Visual Studio Enterprise.
You can view code maps and make limited edits to code maps in Visual Studio Professional.

Get started working with code maps


Create a code map (see Map dependencies across your solutions for more details). If you don't want to wait for
the map to finish generating, click the Cancel link at any time to stop the generation process. However, you will
not see the details of all dependencies and links if you do this.
After you generate the map, get started with these tips for reviewing your code:
Look at the natural dependency clusters in the code. On the map toolbar, choose Layout, Quick Clusters
. See Change the map layout.

Organize the map into smaller areas by grouping related nodes. Collapse those groups to see only the
intergroup dependencies, which appear automatically. See Group nodes.
Use filters to simplify the map and focus on the types of nodes or links you are interested in. See Filter
nodes and links.
Maximize the performance of large maps. See Map dependencies across your solutions for more
information.For example, turn on Skip Build on the map toolbar so that Visual Studio doesn't rebuild
your solution when you update items on the map.

Change the map layout


TO PERFORM THESE STEPS

Arrange the dependency flow for the entire map in a specific On the map toolbar, choose Layout, and then:
direction. This can help you see architectural layers in the
code. - Top to Bottom
- Bottom to Top
- Left to Right
- Right to Left

See natural dependency clusters in the code with the most On the map toolbar, choose Layout, and then Quick
dependent nodes at the center of the clusters and the least Clusters .
dependent nodes at the outside of those clusters.

Select one or more nodes on the map. Click on a node to select it. To select or deselect more than
one node, hold CTRL while clicking.

Keyboard: press TAB or use the arrow keys to move the


dotted focus rectangle to a node and press SPACE to select
it. Press CTRL + SPACE to multi-select or deselect nodes.

Move specific nodes around on the map. Drag nodes to move them. To move other nodes and links
out of the way as you drag nodes, press and hold the SHIFT
key.

Keyboard: hold CTRL and press the arrow keys.

Change the layout inside a group independently of the other Select a node and open the shortcut menu. Choose Layout
nodes and groups on the map. and select a layout style.

- or -

Select a node and expand it to show the child nodes. Click


the node title to show group pop-up toolbar, and open the
Change the layout style of the group list. Select one
of the tree layouts, Quick Clusters, or List View (which
arranges the group's contents into a list).

See Group nodes for more details.

Undo an action in the map. Press CTRL + Z or use the Visual Studio Undo command.

Browse the map


TO PERFORM THESE STEPS

Scan the map. Drag the map in any direction using the mouse.

- or -

Hold SHIFT and rotate the mouse wheel to scroll


horizontally. Hold SHIFT + CTRL and rotate the mouse
wheel to scroll horizontally.
TO PERFORM THESE STEPS

Zoom in or out of the map. Rotate the mouse wheel.

- or -

Use the Zoom drop-down list on the code map toolbar.

- or -

Use the keyboard shortcuts. To zoom in, press CTRL +


SHIFT + . (period). To zoom out, press CTRL + SHIFT + ,
(comma).

Zoom in on a specific area using the mouse. Hold the right mouse button while drawing a rectangle
around that area you are interested in.

Resize and fit the map in its window. Choose Zoom to Fit from the Zoom list on the code map
toolbar.

- or -

Click the icon on the code map toolbar.


Zoom to fit
Keyboard: press CTRL + 0 (zero).

Find a node on the map by its name. Tip: This works only for 1. Choose the icon on the code map toolbar
items on the map. To find items in your solution but not on Find
the map, find them in Solution Explorer, and then drag (Keyboard: press CTRL + F) to show the search box in the
them to the map. (Drag your selection, or on the Solution upper right corner of the map.
Explorer toolbar, click Show on Code Map). 2. Type the item name and press Return or click the
"magnifier" icon. The first item that matches your search
appears selected on the map.
3. To customize your search, open the drop-down list and
choose a search option. The options are Find Next, Find
Previous, and Select All. Then click the corresponding
button next to the search textbox.

Alternatively, use the keyboard: press F3 to select the next


matching node or SHIFT + F3 to select the previous
matching node.
4. Select any one of the options that specify how search
terms are handled by clicking the icons below the search
textbox.

The options are, from left to right, case sensitive matching,


match whole words only, use .NET regular expression syntax,
automatically expand groups to show matches to enclosed
items. Important: You can use the search box to find
matches in collapsed groups only if those groups were
expanded previously. To find these matches and expand their
parent groups automatically, choose this option under the
search box.

Select all unselected nodes. Open the shortcut menu for the selected nodes. Choose
Select, Invert Selection.
TO PERFORM THESE STEPS

Select additional nodes that link to the selected ones. Open the shortcut menu for the selected nodes. Choose
Select and one of these:

- To select additional nodes that link directly to the selected


node, choose Incoming Dependencies.
- To select additional nodes that link directly from the
selected node, choose Outgoing Dependencies.
- To select additional nodes that link directly to and from the
selected node, choose Both.
- To select all nodes that link to and from the selected node,
choose Connected Subgraph.
- To select all children of the selected node, choose Children.

Filter nodes and links


TO PERFORM THESE STEPS

Show or hide the Filters pane. Choose the Filters button on the code map toolbar. The
Filters pane is displayed as a tabbed page in the Solution
Explorer window by default.

Filter the types of nodes that are shown on the map. Set or clear the checkboxes in the Code Elements list in the
Filters pane.

Filter the types of links that are shown on the map. Set or clear the checkboxes in the Relationships list in the
Filters pane.

Show or hide Test project nodes on the map. Set or clear the Test Assets checkbox in the Miscellaneous
list in the Filters pane.

The icons shown in the Legend panel of the map reflect the settings you make in the list. To show or hide the
Legend panel, click the Legend button on the code map toolbar.

Examine nodes and links


Code maps show these kinds of links:
An individual link represents a single relationship between two nodes.
A cross-group link represents a relationship between two nodes in different groups.
An aggregate link represents all the relationships that point in the same direction between two groups.

TIP
By default, the map shows cross-group links only for selected nodes. To change this behavior to show or hide aggregated
links between groups, click Layout on the code map toolbar and choose Advanced, then Show All Cross-Group Links
or Hide All Cross-Group Links. See Hide or show nodes and links for more details.

TO PERFORM THESE STEPS


TO PERFORM THESE STEPS

See more information about a node or a link. Move the mouse pointer on top of the node or link until a
tooltip appears.

The tooltip for an aggregated link lists the individual


dependencies that it represents.

- or -

Open the shortcut menu for the node or the link. Choose
Edit, Properties.

Show or hide the contents of a group. - To expand a group, open the shortcut menu for the node
and choose Group, Expand.
- or -
Move the mouse pointer on top of the node until the
chevron (down arrow) button appears. Click this button to
expand the group. Keyboard: to expand or collapse the
selected group, press the PLUS key (+) or the MINUS key (-).
- To collapse a group, open the shortcut menu for the node
and choose Group, Collapse.
- or -
Move the mouse pointer on top of a group until the chevron
(up arrow) button appears. Click this button to collapse the
group.
- To expand all groups, press CTRL + A to select all the
nodes. Open the shortcut menu for the map and choose
Group, Expand. Note: This command is not available if
expanding all groups generates an unusable map or memory
issues. It is recommended that you expand the map only to
the level of detail that you care about.
- To collapse all groups, open the shortcut menu for a node
or for the map. Choose Group, Collapse All.
TO PERFORM THESE STEPS

See the code definition for a namespace, type, or member. Open the shortcut menu for the node and choose Go To
Definition.

-or-

Double-click the node. For expanded groups, double-click the


header on the group.

-or-

Select the node and press F12.

For example:

- For a namespace containing one class, the code file for the
class opens to show the definition of that class. In other
cases, the Find Symbol Results window shows a list of code
files. Note: When you perform this task on a Visual Basic
.NET namespace, the code file behind the namespace does
not open. This issue also occurs when you perform this task
on a group of selected nodes that include a Visual Basic .NET
namespace. To work around this issue, browse manually to
the code file behind the namespace, or omit the node for the
namespace from your selection.
- For a class or a partial class, the code file for that class
opens to show the class definition.
- For a method, the code file for the parent class opens to
show the method definition.

Examine dependencies and items that participate in an Select the links you're interested in and open the shortcut
aggregate link. menu for your selection. Choose Show Contributing Links
or Show Contributing Links on New Code Map.

Visual Studio expands the groups at both ends of the link


and shows only those items and dependencies that
participate in the link. Note: When you examine
dependencies between items in partial groups, you might see
this behavior:
Links to items that don't participate in your
examination disappear from the map, even though
those links still exist.
Suppose you examine a link to an item in a partial
group, and then later examine a different link to the
same item. During your second examination, the
target partial group shows only items from your first
examination. Links and target items that didn't
participate in your first examination but participate in
your second examination don't appear.
To see missing items from a group, choose Refetch Children
(which indicates that not all members of a group appear
on the map). You can also try undoing your actions
(Keyboard: press CTRL+Z) and examine the dependencies on
a new map.
TO PERFORM THESE STEPS

Examine dependencies across multiple nodes in different Expand the groups so you can see all their children. Select all
groups. the nodes that interest you, including their children. The map
shows the cross-group links between the selected nodes.

To select all nodes in a group, press and hold SHIFT and the
left mouse button while you draw a rectangle around that
group. To select all nodes on a map, press CTRL+A. Tip: To
show cross-group links at all times, choose Layout on the
map toolbar, Advanced, Show All Cross-Group Links.

See the items that a node or link references. Open the shortcut menu for the node and choose Find All
References. Note: This applies only when the Reference
attribute is set for the node or link in the map's .dgml file. To
add references to items from nodes or links, see Customize
code maps by editing the DGML files.

Hide or show nodes and links


Hiding nodes keeps them from participating in layout algorithms. By default, cross-group links are hidden.
Cross-group links are individual links that connect nodes across groups. When groups are collapsed, the map
aggregates all cross-group links into single links between groups. When you expand a group and select nodes
inside the group, cross-group links appear and show the dependencies in that group.
Cau t i on

Before you share a map that was created in Visual Studio Enterprise with those who use Visual Studio
Professional, make sure to unhide any nodes or cross-group links that you want others to see. Otherwise, those
users won't be able to unhide those items.
To hide or show nodes
TO PERFORM THESE STEPS

Hide selected nodes. 1. Select nodes you want to hide.


2. Open the shortcut menu for the selected nodes or for the
map. Choose Select, Hide Selected.

Hide unselected nodes. 1. Select nodes you want to remain visible.


2. Open the shortcut menu for the selected nodes or for the
map. Choose Select, Hide Unselected.

Show hidden nodes. - To show all hidden nodes inside a group, first make sure
the group is expanded. Open the shortcut menu and choose
Select, Unhide Children.
- or -
Click the Unhide Children icon in the upper left corner of
the group (this is visible only when there are hidden child
nodes).
- To show all hidden nodes, open the shortcut menu for the
map or a node and choose Select, Unhide All.

To hide or show links


ON THE MAP TOOLBAR, CHOOSE LAYOUT, ADVANCED, AND THEN
TO CHOOSE
ON THE MAP TOOLBAR, CHOOSE LAYOUT, ADVANCED, AND THEN
TO CHOOSE

Show cross-group links at all times. Show All Cross-Group Links. This hides aggregated links
between groups.

Hide cross-group links at all times. Hide All Cross-Group Links

Show only cross-group links for selected nodes. Show Cross-Group Links On Selected Nodes

Hide all links. Hide All Links. To show links again, choose one of the
options listed above.

Group nodes
TO PERFORM THESE STEPS

Show container nodes as group nodes or leaf nodes. To show container nodes as leaf nodes: select the nodes,
open the shortcut menu for your selection, and choose
Group, Convert To Leaf.

To show container nodes as group nodes: select the nodes,


open the shortcut menu for your selection, and choose
Group, Convert To Group.

Change the layout inside a group. Select the group, open the shortcut menu, choose Layout,
and select the layout style you want.

- or -

1. Select the group and make sure it is expanded.


2. Click the group header again and the group toolbar
appears.

3. Open the Change the layout style of the group list


and choose the layout style you want.

List View rearranges the group's members into list. Graph


Default resets the group layout to the map default layout.
For other options, see Change the map layout.

Add a node to a group. Drag the node onto the group.

While you drag the node, Visual Studio displays an indicator


to show that you are moving the node.

You can also drag nodes out of a group.

Add a node to a non-group node. Drag the node onto the target node. You can convert any
target node into a group by adding nodes to it.
TO PERFORM THESE STEPS

Group selected nodes. 1. Select the nodes that you want to group. A pop-up
toolbar appears above the last node you select.

2. On the toolbar, choose the fourth icon Group the


selected nodes (if the node is expanded it will have five
instead of four icons). Type the name for the new group and
press Return.
- or -
Select the nodes that you want to group and open the
shortcut menu for your selection. Choose Group, Add
Parent Group, type the name for the new group, and press
Return.

You can rename a group. Open the shortcut menu for the
group and choose Edit, Properties to open the Visual
Studio Properties window. In the Label property, rename the
group as required.

Remove groups. Select the group or groups that you want to remove. Open
the shortcut menu for your selection and choose Group,
Remove Group.

Remove nodes from their parent group. Select the nodes that you want to move. Open the shortcut
menu for your selection and choose Group, Remove From
Parent. This removes nodes up to their grandparent, or to
outside the group if they have no grandparent group.

- or -

Select the nodes and drag them out of the group.

Add, remove, or rename nodes, links, and comments


You can display more or fewer items on a map in order to drill down or to simplify the map. You can also
rename items, and add comments to items.
Cau t i on

Before you share a map that was created using Visual Studio Enterprise with those who use Visual Professional,
make sure any code elements you want others to see are visible on the map. Otherwise, those users won't be
able to retrieve deleted code elements.
Add a node for a code element
TO PERFORM THESE STEPS

Add a new generic node at the current mouse pointer 1. Move the mouse pointer to the place on the map where
location. you want to put the new code element and press Insert.
- or -
Open the shortcut menu for the map and choose Edit, Add,
Generic Node.
2. Type the name for the new node and press Return.
TO PERFORM THESE STEPS

Add a specific type of code element node at the current 1. Move the mouse pointer to the place on the map where
mouse pointer location. you want to put the new code element and open the
shortcut menu for the map.
2. Choose Edit, Add, and select the type of node you want.
3. Type the name for the new node and press Return.

Add a generic or a specific type of code element node to a 1. Select the group node and open the shortcut menu.
group. 2. Choose Edit, Add, and select the type of node you want.
3. Type the name for the new node and press Return.

Add a new node of the same type, and linked from, an 1. Select the code element. A pop-up toolbar appears above
existing node. it.

2. On the toolbar, choose the second icon Create a node


with the same category as this node and add a new link
to it.
3. Choose a place on the map to put the new code element
and click the left mouse button.
4. Type the name for the new node and press Return.

Add a new generic node that is linked from an existing code 1. Using the keyboard, press Tab until the code element to
element that has focus. link from has the focus (dotted rectangle).
2. Press Alt+Insert.
3. Type the name for the new node and press Return.

Add a new generic node that links to an existing code 1. Using the keyboard, press Tab until the code element to
element that has focus. link to has the focus (dotted rectangle).
2. Press Alt+Shift+Insert.
3. Type the name for the new node and press Return.

TO ADD CODE ELEMENTS FOR PERFORM THESE STEPS


TO ADD CODE ELEMENTS FOR PERFORM THESE STEPS

Code elements in the solution. 1. Find the code element in Solution Explorer. Use the
Solution Explorer search box or browse the solution. Tip:
To find code elements that have dependencies on a type or a
member, open the shortcut menu for the type or the
member in Solution Explorer. Choose the relationship that
interests you. Solution Explorer shows only those code
elements with the specified dependencies.
2. Drag the code elements that interest you to the map
surface. You can also drag code elements from Class View or
Object Browser.
- or -
In Solution Explorer, select the code elements that you
want to map. Then, on the Solution Explorer toolbar, click
Show on Code Map.

By default, the parent container hierarchy for the new code


elements is shown on the map. Use the Include Parents
button on the code map toolbar to change this behavior.
When turned off, only the code element itself is added to the
map. To reverse this behavior for just one drag and drop
action, press and hold the CTRL key while you drag the code
elements to the map.

Visual Studio adds code elements for the top-level code


elements in your selection. To see if a code element contains
other code elements, move the mouse pointer on top of the
code element so that the chevron (down arrow) appears.
Choose the chevron to expand the code element. To expand
all code elements, press CTRL+A to select all elements, open
the shortcut menu for the map, and choose Group, Expand.
This command is not available if expanding all groups would
produce an unusable map or cause shortage of memory
issues.

Code elements related to code elements on the map. Click the Show Related button on the code map toolbar
and choose the type of related items you are interested in.

- or -

Open the shortcut menu for the code element. Choose one
of the Show ... items on the menu depending on the kind of
relationship that interests you. For example, you can see
items that the current item references, items that reference
the current item, base and derived types for classes, method
callers, and the containing classes, namespaces, and
assemblies.

For more details, see this topic.

Compiled .NET assemblies (.dll or .exe) or binaries. Drag the assemblies or binaries from outside Visual Studio to
a map.

You can drag from Windows Explorer or File Explorer only if


you are running it and Visual Studio at the same User Access
Control (UAC) permissions level. For example, if UAC is
turned on and you are running Visual Studio as
Administrator, Windows Explorer or File Explorer will block
the dragging operation.

A d d a l i n k b e t w e e n e x i st i n g c o d e e l e m e n t s

1. Select the source code element. A toolbar appears above the code element.
2. On the toolbar, choose the first icon, Create new link from this node to which ever node that you
click on next.
3. Select the target code element. A link appears between the two code elements.
- or -
4. Select the source code element on the map.
5. If you have a mouse installed, move the mouse pointer outside the bounds of the map.
6. Open the code element's shortcut menu and choose Edit, Add, Generic Link.
7. Tab to and select the target code element for the link.
8. Press RETURN.
A d d a c o m m e n t t o a n e x i st i n g n o d e o n t h e m a p

1. Select the code element. A toolbar appears above it.

2. On the toolbar, choose the third icon, Create a new comment node with a new link to the selected
node.
- or -
Open the shortcut menu for the code element and choose Edit, New Comment.
3. Type your comments. To type on a new line, press SHIFT + RETURN.
A d d a c o m m e n t t o t h e m a p i t se l f

1. Open the shortcut menu for the map and choose Edit, New Comment.
2. Type your comments. To type on a new line, press SHIFT + RETURN.
Ren am e a c o de el em en t o r l i n k

1. Select the code element or the link you want to rename.


2. Press F2, or open the shortcut menu and choose Edit, Rename.
3. When the edit box appears in the map, rename the code element or the link.
- or -
4. Open the shortcut menu and choose Edit, Properties.
5. Edit the Label property in the Visual Studio Properties window.
R e m o v e a c o d e e l e m e n t o r l i n k fr o m t h e m a p

1. Select the code element or link and press the Delete key.
- or -
Open the shortcut menu for the code element or link and choose Edit, Remove.
2. If the element or link is part of a group, the Refetch Children button appears inside the group. Click
this to retrieve missing elements and links.
You can remove code elements and links from a map without affecting the underlying code. When you
delete them, their definitions are removed from the DGML (.dgml) file.
Maps created by editing the DGML, by adding undefined code elements, or by using some earlier versions
of Visual Studio, do not support this capability.
F l a g a c o d e e l e m e n t fo r fo l l o w - u p

1. Select the code element or link you want to flag for follow-up.
2. Open the shortcut menu and choose Edit, Flag for Follow Up.
By default, the code element gains a red background. Consider adding a comment to it with the
appropriate follow-up information.
Change the background color of the element or clear the follow-up flag by choosing Edit, Other Flag
Colors.

Change the style of a code element or link


You can change the icons on code elements and the colors of code elements and links using predefined icons
and colors. For example, you can choose a color to highlight code elements and links that have a certain category
or property. This lets you identify and focus on specific areas of the map. You can specify custom icons and
colors by editing the map's .dgml file; see Customize code maps by editing the DGML files.
To apply a predefined color or icon to code elements or links with a certain category or property
1. On the map toolbar, choose Legend.
2. In the Legend box, see if the code element category or property already appears in the list.
3. If the list does not include the category or property, choose + in the Legend box, then choose Node
Property, Node Category, Link Property, or Link Category. Then choose the property or category. The
category or property now appears in the Legend box.

NOTE
To create and assign a category or a property to a code element, you can edit the map's .dgml file; see Customize
code maps by editing the DGML files.

4. In the Legend box, click the icon next to the category or property you added or you want to change.
5. Use the following table to select the style that you want to change:

TO CHANGE THE CHOOSE

Background color Background

Outline color Stroke

Text color (a letter "f" is displayed to show the result) Foreground

Icon Icons

The Color Set Picker or Icon Set Picker dialog box appears for you to select a color or icon.
6. In the Color Set Picker or Icon Set Picker dialog box, do one of the following:

TO APPLY A PERFORM THESE STEPS

Set of colors or icons Open the Select color (or icon) set list. Select a set of
colors or icons.
TO APPLY A PERFORM THESE STEPS

Specific color or icon Open the category or property value list. Select a color or
icon.

NOTE
You can rearrange, delete, or temporarily inactivate styles in the Legend box. See Edit the Legend box.

Edit the Legend box


You can rearrange, delete, or temporarily inactivate styles in the Legend box:
1. Open the shortcut menu for a style in the Legend box.
2. Perform one of the following tasks:

TO CHOOSE

Deactivate the code element Disable

Delete the code element Delete

Move the style up Move Up

Move the code element down Move Down

Copy styles from one map to another


1. Make sure the Legend box appears on the source map. If it is not visible, on the map toolbar, click
Legend.
2. Open the shortcut menu for the Legend box. Choose Copy Legend.
3. Paste the legend onto the target map.

Merge code maps


You can merge maps by copying and pasting code elements between maps. If the code element identifiers
match, then pasting code elements functions like a merge operation. To make this task easier, put all the
assemblies or binaries that you want to visualize in the same folder so that the full path of each assembly or
binary is the same for each map that you want to merge.
Alternatively, you can drag those assemblies or binaries to the same map from that folder.

See Also
Map dependencies across your solutions
Use code maps to debug your applications
Find potential problems using code map analyzers
Customize code maps by editing the DGML files
Directed Graph Markup Language (DGML) reference
Customize code maps by editing the DGML files
10/18/2017 12 min to read Edit Online

To customize a code map, you can edit a map's Directed Graph Markup Language (.dgml) file. For example, you
can edit elements to specify custom styles, assign properties and categories to code elements and links, or link
documents or URLs to code elements or to links. For more information about DGML elements, see Directed
Graph Markup Language (DGML) reference.
Edit the code map's .dgml file in a text or XML editor. If the map is part of your Visual Studio solution, select it in
Solution Explorer, open the shortcut menu, and choose Open With, XML (Text) Editor.

NOTE
To create code maps, you must have Visual Studio Enterprise. When you edit a code map in Visual Studio, it cleans up any
unused DGML elements and attributes by deleting them when you save the .dgml file. It also creates code elements
automatically when you manually add new links. When you save the .dgml file, any attributes that you added to an
element might rearrange themselves in alphabetical order.

Group code elements


You can add new groups or convert existing nodes into a group.
1. Open the .dgml file in a text or XML editor.
2. To convert a code element to a group, find the <Node/> element for that code element.
- or -
To add a new group, find the <Nodes> section. Add a new <Node/> element.
3. In the <Node/> element, add a Group attribute to specify whether the group appears expanded or
collapsed. For example:

<Nodes>
<Node Id="MyFirstGroup" Group="Expanded" />
<Node Id="MySecondGroup" Group="Collapsed" />
</Nodes>

4. In the <Links> section, make sure that a <Link/> element that has the following attributes exist for each
relationship between a group code element and its child code elements:
A Source attribute that specifies the group code element
A Target attribute that specifies the child code element
A Category attribute that specifies a Contains relationship between the group code element and
its child code element
For example:
<Links>
<Link Category="Contains" Source="MyFirstNewGroup" Target="FirstGroupChildOne" />
<Link Category ="Contains" Source="MyFirstNewGroup" Target="FirstGroupChildTwo" />
<Link Category ="Contains" Source="MySecondNewGroup" Target="SecondGroupChildOne" />
<Link Category="Contains" Source="MySecondNewGroup" Target="SecondGroupChildTwo" />
</Links>

For more information about the Category attribute, see Assign categories to code elements and links.

Change the style of the map


You can change the background color and border color of the map by editing the map's .dgml file. To change the
style of code elements and links, see Change the style of code elements and links.
1. Open the .dgml file in a text or XML editor.
2. In the <DirectedGraph> element, add any of the following attributes to change its style:
Background color

Background="ColorNameOrHexadecimalValue"

Border color

Stroke="StrokeValue"

For example:

<DirectedGraph Background="Green" xmlns="http://schemas.microsoft.com/vs/2009/dgml" >


...
...
</DirectedGraph>

Change the style of code elements and links


You can apply custom styles to the following code elements:
Single code elements and links
Groups of code elements and links
Groups of code elements and links based on certain conditions

TIP
If you have repeating styles across many code elements or links, you might consider applying a category to those code
elements or links, and then applying a style to that category. For more information, see Assign Categories to Code
elements and Links and Assign Properties to Code elements and Links.

To a p p l y a c u st o m st y l e t o a si n g l e c o d e e l e m e n t

1. Open the .dgml file in a text or XML editor.


2. Find the code element's <Node/> element. Add any of these attributes to customize its style:
Background color
Background="ColorNameOrHexadecimalValue"

Outline

Stroke="ColorNameOrHexadecimalValue"

Outline thickness

StrokeThickness="StrokeValue"

Text color

Foreground="ColorNameOrHexadecimalValue"

Icon

Icon="IconFilePathLocation"

Text size

FontSize="FontSizeValue"

Text type

FontFamily="FontFamilyName"

Text weight

FontWeight="FontWeightValue"

Text style

FontStyle="FontStyleName"

For example, you can specify Italic as the text style.


Texture

Style="Glass"

or -

Style="Plain"

Shape
To replace the shape with an icon, set the Shape property to None and set the Icon property to the path
with the icon file.
Shape="ShapeFilePathLocation"

For example:

<Nodes>
<Node Id="MyNode" Background="#FF008000" Stroke="#FF000000"
Foreground="#FFFFFFFF" Icon="...\Icons\Globe.png"/>
</Nodes>

To a p p l y a c u st o m st y l e t o a si n g l e l i n k

1. Open the .dgml file in a text or XML editor.


2. Find the <Link/> element that contains both the names of the source code element and target code
element.
3. In the <Link/> element, add any of the following attributes to customize its style:
Outline and arrowhead color

Stroke="ColorNameOrHexadecimalValue"

Outline thickness

StrokeThickness="StrokeValue"

Outline style

StrokeDashArray="StrokeArrayValues"

For example:

<Links>
<Link Source="MyFirstNode" Target="MySecondNode" Background="Green" Stroke="#FF000000"
StrokeDashArray="2,2"/>
</Links>

To a p p l y c u st o m st y l e s t o a g r o u p o f c o d e e l e m e n t s o r l i n k s

1. Open the .dgml file in a text or XML editor.


2. If a <Styles></Styles> element does not exist, add one under the <DirectedGraph></DirectedGraph>
element after the <Links></Links> element.
3. In the <Styles></Styles> element, under the <Style/> element and specify the following attributes:
TargetType="Node | Link | Graph"

GroupLabel=" NameInLegendBox "

ValueLabel=" NameInStylePickerBox "

To apply a custom style to all target types, do not use a condition.


To a p p l y a c o n d i t i o n a l st y l e t o g r o u p s o f c o d e e l e m e n t s o r l i n k s

1. Open the .dgml file in a text or XML editor.


2. In the <Style/> element, add a <Condition/> element that contains an Expression attribute to specify an
expression that returns a Boolean value.
For example:

<Condition Expression="MyCategory"/>

or -

<Condition Expression="MyCategory > 100"/>

or -

<Condition Expression="HasCategory('MyCategory')"/>

This expression uses the following Backus-Naur Form (BNF) syntax:


::= | | "("")" | | |
::=
::= "!" | "+" | "-"
::= "<" | "<=" | "=" | ">=" | ">" | "!=" | "or" | "and" | "+" | "*" | "/" | "-"
::= | "."
::= |
::= "(" ")"
::= Identifier
::= | "," |
::= [^. ]*
::= single or double-quoted string literal
::= string of digits with optional decimal point
You can specify multiple <Condition/> elements, which must all be true to apply the style.
3. On the next line after the <Condition/> element, add one or multiple <Setter/> elements to specify a
Property attribute and a fixed Value attribute or a computed Expression attribute to apply to the map,
code elements, or links that meet the condition.
For example:

<Setter Property="BackGround" Value="Green"/>

As a simple complete example, the following condition specifies that a code element appears green or red
based on whether its Passed category is set to True or False :
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="MyFirstNode" Passed="True" />
<Node Id="MySecondNode" Passed="False" />
</Nodes>
<Links>
</Links>
<Styles>
<Style TargetType="Node" GroupLabel="Passed" ValueLabel="True">
<Condition Expression="Passed='True'"/>
<Setter Property="Background" Value="Green"/>
</Style>
<Style TargetType="Node" GroupLabel="Passed" ValueLabel="False">
<Condition Expression="Passed='False'"/>
<Setter Property="Background" Value="Red"/>
</Style>
</Styles>
</DirectedGraph>

The following table includes some example conditions that you can use:
Set the font size as a function of the number of lines of code, which also changes the size of the code element.
This example uses a single conditional expression to set multiple properties, FontSize and FontFamily .

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Class1" LinesOfCode ="200" />
<Node Id="Class2" LinesOfCode ="1000" />
<Node Id="Class3" LinesOfCode ="20" />
</Nodes>
<Properties>
<Property Id="LinesOfCode" Label="LinesOfCode" Description="LinesOfCode" DataType="System.Int32" />
</Properties>
<Styles>
<Style TargetType="Node" GroupLabel="LinesOfCode" ValueLabel="Function">
<Condition Expression="LinesOfCode > 0" />
<Setter Property="FontSize" Expression="Math.Max(9,Math.Sqrt(LinesOfCode))" />
<Setter Property="FontFamily" Value="Papyrus" />
</Style>
</Styles>
</DirectedGraph>

Set the background color of a code element based on the Coverage property. The styles are evaluated in the
order that they appear, similar to if-else statements.
In this example:
1. If Coverage is > 80, then set the Background property to green.
2. Else if Coverage is > 50, then set the Background property to a shade of orange based on the value of the
Coverage property.
3. Else set the Background property to a shade of red based on the value of the Coverage property.
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Class1" Coverage="58" />
<Node Id="Class2" Coverage="95" />
<Node Id="Class3" Coverage="32" />
</Nodes>
<Properties>
<Property Id="Coverage" Label="Coverage" Description="Code coverage as a percentage of blocks"
DataType="Double" />
</Properties>
<Styles>
<Style TargetType="Node" GroupLabel="Coverage" ValueLabel="Good">
<Condition Expression="Coverage > 80" />
<Setter Property="Background" Value="Green" />
</Style>
<Style TargetType="Node" GroupLabel="Coverage" ValueLabel="OK">
<Condition Expression="Coverage > 50" />
<Setter Property="Background" Expression="Color.FromRgb(180 * Math.Max(1, (80 - Coverage) / 30), 180,
0)" />
</Style>
<Style TargetType="Node" GroupLabel="Coverage" ValueLabel="Bad">
<Setter Property="Background" Expression="Color.FromRgb(180, 180 * Coverage / 50, 0)" />
</Style>
</Styles>
</DirectedGraph>

Set the Shape property to None so that the icon replaces the shape. Use the Icon property to specify the
location of the icon.

<DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Automation" Category="Test" Label="Automation" />
<Node Id="C# Provider" Category="Provider" Label="C# Provider" />
</Nodes>
<Categories>
<Category Id="Provider" Icon="...\Icons\Module.png" Shape="None" />
<Category Id="Test" Icon="...\Icons\Page.png" Shape="None" />
</Categories>
<Properties>
<Property Id="Icon" DataType="System.String" />
<Property Id="Label" Label="Label" Description="Displayable label of an Annotatable object"
DataType="System.String" />
<Property Id="Shape" DataType="System.String" />
</Properties>
<Styles>
<Style TargetType="Node" GroupLabel="Group" ValueLabel="Has category">
<Condition Expression="HasCategory('Group')" />
<Setter Property="Background" Value="#80008080" />
</Style>
<Style TargetType="Node">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</Styles>
</DirectedGraph>

Assign properties to code elements and links


You can organize code elements and links by assigning properties to them. For example, you can select code
elements that have specific properties so that you can group them, change their style, or hide them.
To assign a property to a code element
1. Open the .dgml file in a text or XML editor.
2. Find the <Node/> element for that code element. Specify the name of the property and its value. For
example:

<Nodes>
<Node Id="MyNode" MyPropertyName="PropertyValue" />
</Nodes>

3. Add a <Property/> element to the <Properties> section to specify attributes such as its visible name and
data type:

<Properties>
<Property Id="MyPropertyName" Label="My Property" DataType="System.DataType"/>
</Properties>

To assign a property to a link


1. Open the .dgml file in a text or XML editor.
2. Find the <Link/> element that contains both the names of the source code element and target code
element.
3. In the <Node/> element, specify the name of the property and its value. For example:

<Links>
<Link Source="MyFirstNode" Target="MySecondNode" MyPropertyName="PropertyValue" />
</Links>

4. Add a <Property/> element to the <Properties> section to specify attributes such as its visible name and
data type:

<Properties>
<Property Id="MyPropertyName" Label="My Property Name" DataType="System.DataType"/>
</Properties>

Assign categories to code elements and links


The following sections demonstrate how you can organize code elements by assigning categories to them, and
how you can create hierarchical categories that help you organize code elements and add attributes to child
categories by using inheritance.
To assign a category to a code element
Open the .dgml file in a text or XML editor.
Find the <Node/> element for the code element that you want.
In the <Node/> element, add a Category attribute to specify the name of the category. For example:

<Nodes>
<Node Id="MyNode" Category="MyCategory" />
</Nodes>

Add a <Category/> element to the <Categories> section so that you can use the Label attribute to
specify the display text for that category:
<Categories>
<Category Id="MyCategory" Label="My Category" />
</Categories>

To assign a category to a link


1. Open the .dgml file in a text or XML editor.
2. Find the <Link/> element that contains both the names of the source code element and target code
element.
3. In the <Link/> element, add a Category attribute to specify the name of the category. For example:

<Links>
<Link Source="MyFirstNode" Target="MySecondNode" Category="MyCategory"
</Links>

4. Add a <Category/> element to the <Categories> section so that you can use the Label attribute to
specify the display text for that category:

<Categories>
<Category Id="MyCategory" Label="My Category" />
</Categories>

To create hierarchical categories


1. Open the .dgml file in a text or XML editor.
2. Add a <Category/> element for the parent category, and then add the BasedOn attribute to the child
category's <Category/> element.
For example:

<Nodes>
<Node Id="MyFirstNode" Label="My First Node" Category= "MyCategory" />
<Node Id="MySecondNode" Label="My Second Node" />
</Nodes>
<Links>
<Link Source="MyFirstNode" Target="MySecondNode" />
</Links>
<Categories>
<Category Id="MyCategory" Label="My Category" BasedOn="MyParentCategory"/>
<Category Id="MyParentCategory" Label="My Parent Category" Background="Green"/>
</Categories>

In this example, the background of MyFirstNode is green because its Category attribute inherits the
Background attribute of MyParentCategory .

Link documents or URLs to code elements and links


You can link documents or URLs to code elements or to links by editing the map's .dgml file and adding a
Reference attribute to the <Node/> element for a code element or the <Link/> element for a link. You can then
open and view that content from the code element or link. The Reference attribute specifies the path of that
content. This can be a path relative to the location of the .dgml file or an absolute path.
Cau t i on

If you use relative paths, and the .dgml file is moved to a different location, then those paths will no longer
resolve. When you try to open and view the linked content, an error stating that the content cannot be viewed
will occur.
For example, you might want to link the following code elements:
To describe the changes to a class, you might link the URL of a work code element, document, or another
.dgml file to the code element for a class.
You might link a dependency diagram to a group code element that represents a layer in the software's
logical architecture.
To show more information about a component that exposes an interface, you might link a component
diagram to the code element for that interface.
Link a code element to a Team Foundation Server work item or bug, or some other information that is
related to the code element.
To link a document or URL to a code element
1. Open the .dgml file in a text or XML editor.
2. Find the <Node/> element for the code element that you want.
3. Perform one of the tasks in the following table:
A single code element
In the <Node/> or <Link/> element, add a Reference attribute to specify the location of the code
element.

NOTE
You can have only one Reference attribute per element.

For example:

<Nodes>
<Node Id="MyNode" Reference="MyDocument.txt" />
</Nodes>
<Properties>
<Property Id="Reference" Label="My Document" DataType="System.String" IsReference="True" />
</Properties>

Multiple code elements


a. In the <Node/> or <Link/> element, add a new attribute to specify the location of each reference.
b. In the <Properties> section:
a. Add a <Property/> element for each new type of reference.
b. Set the Id attribute to the name of the new reference attribute.
c. Add the IsReference attribute and set it to True to make the reference appear on the code
element's Go To Reference shortcut menu.
d. Use the Label attribute to specify the display text on the code element's Go To Reference
shortcut menu.
For example:
<Nodes>
<Node Id="MyNode" SequenceDiagram="MySequenceDiagram.sequencediagram"
ActiveBugs="MyActiveBugs.wiq"/>
</Nodes>
<Properties>
<Property Id="SequenceDiagram" Label="My Sequence Diagram" DataType="System.String"
IsReference="True" />
<Property Id="ActiveBugs" Label="Active Bugs" DataType="System.String" IsReference="True" />
</Properties>

On the map, the name of the code element appears underlined. When you open the shortcut menu for the
code element or the link, you will see a Go To Reference shortcut menu that contains the linked code
elements for you to choose.
4. Use the ReferenceTemplate attribute to specify a common string, such as a URL, that is used by multiple
references instead of repeating that string in the reference.
The ReferenceTemplate attribute specifies a placeholder for the value of the reference. In the following
example, the {0} placeholder in the ReferenceTemplate attribute will be replaced by the values of the
MyFirstReference and MySecondReference attributes in the <Node/> element to produce a full path:

<Nodes>
<Node Id="MyNode" MyFirstReference="MyFirstDocument" MySecondReference="MySecondDocument"/>
<Node Id="MySecondNode" MyFirstReference="AnotherFirstDocument"
MySecondReference="AnotherSecondDocument"/>
</Nodes>
<Properties>
<Property Id="MyFirstReference" Label="My First Document" DataType="System.String"
IsReference="True" ReferenceTemplate="http://www.Fabrikam.com/FirstDocuments/{0}.asp"/>
<Property Id="MySecondReference" Label="My Second Document" DataType="System.String"
IsReference="True" ReferenceTemplate=" http://www.Fabrikam.com/SecondDocuments/{0}.asp"/>
</Properties>

5. To view the referenced code element or code elements from the map, open the shortcut menu for the
code element or the link. Choose Go To Reference and then the code element.

See Also
Map dependencies across your solutions
Use code maps to debug your applications
Find potential problems using code map analyzers
Browse and rearrange code maps
Directed Graph Markup Language (DGML) reference
Directed Graph Markup Language (DGML) reference
10/18/2017 7 min to read Edit Online

Directed Graph Markup Language (DGML) describes information used for visualization and to perform complexity
analysis, and is the format used to persist code maps in Visual Studio. It uses simple XML to describe both cyclical
and acyclic directed graphs. A directed graph is a set of nodes that are connected by links, or edges. Nodes and
links can be used represent network structures, such as elements in a software project.
Note that some versions of Visual Studio support only a subset of DGML capabilities, see Version support for
architecture and modeling tools.

NOTE
When you edit a .dgml file, IntelliSense helps you identify attributes that are available for each element and their values. To
specify color in an attribute, use names for common colors, such as "Blue", or ARGB hexadecimal values, such as "#ffa0b1c3".
DGML uses a small subset of Windows Presentation Foundation (WPF) color definition formats. For more information, see
Colors Class.

DGML syntax
The following table describes kinds of elements that are used in DGML:
<DirectedGraph></DirectedGraph>

This element is the root element of code map (.dgml) document. All other DGML elements appear within
the scope of this element.
The following list describes optional attributes that you can include:
Background - The color of the map background
BackgroundImage - The location of an image file to use as the map background.
GraphDirection - When the map is set to tree layout ( Sugiyama ), arrange the nodes so that most of the
links flow in the specified direction: TopToBottom , BottomToTop , LeftToRight , or RightToLeft . See Change
the map layout.
Layout - Set the map to the following layouts: None , Sugiyama (tree layout), ForceDirected (quick
clusters), or DependencyMatrix . See Change the map layout.
NeighborhoodDistance - When the map is set to tree layout or quick clusters layout, show only those nodes
that are a specified number (1-7) of links away from selected nodes. See Change the map layout.
Example:
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph Title="DrivingTest" Background="Blue" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
...
</Nodes>
<Links>
...
</Links>
<Categories>
...
</Categories>
<Properties>
...
</Properties>
</DirectedGraph>

<Nodes></Nodes>

This optional element contains a list of <Node/> elements, which define nodes on the map. For more
information, see the <Node/> element.

NOTE
When you reference an undefined node in a <Link/> element, the map creates a <Node/> element automatically.

Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node ... />
</Nodes>
<Links>
<Link ... />
</Links>
</DirectedGraph>

<Node/>

This element defines a single node. It appears within the <Nodes><Nodes/> element list.
This element must include the following attributes:
Id - The unique name of the node and the default value of the Label attribute, if no separate Label
attribute is specified. This name must match the Source or Target attribute of the link that references it.
The following list describes some of the optional attributes that you can include:
Label - The display name of the node.
Style attributes. See Customize code maps by editing the DGML files.
Category - The name of a category that identifies elements that share this attribute. For more information,
see the <Category/> element.
Property - The name of a property that identifies elements that have the same property value. For more
information, see the <Property/> element.
Group - If the node contains other nodes, set this attribute to Expanded or Collapsed to show or hide its
contents. There must be a <Link/> element that includes the Category="Contains" attribute and specifies
the parent node as the source node and the child node as the target node. See Group code elements.
Visibility - Set this attribute to Visible , Hidden , or Collapsed . Uses System.Windows.Visibility . See
Hide or show nodes and links.
Reference - Set this attribute to link to a document or URL. See Link documents or URLs to code elements
and links.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Driver" Label="Student" Category="Person" />
<Node Id="Passenger" Label="Instructor" Category="Person" />
<Node Id="Car" Label="Car" Category="Automobile" />
<Node Id="Truck" Label="Truck" Category="Automobile" />
</Nodes>
<Links>
<Link ... />
</Links>
<Categories>
<Category Id="Person" Background="Orange" />
<Category Id="Automobile" Background="Yellow"/>
</Categories>
</DirectedGraph>

<Links></Links>

This element contains the list of <Link> elements, which define links between nodes. For more information,
see the <Link/> element.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Links>
<Link ... />
</Links>
</DirectedGraph>

<Link/>

This element defines a single link that connects a source node to a target node. It appears within the
<Links></Links> element list.

NOTE
If this element references an undefined node, the map document automatically creates a node that has the specified
attributes, if any.

This element must include the following attributes:


Source - The source node of the link
Target - The target node of the link
The following list describes some of the optional attributes that you can include:
Label - The display name of the link
Style attributes. See Customize code maps by editing the DGML files.
Category - The name of a category that identifies elements that share this attribute. For more information,
see the <Category/> element.
Property - The name of a property that identifies elements that have the same property value. For more
information, see the <Property/> element.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Driver" Label="Student" Category="Person" />
<Node Id="Passenger" Label="Instructor" Category="Person" />
<Node Id="Car" Label="Car" Category="Automobile" />
<Node Id="Truck" Label="Truck" Category="Automobile" />
</Nodes>
<Links>
<Category Id="Person" Background="Orange" />
<Category Id="Automobile" Background="Yellow"/>
<Link Source="Driver" Target="Car" Label="Passed" Stroke="Black" Background="Green"
Category="PassedTest" />
<Link Source="Driver" Target="Truck" Label="Failed" Stroke="Black" Background="Red"
Category="PassedTest" />
</Links>
</DirectedGraph>

<Categories></Categories>

This element contains the list of <Category/> elements. For more information, see the <Category/>
element.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Categories>
<Category ... />
</Categories>
</DirectedGraph>

<Category/>

This element defines a Category attribute, which is used to identify elements that share this attribute. A
Category attribute can be used to organize map elements, provide for shared attributes through
inheritance, or define additional metadata.
This element must include the following attributes:
Id - The unique name of the category and the default value of the Label attribute, if no separate Label
attribute is specified.
The following list describes some of the optional attributes that you can include:
Label - A reader-friendly name for the category.
BasedOn - The parent category from which the <Category/> of the current element inherits.
In the example for this element, the FailedTest category inherits its Stroke attribute from the PassedTest
category. See "To create hierarchical categories" in Customize code maps by editing the DGML files.
Categories also provide some basic template behavior that controls the appearance of nodes and links
when they are displayed on a map. See Customize code maps by editing the DGML files.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Driver" Label="Driver" Category="Person" />
<Node Id="Car" Label="Car" Category="Automobile" />
<Node Id="Truck" Label="Truck" Category="Automobile" />
<Node Id="Passenger" Category="Person" />
</Nodes>
<Links>
<Link Source="Driver" Target="Car" Label="Passed" Category="PassedTest" />
<Link Source="Driver" Target="Truck" Label="Failed" Category="FailedTest" />
</Links>
<Categories>
<Category Id="Person" Background="Orange" />
<Category Id="Automobile" Background="Yellow"/>
<Category Id="PassedTest" Label="Passed" Stroke="Black" Background="Green" />
<Category Id="FailedTest" Label="Failed" BasedOn="PassedTest" Background="Red" />
</Categories>
</DirectedGraph>

<Properties></Properties>

This element contains the list of <Property/> elements. For more information, see the <Property/>
element.
Example:

<?xml version="1.0" encoding="utf-8"?>


<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Properties>
<Property ... />
</Properties>
</DirectedGraph>

<Property/>

This element defines a Property attribute that you can use to assign a value to any DGML element or
attribute, including categories and other properties.
This element must include the following attributes:
Id - The unique name of the property and the default value of the Label attribute, if no separate
Label attribute is specified.

DataType - The type of data stored by the property


If you want the property to appear in the Properties window, use the Label property to specify the
property's display name.
See Assign categories to code elements and links.
Example:
<?xml version="1.0" encoding="utf-8"?>
<DirectedGraph Title="DrivingTest" xmlns="http://schemas.microsoft.com/vs/2009/dgml">
<Nodes>
<Node Id="Driver" Label="Driver" Category="Person" DrivingAge="18"/>
<Node Id="Car" Label="Car" Category="Automobile" />
<Node Id="Truck" Label="Truck" Category="Automobile" />
<Node Id="Passenger" Category="Person" />
</Nodes>
<Links>
<Link Source="Driver" Target="Car" Label="Passed" Category="PassedTest" />
<Link Source="Driver" Target="Truck" Label="Failed" Category="FailedTest" />
</Links>
<Categories>
<Category Id="Person" Background="Orange" />
<Category Id="Automobile" Background="Yellow"/>
<Category Id="PassedTest" Label="Passed" Stroke="Black" Background="Green" />
<Category Id="FailedTest" Label="Failed" BasedOn="PassedTest" Background="Red" />
</Categories>
<Properties>
<Property Id="DrivingAge" Label="Driving Age" DataType="System.Int32" />
</Properties>
</DirectedGraph>

Aliases for commonly-used paths


Replacing commonly-used paths with aliases helps reduce the size of the .dgml file and the time required to load
or save the file. To create an alias, add a <Paths></Paths> section at the end of the .dgml file. In this section, add a
<Path/> element to define an alias for the path:

<Paths>
<Path Id="MyPathAlias" Value="C:\...\..." />
</Paths>

To reference the alias from an element in the .dgml file, enclose the Id of the <Path/> element with a dollar sign
($) and parentheses (()):

<Nodes>
<Node Id="MyNode" Reference="$(MyPathAlias)MyDocument.txt" />
</Nodes>
<Properties>
<Property Id="Reference" Label="My Document" DataType="System.String" IsReference="True" />
</Properties>

See Also
Map dependencies across your solutions
Use code maps to debug your applications
Find potential problems using code map analyzers
Create dependency diagrams from your code
10/18/2017 9 min to read Edit Online

To visualize your software system's high-level, logical architecture, create a dependency diagram in Visual
Studio. To make sure that your code stays consistent with this design, validate your code with a dependency
diagram. You can create dependency diagrams for Visual C# .NET and Visual Basic .NET projects. To see which
versions of Visual Studio support this feature, see Version support for architecture and modeling tools

A dependency diagram lets you organize Visual Studio solution items into logical, abstract groups called layers.
You can use layers to describe major tasks that these artifacts perform or the system's major components. Each
layer can contain other layers that describe more detailed tasks. You can also specify the intended or existing
dependencies between layers. These dependencies, which are represented as arrows, show which layers can use
or currently use the functionality represented by other layers. To maintain architectural control of the code, show
the intended dependencies on the diagram and then validate the code against the diagram.
Video: Validate your architecture dependencies in real time

Create a dependency diagram


Before you create a dependency diagram, make sure your solution has a modeling project.

IMPORTANT
Don't add, drag, or copy an existing dependency diagram from a modeling project to another modeling project or to
another place in the solution. This preserves the references from the original diagram, even if you change the diagram.
This also prevents layer validation from working correctly and might cause other issues, such as missing elements or other
errors when you try to open the diagram.
Instead, add a new dependency diagram to the modeling project. Copy the elements from the source diagram to the new
diagram. Save both the modeling project and the new dependency diagram.

To add a new dependency diagram to a modeling project


1. On the Architecture menu, choose New Dependency Diagram.
2. Under Templates, choose dependency diagram.
3. Name the diagram.
4. In Add to Modeling Project, browse to and select an existing modeling project in your solution.
-or-
Choose Create a new modeling project to add a new modeling project to the solution.

NOTE
The dependency diagram must exist inside a modeling project. However, you can link it to items anywhere in the
solution.

5. Make sure to save both the modeling project and the dependency diagram.

Drag and drop, or copy and paste, from a Code Map


1. Generate a Code Map for the solution using the Architecture menu.
2. Consider applying a Code Map filter to remove solution folders and "Test Assets" if you only want to
enforce dependencies in product code.
3. On the generated Code Map, remove the "External" node, or expand it to show external assemblies,
depending on whether you want to enforce namespace dependencies, and delete non-required
assemblies from the Code Map.
4. Create a new Dependency Diagram for the solution using the Architecture menu
5. Select all the nodes on the Code Map (use Ctrl + A, or use the rubber band selection by pressing the Shift
key before you click, drag, and release.
6. Drag and drop, or a copy and paste, the selected elements to the new Dependency Validation diagram.
7. This shows the current app architecture. Decide what you want the architecture to be and modify the
dependency diagram accordingly.

Create layers from artifacts


You can create layers from Visual Studio solution items, such as projects, code files, namespaces, classes, and
methods. This automatically creates links between layers and items, including them in the layer validation
process.
You can also link layers to items that don't support validation, such as Word documents or PowerPoint
presentations, so that you can associate a layer with specifications or plans. You can also link layers to files in
projects that are shared across multiple apps, but the validation process won't include those layers, which appear
with generic names such as "Layer 1" and "Layer 2".
To see if a linked item supports validation, open Layer Explorer and examine the Supports Validation
property of the item. See Managing links to artifacts.

TO FOLLOW THESE STEPS

Create a layer for a single artifact 1. Drag the item onto the dependency diagram from
these sources:

Solution Explorer

For example, you can drag files or projects.


Code maps

See Map dependencies across your solutions


and Use code maps to debug your
applications.
Class View or Object Browser

A layer appears on the diagram and is linked to the


artifact.
2. Rename the layer to reflect the responsibilities of the
associated code or artifacts.
Important: Dragging binary files to the dependency diagram
does not automatically add their references to modeling
project. You must manually add the binary files that you
want to validate to the modeling project. To add binary
files to the modeling project
1. In Solution Explorer, open the shortcut menu for
the modeling project, and then choose Add Existing
Item.
2. In the Add Existing Item dialog box, browse to the
binary files, select them, and then choose OK. The
binary files appear in the modeling project.
3. In Solution Explorer, choose a binary file that you
added, and then press F4 to open the Properties
window.
4. On each binary file, set the Build Action property to
Validate.

Create a single layer for all selected artifacts Drag all the artifacts to the dependency diagram at the same
time.

A layer appears on the diagram and is linked to all of the


artifacts.
TO FOLLOW THESE STEPS

Create a layer for each selected artifact Press and hold the SHIFT key while you drag all of the
artifacts to the dependency diagram at the same time. Note:
If you use the SHIFT key to select a range of items, release
the key after you select the artifacts. Press and hold it again
when you drag the artifacts to the diagram.

A layer for each artifact appears on the diagram and is linked


to each artifact.

Add an artifact to a layer Drag the artifact to the layer.

Create a new unlinked layer In the Toolbox, expand the Dependency Diagram section,
and then drag a Layer to the dependency diagram.

To add multiple layers, double-click the tool. When you are


finished, choose the Pointer tool or press the ESC key.

- or -

Open the shortcut menu for the dependency diagram,


choose Add, and then choose Layer.

Create nested layers Drag an existing layer onto another layer.

- or -

Open the shortcut menu for a layer, choose Add, and then
choose Layer.

Create a new layer that contains two or more existing layers Select the layers, open the shortcut menu for your selection,
and then choose Group.

Change the color of a layer Set its Color property to the color that you want.

Specify that artifacts associated with a layer must not belong Type the namespaces in the layer's Forbidden Namespaces
to the specified namespaces property. Use a semicolon (;) to separate the namespaces.

Specify that artifacts associated with a layer cannot depend Type the namespaces in the layer's Forbidden Namespace
on the specified namespaces Dependencies property. Use a semicolon (;) to separate the
namespaces.

Specify that artifacts associated with a layer must belong to Type the namespace in the layer's Required Namespaces
one of the specified namespaces property. Use a semicolon (;) to separate the namespaces.

The number on a layer indicates the number of artifacts that are linked to the layer. However, when reading this
number, remember the following:
If a layer links to an artifact that contains other artifacts, but the layer does not link directly to the other
artifacts, then the number includes only the linked artifact. However, the other artifacts are included for
analysis during layer validation.
For example, if a layer is linked to a single namespace, then the number of linked artifacts is 1, even if the
namespace contains classes. If the layer also has links to each class in the namespace, then the number
will include the linked classes.
If a layer contains other layers that are linked to artifacts, then the container layer is also linked to those
artifacts, even though the number on the container layer does not include those artifacts.
Manage links between layers and artifacts
1. On the dependency diagram, open the shortcut menu for the layer, and then choose View Links.
Layer Explorer shows the artifact links for the selected layer.
2. Use the following tasks to manage these links:

TO IN LAYER EXPLORER

Delete the link between the layer and an artifact Open the shortcut menu for the artifact link, and then
choose Delete.

Move the link from one layer to another Drag the artifact link to an existing layer on the diagram.

- or -

1. Open the shortcut menu for the artifact link, and then
choose Cut.
2. On the dependency diagram, open the shortcut menu for
the layer, and then choose Paste.

Copy the link from one layer to another 1. Open the shortcut menu for the artifact link, and then
choose Copy.
2. On the dependency diagram, open the shortcut menu for
the layer, and then choose Paste.

Create a new layer from an existing artifact link Drag the artifact link to a blank area on the diagram.

Verify that a linked artifact supports validation against the Look at the Supports Validation column for the artifact link.
dependency diagram.

Reverse-engineer existing dependencies


A dependency exists wherever an artifact that is associated with one layer has a reference to an artifact that is
associated with another layer. For example, a class in one layer declares a variable that has a class in another
layer. You can reverse-engineer existing dependencies for artifacts that are linked to layers on the diagram.

NOTE
Dependencies cannot be reverse-engineered for certain kinds of artifacts. For example, no dependencies will be reverse-
engineered from or to a layer that is linked to a text file. To see which artifacts have dependencies that you can reverse-
engineer, open the shortcut menu for one or multiple layers, and then choose View Links. In Layer Explorer, examine
the Supports Validation column. Dependencies will not be reverse-engineered for artifacts for which this column shows
False.

Select one or multiple layers, open the shortcut menu for a selected layer, and then choose Generate
Dependencies.
Typically, you will see some dependencies that should not exist. You can edit these dependencies to align
them with the intended design.

Edit layers and dependencies to show the intended design


To describe the changes that you plan to make to your system or the intended architecture, edit the dependency
diagram:

TO PERFORM THESE STEPS

Change or restrict the direction of a dependency Set its Direction property.

Create new dependencies Use the Dependency and Bidirectional Dependency tools.

To draw multiple dependencies, double-click the tool. When


you are finished, choose the Pointer tool or press the ESC
key.

Specify that artifacts associated with a layer cannot depend Type the namespaces in the layer's Forbidden Namespace
on the specified namespaces Dependencies property. Use a semicolon (;) to separate the
namespaces.

Specify that artifacts associated with a layer must not belong Type the namespaces in the layer's Forbidden Namespaces
to the specified namespaces property. Use a semicolon (;) to separate the namespaces.

Specify that artifacts associated with a layer must belong to Type the namespace in the layer's Required Namespaces
one of the specified namespaces property. Use a semicolon (;) to separate the namespaces.

Change how elements appear on the diagram


You can change the size, shape, color, and position of layers or the color of dependencies by editing their
properties.

Discover patterns and dependencies on a code map


While creating dependency diagrams, you might also create code maps. These diagrams can help you discover
patterns and dependencies while you explore the code. Use Solution Explorer, Class View, or Object Browser to
explore assemblies, namespaces, and classes - which often correspond well to existing layers. For more
information about code maps, see:
Map dependencies across your solutions
Use code maps to debug your applications
Find potential problems using code map analyzers

See Also
Video: Validate your architecture dependencies in real time
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Validate code with dependency diagrams
Visualize code
Create models for your app
10/18/2017 1 min to read Edit Online

Modeling diagrams help you understand, clarify, and communicate ideas about your code and the user
requirements that your software system must support.
To see which versions of Visual Studio support each type of diagram, see Version support for architecture and
modeling tools.
To visualize the architecture of a system or existing code, create the following diagrams:

DIAGRAM SHOWS

Dependency Diagrams: Guidelines High-level architecture of the system

Dependency Diagrams: Reference

Code maps Dependencies and other relationships in existing code

Map dependencies across your solutions

Find potential problems using code map analyzers

Code-generated class diagrams Types and their relationships in .NET code

Working with Class Diagrams (Class Designer)

Related Tasks
TOPIC TASK

Visualize code Create code maps and dependency diagrams to better


understand unfamiliar code.

Model user requirements Use models to clarify and communicate the users' needs.

Model your app's architecture Use models to describe the overall structure and behavior of
your system and to make sure that it meets the users' needs.

Validate your system during development Make sure that your software stays consistent with your
users' needs and the overall architecture of your system.

Use models in your development process Use models to help you understand and change your system
during its development.
Use models in Agile development

Structure your modeling solution Organize models in a large or medium project.

External Resources
CATEGORY LINKS

Forums - Visual Studio Visualization & Modeling Tools


- Visual Studio Visualization & Modeling SDK (DSL Tools)
Dependency Diagrams: Reference
10/18/2017 3 min to read Edit Online

In Visual Studio, you can use a dependency diagram to visualize the high-level, logical architecture of your
system. A dependency diagram organizes the physical artifacts in your system into logical, abstract groups called
layers. These layers describe major tasks that the artifacts perform or the major components of your system.
Each layer can also contain nested layers that describe more detailed tasks.
To see which versions of Visual Studio support this feature, see Version support for architecture and modeling
tools.
You can specify the intended or existing dependencies between layers. These dependencies, which are
represented as arrows, indicate which layers can use or currently use the functionality represented by other
layers. By organizing your system into layers that describe distinct roles and functions, a dependency diagram
can help make it easier for you to understand, reuse, and maintain your code.
Use a dependency diagram to help you perform the following tasks:
Communicate the existing or intended logical architecture of your system.
Discover conflicts between your existing code and the intended architecture.
Visualize the impact of changes on the intended architecture when you refactor, update, or evolve your
system.
Reinforce the intended architecture during the development and maintenance of your code by including
validation with your check-in and build operations.
This topic describes the elements that you can use on a dependency diagram. For more detailed
information about how to create and draw dependency diagrams, see Dependency Diagrams: Guidelines.
For more information about layering patterns, visit the Patterns & Practices site.

Reading dependency diagrams

The following table describes the elements that you can use on a dependency diagram.

SHAPE ELEMENT DESCRIPTION


SHAPE ELEMENT DESCRIPTION

1 Layer A logical group of physical artifacts in


your system. These artifacts can be
namespaces, projects, classes, methods,
and so on.

To see the artifacts that are linked to a


layer, open the shortcut menu for the
layer, and then choose View Links to
open Layer Explorer.

For more information, see Layer


Explorer.

- Forbidden Namespace
Dependencies - Specifies that artifacts
associated with this layer cannot
depend on the specified namespaces.
- Forbidden Namespaces - Specifies
that artifacts associated with this layer
must not belong to the specified
namespaces.
- Required Namespaces - Specifies
that artifacts associated with this layer
must belong to one of the specified
namespaces.

2 Dependency Indicates that one layer can use the


functionality in another layer, but not
vice versa.

- Direction - Specifies the direction of


the dependency.

3 Bidirectional Dependency Indicates that one layer can use the


functionality in another layer, and vice
versa.

- Direction - Specifies the direction of


the dependency.

4 Comment Use to add general notes to the


diagram or elements on the diagram.

5 Comment Link Use to link comments to elements on


the diagram.

Layer Explorer
You can link each layer to artifacts in your solution, such as projects, classes, namespaces, project files, and other
parts of your software. The number on a layer shows the number of artifacts that are linked to the layer.
However, when reading the number of artifacts on a layer, remember the following:
If a layer links to an artifact that contains other artifacts, but the layer does not link directly to the other
artifacts, then the number includes only the linked artifact. However, the other artifacts are included for
analysis during layer validation.
For example, if a layer is linked to a single namespace, then the number of linked artifacts is 1, even if the
namespace contains classes. If the layer also has links to each class in the namespace, then the number
will include the linked classes.
If a layer contains other layers that are linked to artifacts, then the container layer is also linked to those
artifacts, even though the number on the container layer does not include those artifacts.
For more information about linking layers and artifacts, see:
Dependency diagrams: Guidelines
Create dependency diagrams from your code
To examine the linked artifacts
On the dependency diagram, open the shortcut menu for one or more layers, and then choose View
Links.
Layer Explorer opens and shows the artifacts that are linked to the selected layers. Layer Explorer has a
column that shows each of the properties of the artifact links.

NOTE
If you cannot see all of these properties, expand the Layer Explorer window.

COLUMN IN LAYER EXPLORER DESCRIPTION

Categories The kind of artifact, such as a class, namespace, source


file, and so on

Layer The layer that links to the artifact

Supports Validation If True, then the layer validation process can verify that
the project conforms to dependencies to or from this
element.

If False, then the link does not participate in the layer


validation process.

For more information, see Dependency Diagrams:


Guidelines.

Identifier The reference to the linked artifact

See Also
Create models for your app
Dependency Diagrams: Guidelines
11/7/2017 10 min to read Edit Online

Describe your app's architecture at a high level by creating dependency diagrams in Visual Studio. Make sure
that your code stays consistent with this design by validating your code with a dependency diagram. You can
also include layer validation in your build process. See Channel 9 Video: Design and validate your architecture
using dependency diagrams.
To see which versions of Visual Studio support this feature, see Version support for architecture and modeling
tools.

What is a dependency diagram?


Like a traditional architecture diagram, a dependency diagram identifies the major components or functional
units of the design and their interdependencies. Each node on the diagram, called a layer, represents a logical
group of namespaces, projects, or other artifacts. You can draw the dependencies that should exist in your
design. Unlike a traditional architecture diagram, you can verify that the actual dependencies in the source code
conform to the intended dependencies that you have specified. By making validation part of a regular build on
Team Foundation Server, you can ensure that the program code continues to adhere to the system's
architecture through future changes. See Dependency Diagrams: Reference.

How to design or update your app with dependency diagrams


The following steps provide an overview of how to use dependency diagrams within the development process.
Later sections in this topic describe more detail about each step. If you are developing a new design, omit the
steps that refer to existing code.

NOTE
These steps appear in approximate order. You will probably want to overlap the tasks, reorder them to suit your own
situation, and revisit them at the start of each iteration in your project.

1. Create a dependency diagram for the whole application, or for a layer within it.
2. Define layers to represent primary functional areas or components of your application. Name these
layers according to their function, for example, "Presentation" or "Services". If you have a Visual Studio
solution, you can associate each layer with a collection of artifacts, such as projects, namespaces, files,
and so on.
3. Discover the existing dependencies between layers.
4. Edit the layers and dependencies to show the updated design that you want the code to reflect.
5. Design new areas of your application by creating layers to represent the principal architectural blocks or
components and defining dependencies to show how each layer uses the others.
6. Edit the layout and appearance of the diagram to help you discuss it with colleagues.
7. Validate the code against the dependency diagram to highlight the conflicts between the code and the
architecture you require.
8. Update the code to conform to your new architecture. Iteratively develop and refactor code until the
validation shows no conflicts.
9. Include layer validation in the build process to ensure that the code continues to adhere to your design.

Create a dependency diagram


A dependency diagram must be created inside a modeling project. You can add a new dependency diagram to
an existing modeling project, create a new modeling project for the dependency diagram, or copy an existing
dependency diagram within the same modeling project.

IMPORTANT
Do not add, drag, or copy an existing dependency diagram from a modeling project to another modeling project or to
another location in the solution. A dependency diagram that is copied in this way will have the same references as the
original diagram, even if you modify the diagram. This will prevent layer validation from working correctly and might cause
other issues, such as missing elements or other errors when trying to open the diagram.

See Create dependency diagrams from your code.

Define layers to represent functional areas or components


Layers represent logical groups of artifacts, such as projects, code files, namespaces, classes, and methods. You
can create layers from artifacts from Visual C# .NET and Visual Basic .NET projects, or you can attach
specifications or plans to a layer by linking documents, such as Word files or PowerPoint presentations. Each
layer appears as a rectangle on the diagram and shows the number of artifacts that are linked to it. A layer can
contain nested layers that describe more specific tasks.
As a general guideline, name layers according to their function, for example, "Presentation" or "Services". If the
artifacts are closely interdependent, place them in the same layer. If the artifacts can be updated separately or
used in separate applications, place them in different layers. To learn about layering patterns, visit the Patterns &
Practices site at http://go.microsoft.com/fwlink/?LinkId=145794.

TIP
There are certain types of artifacts that you can link to layers but that do not support validation against the dependency
diagram. To see whether the artifact supports validation, open Layer Explorer to examine the Supports Validation
property of the artifact link. See Discover existing dependencies between layers.

When updating an unfamiliar application, you might also create code maps. These diagrams can help you
discover patterns and dependencies while you explore the code. Use Solution Explorer to explore namespaces
and classes, which often correspond well to existing layers. Assign these code artifacts to layers by dragging
them from Solution Explorer to dependency diagrams. You can then use dependency diagrams to help you
update the code and keep it consistent with your design.
See:
Create dependency diagrams from your code
Use code maps to debug your applications
Map dependencies across your solutions

Discover existing dependencies between layers


A dependency exists wherever an artifact that is associated with one layer has a reference to an artifact that is
associated with another layer. For example, a class in one layer declares a variable that has a class in another
layer. You can discover existing dependencies by reverse-engineering them.

NOTE
Dependencies cannot be reverse-engineered for certain kinds of artifacts. For example, no dependencies will be reverse-
engineered from or to a layer that is linked to a text file. To see which artifacts have dependencies that you can reverse-
engineer, right-click one or multiple layers, and then click View Links. In Layer Explorer, examine the Supports
Validation column. Dependencies will not be reverse-engineered for artifacts for which this column shows False.

To reverse-engineer existing dependencies between layers


Select one layer or multiple layers, right-click a selected layer, and then click Generate Dependencies.
Typically, you will see some dependencies that should not exist. You can edit these dependencies to align
them with the intended design.

Edit layers and dependencies to show the intended design


To describe the changes that you plan to make to your system or the intended architecture, use the following
steps to edit the dependency diagram. You might also consider making some refactoring changes to improve
the structure of the code before extending it. See Improving the structure of the code.

TO PERFORM THESE STEPS

Delete a dependency that should not exist Click the dependency, and then press DELETE.

Change or restrict the direction of a dependency Set its Direction property.

Create new dependencies Use the Dependency and Bidirectional Dependency


tools.

To draw multiple dependencies, double-click the tool. When


you are finished, click the Pointer tool or press the ESC key.

Specify that artifacts associated with a layer cannot depend Type the namespaces in the layer's Forbidden Namespace
on the specified namespaces Dependencies property. Use a semicolon (;) to separate the
namespaces.

Specify that artifacts associated with a layer must not belong Type the namespaces in the layer's Forbidden
to the specified namespaces Namespaces property. Use a semicolon (;) to separate the
namespaces.

Specify that artifacts associated with a layer must belong to Type the namespace in the layer's Required Namespaces
one of the specified namespaces property. Use a semicolon (;) to separate the namespaces.

Improving the structure of the code


Refactoring changes are improvements that do not affect the behavior of the application, but help make the
code easier to change and extend in the future. Well-structured code has a design that is easy to abstract to a
dependency diagram.
For example, if you create a layer for each namespace in the code and then reverse-engineer the dependencies,
there should be a minimal set of one-way dependencies between the layers. If you create a more detailed
diagram using classes or methods as your layers, then the result should also have the same characteristics.
If this is not the case, the code will be more difficult to change throughout its life and will be less suitable for
validation using dependency diagrams.
Design new areas of your application
When you start development of a new project, or a new area in a new project, you can draw layers and
dependencies to help identify the major components before you start to develop the code.
Show identifiable architectural patterns in your dependency diagrams, if possible. For example, a
dependency diagram that describes a desktop application might include layers such as Presentation,
Domain Logic, and Data Store. A dependency diagram that covers a single feature within an application
might have layers such as Model, View, and Controller. For more information about such patterns, see
Patterns & Practices: Application Architecture.
Create a code artifact for each layer such as a namespace, class, or component. This makes it easier to
follow the code and to link the code artifacts to layers. As soon as you create each artifact, link it to the
appropriate layer.
You do not have to link most classes and other artifacts to layers because they fall within larger
artifacts such as namespaces that you have already linked to layers.
Create a new diagram for a new feature. Typically, there will be one or more dependency diagrams
describing the whole application. If you are designing a new feature within the application, do not add to
or change the existing diagrams. Instead, create your own diagram that reflects the new parts of the code.
The layers in the new diagram might include presentation, domain logic, and database layers for the new
feature.
When you build the application, your code will be validated both against the overall diagram and your
more detailed feature diagram.

Edit the layout for presentation and discussion


To help you identify layers and dependencies or discuss them with team members, edit the appearance and
layout of the diagram in the following ways:
Change the sizes, shapes, and positions of layers.
Change the colors of layers and dependencies.
Select one or more layers or dependencies, right-click, and then click Properties. In the Properties
window, edit the Color property.

Validate the code against the diagram


When you have edited the diagram, you can validate it against the code manually at any time or automatically
every time that you run a local build or Team Foundation Build.
See:
Validate code with dependency diagrams
Include Layer Validation in the Build Process

Update the code to conform to the new architecture


Typically, errors will appear the first time that you validate code against an updated dependency diagram. These
errors can have several causes:
An artifact is assigned to the wrong layer. In this case, move the artifact.
An artifact, such as a class, uses another class in a way that conflicts with your architecture. In this case,
refactor the code to remove the dependency.
To resolve these errors, update the code until no more errors appear during validation. This is usually an
iterative process. For more information about these errors, see Validate code with dependency diagrams.

NOTE
As you develop or refactor the code, you might have new artifacts to link to the dependency diagram. However, this
might not be necessary, for example, when you have layers that represent existing namespaces, and the new code only
adds more material to those namespaces.

During the development process, you might want to suppress some of the reported conflicts during validation.
For example, you might want to suppress errors that you are already addressing or that are not relevant to your
particular scenario. When you suppress an error, it is a good practice to log a work item in Team Foundation. To
perform this task, see Validate code with dependency diagrams.

Include layer validation in the build process


To ensure that future changes in the code conform to the dependency diagrams, include layer validation to your
solution's standard build process. Whenever other team members build the solution, any differences between
the dependencies in the code and the dependency diagram will be reported as build errors. For more
information about including layer validation in the build process, see Validate code with dependency diagrams.

See Also
Dependency Diagrams: Reference
Create dependency diagrams from your code
Share models and exporting diagrams
11/7/2017 1 min to read Edit Online

In Visual Studio, you can share models and diagrams with colleagues in several ways.
To see which versions of Visual Studio support this feature, see Version support for architecture and modeling
tools.

Working on a Model as a Team


Users of Visual Studio can create and edit models, dependency diagrams, and directed graphs.
Different users can work at the same time on a model, but should work on different packages.
We recommend that you use Team Foundation version control.
For suggestions about organizing models in a medium or large project, see Structure your modeling solution.

Reading and Reviewing Models


Team members using some versions of Visual Studio (which cannot create models) can read models, modeling
diagrams, and directed graphs, but cannot edit them. To see which versions of Visual Studio support this feature,
see Version support for architecture and modeling tools.
A dependency diagram can be opened only within the model in which it was created. To provide access to a
diagram, you must therefore provide access to all the modeling project files, or to copies of them.
For more information, see Read models and diagrams in other Visual Studio editions.

Using Diagrams in Documents and Presentations


You can copy parts of diagrams into documents or presentations. You can also print diagrams, for example to XPS
files.
For more information, see Export diagrams as images.

Related Topics
TITLE DESCRIPTION

Structure your modeling solution Sharing models in a large or medium project.

Export diagrams as images You can copy parts of diagrams into applications such as
PowerPoint or Word, or print a diagram to an XPS file.

Analyzing and Modeling Architecture Overview of models and diagrams.

See Also
Use models in your development process
Read models and diagrams in other Visual Studio
editions
10/18/2017 1 min to read Edit Online

When you open a model in a version of Visual Studio that does not support model creation, the model opens in
read-only mode. In this mode, you can change the layout of the diagrams, but you cannot change the model.
To see which versions of Visual Studio support model creation, see Version support for architecture and modeling
tools.

Obtaining Access to a Model and Diagrams


To read a dependency diagram, you must first use Visual Studio to open the modeling project, and then open the
diagram within it.
For this reason, if you want to read a dependency diagram, you must also have access to the modeling project in
which it was created. You can do this either by accessing the project from Team Foundation version control, or by
obtaining a copy of the project files.

NOTE
This does not apply to code maps and .NET class diagrams generated from code. Those diagrams can be viewed
independently of a modeling project.

To read a dependency diagram, the minimum set of files that you need is as follows:
The two diagram files for the diagram that you want to read, for example, MyDiagram.classdiagram and
MyDiagram.classdiagram.layout.

NOTE
For dependency diagrams, you should also have the file that is named MyDiagram.layerdiagram.suppressions.

The modeling project file (MyModel.modelproj)


The root model file (ModelDefinition\MyModel.uml)
The package files for any package referenced in the diagram (ModelDefinition\MyPackage.uml)

Changes that you can Make in Read-Only Mode


If you open a model and its diagrams in a version of Visual Studio that does not support model creation, you
cannot change the model. That is, you cannot change the elements and relationships that are displayed on the
diagrams or in the model explorer. However, you can make some changes to the layout of the diagrams:
Rearrange the shapes and connectors on the diagram.
Expand and collapse shapes.
You can save these changes. If you want to make your changes visible to other users, you must at least send
the updated .layout files.
Related Topics
TITLE DESCRIPTION

Dependency Diagrams: Reference A layer diagram shows the structure of an existing or


proposed architecture. When code is written, it can be
automatically validated against a layer diagram.

See Also
Create models for your app
Export diagrams as images
10/18/2017 1 min to read Edit Online

In Visual Studio, you can save modeling diagrams in read-only formats so that you can share them with
stakeholders, users, or project team members who do not use the same version of Visual Studio.
To see which versions of Visual Studio support this feature, see Version support for architecture and modeling
tools.
To copy a diagram or part of a diagram to another application
1. On the diagram, select some or all the shapes.
2. Right-click your selection, and then click Copy.
3. Paste your selection into the application that you want.
To save a modeling diagram as an XML Paper Specification (.xps) file
1. On the File menu, click Print to open the Print dialog box.
2. In the Name list, select Microsoft XPS Document Writer, and then click OK.
3. In the Save the file as dialog box, save the file as an XPS Document (*.xps) file.

.NET Framework Security


See Also
Create models for your app
Share models and exporting diagrams
Use models in your development process
11/7/2017 10 min to read Edit Online

In Visual Studio, you can use a model to help you understand and change a system, application, or component. A
model can help you visualize the world in which your system works, clarify users' needs, define the architecture
of your system, analyze the code, and ensure that your code meets the requirements. See Channel 9 Video:
Improve architecture through modeling.
To see which versions of Visual Studio support each type of model, see Version support for architecture and
modeling tools.

How to use models


Models can help you in several ways:
Drawing modeling diagrams helps you clarify the concepts involved in requirements, architecture, and
high-level design. For more information, see Model user requirements.
Working with models can help you expose inconsistencies in requirements.
Communicating with models helps you communicate important concepts less ambiguously than with
natural language. For more information, see Model your app's architecture.
You can sometimes use models to generate code or other artifacts such as database schemas or
documents. For example, the modeling components of Visual Studio Ultimate are generated from a model.
For more information, see Generate and configure your app from models.
You can use models in a wide variety of processes, from extreme agile to high ceremony.
Use Models to Reduce Ambiguity
Modeling language is less ambiguous than natural language, and it is designed to express the ideas typically
required during software development.
If your project has a small team following agile practices, you can use models to help you clarify user stories. In
discussions with the customer about their needs, creating a model can generate useful questions much faster,
and across a broader area of the product, than writing spike or prototype code.
If your project is large and includes teams in different parts of the globe, you can use models to help
communicate the requirements and architecture much more effectively than you can in plain text.
In both cases creating a model almost always results in a significant reduction in inconsistencies and ambiguities.
Different stakeholders frequently have different understandings of the business world in which the system works,
and different developers frequently have different understandings of how the system works. Using a model as
the focus of a discussion usually exposes these differences. For more information about how to use a model to
reduce inconsistencies, see Model user requirements.
Use Models with Other Artifacts
A model is not by itself a requirements specification or an architecture. It is a tool for expressing some aspects of
these things more clearly, but not all the concepts required during software design can be expressed. The models
should therefore be used together with other means of communication, such as OneNote pages or paragraphs,
Microsoft Office documents, work items in Team Foundation, or sticky notes on the project room wall. Apart from
the last item, all of these object types can be linked to elements parts of the model.
Other aspects of specification that are ordinarily used together with models include the following. Depending on
the scale and style of your project, you might use several of these aspects or not use any at all:
User stories. A user story is a short description, discussed with users and other stakeholders, of an aspect
of the system's behavior that will be delivered in one of the project's iterations. A typical user story begins
"The customer will be able to...." A user story might introduce a group of use cases, or can define
extensions of use cases that have been previously developed. Defining or extending the use cases helps
make the user story clearer.
Change Requests. A change request in a more formal project is very similar to a user story in an agile
project. The agile approach treats all requirements as changes to what was developed in previous
iterations.
Use case description. A use case represents one way in which a user interacts with the system to achieve a
particular goal. A full description includes the goal, main and alternative sequences of events, and
exceptional outcomes. A use case diagram helps summarize and provide an overview of the use cases.
Scenarios. A scenario is a fairly detailed description of a sequence of events showing how the system,
users and other systems work together to provide value to the stakeholders. It might take the form of a
slide show of the user interface or a prototype of the user interface. It can describe one use case or a
sequence of use cases.
Glossary. The project's requirements glossary describes the words with which customers discuss their
world. The user interface and requirements models should also use these terms. A class diagram can help
clarify the relationships between most of these terms. Creating the diagrams and glossary not only
reduces misunderstandings between users and developers, but also almost always exposes
misunderstandings between different business stakeholders.
Business rules. Many business rules can be expressed as invariant constraints on the associations and
attributes in the requirements class model, and as constraints on sequence diagrams.
High-level design. Describes the major parts and how they fit together. Component, sequence and
interface diagrams are a major part of a high-level design.
Design patterns. Describe the rules of design that are shared across the different parts of the system.
Test specifications. Test scripts and the designs for test code can make good use of activity and sequence
diagrams to describe sequences of test steps. System tests should be expressed in terms of the
requirements model so that they can easily be changed when the requirements change.
Project plan. The project plan or backlog defines when each feature will be delivered. You can define each
feature by stating what use cases and business rules it implements or extends. You can either refer to the
use cases and business rules directly in the plan, or you can define a set of features in a separate
document, and use the feature titles in the plan.
Use Models in Iteration Planning
Although all projects are different in their scale and organization, a typical project is planned as a series of
iterations of between two and six weeks. It is important to plan enough iterations to allow feedback from early
iterations to be used to adjust the scope and plans for later iterations.
You might find the following suggestions useful to help realize the benefits of modeling in an iterative project.
Sharpen Focus as each Iteration Approaches
As each iteration approaches, use models to help define what is to be delivered at the end of the iteration.
Do not model everything in detail in the early iterations. In the first iteration, create a class diagram for the
main items in the user glossary, draw a diagram of the major use cases, and draw a diagram of the major
components. Do not describe any of these in fine detail, because the detail will change later in the project.
Use the terms defined in this model to create a list of features or major user stories. Assign the features to
iterations so as to approximately balance the estimated workload throughout the project. These
assignments will change later in the project.
Try to implement simplified versions of all the most important use cases in an early iteration. Extend those
use cases in later iterations. This approach helps reduce the risk of discovering a flaw in the requirements
or the architecture too late in the project to do anything about it.
Near the end of each iteration, hold a requirements workshop to define in detail the requirements or user
stories that will be developed in the next iteration. Invite users and business stakeholders who can decide
priorities, as well as developers and system testers. Allow three hours to define requirements for a 2-week
iteration.
The objective of the workshop is for everyone to agree what will be accomplished by the end of the next
iteration. Use models as one of the tools to help clarify the requirements. The output of the workshop is an
iteration backlog: that is, a list of development tasks in Team Foundation and test suites in Microsoft Test
Manager.
In the requirements workshop, discuss the design only insofar as you need to determine estimates for the
development tasks. Otherwise, keep discussion to system behavior that users can experience directly. Keep
the requirements model separate from the architectural model.
Nontechnical stakeholders usually have no problems understanding UML diagrams, with some guidance
from you.
Link Model to Work Items
After the requirements workshop, elaborate the details of the requirements model, and link the model to
development tasks. You can do this by linking work items in Team Foundation to elements in the model.
You can link any element to work items, but the most useful elements are as follows:
Comments describing business rules or quality of service requirements. For more information, see Model user
requirements.
Link Model to Tests
Use the requirements model to guide the design of the acceptance tests. Create these tests concurrently with the
development work.
To learn more about this technique, see Develop tests from a model.
Estimate Remaining Work
A requirements model can help estimate the total size of the project in relation to the size of each iteration.
Assessing the number and complexity of the use cases and classes can help you estimate the development work
that will be required. When you have completed the first few iterations, a comparison of the requirements
covered and the requirements still to cover can give an approximate measure of the cost and scope of the rest of
the project.
Near the end of each iteration, review the assignment of requirements to future iterations. It can be useful to
represent the state of your software at the end of each iteration as a subsystem in a use case diagram. In your
discussions, you can move use cases and use case extensions from one of these subsystems to another.

Levels of abstraction
Models have a range of abstraction in relation to the software. The most concrete models directly represent
program code, and the most abstract models represent business concepts that might or might not be represented
in the code.
A model can be viewed through several kinds of diagrams. For information about models and diagrams, see
Create models for your app.
Different kinds of diagram are useful for describing the design at different levels of abstraction. Many of the
diagram types are useful at more than one level. This table shows how each type of diagram can be used.

DESIGN LEVEL DIAGRAM TYPES

Business Process - Conceptual class diagrams describe the business concepts


used within the business process.
Understanding the context within which your system will be
used helps you understand what the users need from it.

User requirements - Business rules and quality of service requirements can be


described in separate documents.
Definition of what the users need from your system.

High level design - Dependency Diagrams describe how the system is


structured into interdependent parts. You can validate
The overall structure of the system: the major components program code against dependency diagrams to ensure that it
and how they couple together. adheres to the architecture.

Code analysis - Dependency diagrams show the dependencies between


classes. Updated code can be validated against a dependency
Diagrams can be generated from the code. diagram.
- Class diagrams show the classes in the code.

External Resources
CATEGORY LINKS

Videos MSDN How Do I Videos: How to Create and Use UML


Models and Diagrams (Visual Studio 2010 Ultimate)

Channel 9: UML with Visual Studio 2010

MSDN How Do I Series: UML Tools and Extensibility


(Visual Studio 2010 Ultimate)

Forums - Visual Studio Visualization & Modeling Tools


- Visual Studio Visualization & Modeling SDK (DSL Tools)

Blogs Visual Studio ALM + Team Foundation Server Blog

Technical Articles and Journals MSDN Architecture Center

Visual Studio Architecture Tooling Guidance

See Also
Use models in Agile development
Create models for your app
Model user requirements
Model your app's architecture
Develop tests from a model
Structure your modeling solution
In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
Model user requirements
10/18/2017 4 min to read Edit Online

Visual Studio helps you understand, discuss, and communicate your users' needs by drawing diagrams about
their activities and the part your system plays in helping them achieve their goals. A requirements model is a set
of these diagrams, each of which focuses on a different aspect of the users' needs. For a video demonstration,
see: Modeling the Business Domain.
To see which versions of Visual Studio support each type of model, see Version support for architecture and
modeling tools.
A requirements model helps you:
Focus on the system's external behavior, separately from its internal design.
Describe the users' and stakeholders' needs with much less ambiguity than you can in natural language.
Define a consistent glossary of terms that can be used by users, developers, and testers.
Reduce gaps and inconsistencies in the requirements.
Reduce the work needed to respond to requirements changes.
Plan the order in which features will be developed.
Use the models as a basis for system tests, making a clear relationship between the tests and the
requirements. When the requirements change, this relationship helps you update the tests correctly. This
makes sure that the system meets the new requirements.
A requirements model provides greatest benefit if you use it to focus discussions with the users or their
representatives, and revisit it at the beginning of each iteration. You do not have to complete it in detail
before writing code. A partially working application, even if very much simplified, generally forms the
most stimulating basis for discussion of the requirements with users. The model is an effective way to
summarize the results of those discussions. For more information, see Use models in your development
process.

NOTE
Throughout these topics, "system" means the system or the application that you are developing. It might be a large
collection of many software and hardware components; or a single application; or a software component inside a larger
system. In every case, the requirements model describes the behavior that is visible from outside your system, whether
through a user interface or API.

Common Tasks
You can create several different views of the users' requirements. Each view provides a particular type of
information. When you create these views, it is best to move frequently from one to another. You can start from
any view.

WHAT IT DESCRIBES IN A REQUIREMENTS


DIAGRAM OR DOCUMENT MODEL SECTION
WHAT IT DESCRIBES IN A REQUIREMENTS
DIAGRAM OR DOCUMENT MODEL SECTION

Conceptual class diagram Glossary of types that are used to


describe the requirements; the types
visible at the system's interface.

Additional documents or work items Performance, security, usability and Describing quality of service
reliability criteria. requirements

Additional documents or work items Constraints and rules not specific to a Showing business rules
particular use case

Notice that most of the diagram types can be used for other purposes. For an overview of diagram types, see
Create models for your app.

Showing Business Rules


A business rule is a requirement that is not associated with a particular use case, and should be observed
throughout the system.
Many business rules are constraints on the relationships among the conceptual classes. You can write these
staticbusiness rules as comments associated with the relevant classes on a conceptual class diagram. For example:

Dynamic business rules constrain the allowable sequences of events. For example, you use a sequence or activity
diagram to show that a user must log in before performing other operations on your system.
However, many dynamic rules can be more effectively and generically stated by replacing them with static rules.
For example, you could add a Boolean attribute 'Logged In' to a class in the conceptual class model. You would
add Logged In as the postcondition of the log in use case, and add it as a precondition of most of the other use
cases. This approach lets you avoid defining all the possible combinations of sequences of events. It is also more
flexible when you need to add new use cases to the model.
Notice that the choice here is about how you define the requirements, and that this is independent of how you
implement the requirements in the program code.
The following topics provide more information:

TO LEARN ABOUT READ

How to develop code that adheres to business rules Model your app's architecture

Describing Quality of Service Requirements


There are several categories of quality of service requirement. They include the following:
Performance
Security
Usability
Reliability
Robustness
You can include some of these requirements in the descriptions of particular use cases. Other
requirements are not specific to use cases, and are most effectively written in a separate document. When
you can, it is useful to adhere to the vocabulary defined by the requirements model. In the following
example, notice that the main words used in the requirement are the titles of actors, use cases, and classes
in the preceding illustrations:
If a Restaurant deletes a Menu Item while a Customer is Ordering a Meal, any Order Item that refers to
that Menu Item will be displayed in red.
The following topics provide more information:

TO LEARN ABOUT READ

More detailed information about recording quality of service Guidelines for Defining Quality of Service Requirements
requirements

How to develop code that adheres to quality of service Model your app's architecture
requirements

See Also
Use models in your development process
Model your app's architecture
Model your app's architecture
10/18/2017 7 min to read Edit Online

To help ensure that your software system or application meets your users' needs, you can create models in
Visual Studio as part of your description of the overall structure and behavior of your software system or
application. Using models, you can also describe patterns that are used throughout the design. These models
help you understand the existing architecture, discuss changes, and communicate your intentions clearly.
To see which versions of Visual Studio support this feature, see Version support for architecture and modeling
tools.
The purpose of a model is to reduce the ambiguities that occur in natural-language descriptions, and to help you
and your colleagues to visualize the design and to discuss alternative designs. A model should be used together
with other documents or discussions. By itself, a model does not represent a complete specification of the
architecture.

NOTE
Throughout this topic, "system" means the software that you are developing. It might be a large collection of many
software and hardware components, or a single application, or a part of an application.

The architecture of a system can be divided into two areas:


High-level Design. This describes the major components and how they interact with one another to fulfill
each requirement. If the system is large, each component might have its own high-level design that shows
how it is composed of smaller components.
Design Patterns and conventions used throughout the designs of the components. A pattern describes a
particular approach to achieving a programming goal. By using the same patterns throughout a design,
your team can reduce the cost of making changes and developing new software.

High-level Design
A high-level design describes the major components of your system and how they interact with one another to
achieve the goals of the design. The activities in the following list are involved in developing the high level
design, although not necessarily in a particular sequence.
If you are updating existing code, you might begin by describing the major components. Make sure you
understand any changes to the user requirements and then add or modify interactions between the components.
If you are developing a new system, begin by understanding the main features of the users' needs. You can then
explore sequences of interactions for the main use cases, and then consolidate the sequences into a component
design.
In every case, it is helpful to develop the different activities in parallel, and to develop code and tests at an early
stage. Avoid trying to complete one of these aspects before you start another. Typically, both the requirements
and your understanding of the best way to design the system will change while you are writing and testing the
code. Therefore, you should begin by understanding and coding the main features of the requirements and your
design. Fill in the details in later iterations of the project.
Understanding the Requirements. The starting point of any design is a clear understanding of the users'
needs.
Architectural Patterns. The choices you made about core technologies and architectural elements of the
system.
Data Model of the Components and Interfaces. You can draw class diagrams to describe the information
that is passed between components and stored inside the components.

Understanding the Requirements


The high-level design of a complete application is most effectively developed together with a requirements
model or other description of the users' needs. For more information about requirements models, see Model
user requirements.
If the system that you are developing is a component in a larger system, part or all of your requirements might
be embodied in programmatic interfaces.
The requirements model provides these essential pieces of information:
Provided interfaces. A provided interface lists the services or operations that the system or component
must provide to its users, whether they are human users or other software components.
Required interfaces. A required interface lists the services or operations that the system or component can
use. In some cases, you will be able to design all these services as part of your own system. In other cases,
especially if you are designing a component that can be combined with other components in many
configurations, the required interface will be set by external considerations.
Quality of service requirements. The performance, security, robustness, and other goals and constraints
that the system must meet.
The requirements model is written from the point of view of your system's users, whether they are people
or other software components. They know nothing of the internal workings of your system. By contrast,
your goal in an architectural model is to describe the internal workings and show how they meet the
users' needs.
Keeping the requirements and architectural models separate is useful because it makes it easier to discuss
the requirements with the users. It also helps you refactor the design and consider alternative
architectures while keeping the requirements unchanged.
The amount of detail that you should put into either a requirements or an architectural model depends on
the scale of the project and the size and distribution of the team. A small team on a short project might go
no further than sketching a class diagram of the business concepts and some design patterns; a large
project distributed over more than one region would need significantly more detail.

Architectural Patterns
Early in a development, you have to choose the major technologies and elements on which the design depends.
The areas in which these choices must be made include the following:
Base technology choices, such as the choice between a database and a file system, and the choice between
a networked application and a Web client, and so on.
Frameworks choices, such as a choice between Windows Workflow Foundation or ADO.NET Entity
Framework.
Integration method choices, for example between an enterprise service bus or a point-to-point channel.
These choices are frequently determined by quality of service requirements such as scale and flexibility,
and can be made before the detailed requirements are known. In a large system, the configuration of
hardware and software are strongly interrelated.
The selections that you make affect how you use and interpret the architectural model. For example, in a
system that uses a database, associations in a class diagram might represent relations or foreign keys in
the database, whereas in a system that is based on XML files, associations might indicate cross-references
that use XPath. In a distributed system, messages in a sequence diagram can represent messages on a
wire; in a self-contained application, they can represent function calls.

Design Patterns
A design pattern is an outline of how to design a particular aspect of the software, especially one that recurs in
different parts of the system. By adopting a uniform approach across the project, you can reduce the cost of
design, ensure consistency in the user interface, and reduce the cost of understanding and changing the code.
Some general design patterns such as Observer are well-known and widely applicable. In addition, there are
patterns that are applicable just to your project. For example, in a Web sales system, there will be several
operations in the code where changes are made to a customer's order. To ensure that the state of the order is
accurately displayed at every stage, all these operations must follow a particular protocol to update the database.
Part of the work of software architecture is to determine what patterns should be adopted across the design. This
is usually an ongoing task, because new patterns and improvements to existing patterns will be discovered as
the project progresses. It is helpful to organize the development plan so that you exercise each of your major
design patterns at an early stage.
Most design patterns can be partly embodied in framework code. Part of the pattern can be reduced to requiring
the developer to use particular classes or components, such as a database access layer that ensures the database
is handled correctly.
A design pattern is described in a document, and typically includes these parts:
Name.
Description of the context in which it is applicable. What criteria should make a developer consider
applying this pattern?
Brief explanation of the problem it solves.
Model of the major parts and their relationships. These might be classes or components and interfaces,
with associations and dependencies between them. The elements usually fall into two categories:
Naming conventions.
Description of how the pattern solves the problem.
Description of variations that developers might be able to adopt.

See Also
Visualize code
Model user requirements
Develop tests from a model
Use models in your development process
Generate and configure your app from models
10/18/2017 5 min to read Edit Online

You can generate or configure parts of your application from a model.


The model represents the requirements more directly than the code. By deriving the application's behavior directly
from the model, you can respond to changed requirements much more quickly and reliably than by updating the
code. Although some initial work is required to set up the derivation, this investment is returned if you expect
changes in requirements, or if you plan to make several variants of the product.

Generating the Code of your Application from a Model


The easiest way to generate code is by using text templates. You can generate code in the same Visual Studio
solution in which you keep the model. For more information, see:
Design-Time Code Generation by using T4 Text Templates
Generating Code from a Domain-Specific Language
This method is easy to apply incrementally. Start with an application that works only for a specific case, and
choose a few parts of it that you want to vary from the model. Rename the source files of these parts so that
they become text template (.tt) files. At this point, the source .cs files will automatically be generated from
the template files, so the application will work as it did before.
Then you can take one part of the code and replace it with a text template expression, which reads the model
and generates that part of the source file. At least one value of the model should generate the original
source so that again you can run the application and it will work as before. After you test different model
values, you can move on to insert template expressions in another part of the code.
This incremental method means that code generation is usually a low-risk approach. The resulting
applications usually perform almost as well as a hand-written version.
However, if you start with an existing application, you might find that a lot of refactoring is required to
separate the different behaviors that are governed by the model so that they can be independently varied.
We recommend that you assess this aspect of the application when you estimate the cost of your project.

Configuring your Application from a Model


If you want to vary your application's behavior at run time, you cannot use code generation, which generates
source code before the application is compiled. Instead, you can design your application to read the model, and to
vary its behavior accordingly. For more information, see:
How to: Open a Model from File in Program Code
This method can also be applied incrementally, but there is more work at the beginning. You need to write
the code that will read the model, and set up a framework that allows its values to be accessible to the
variable parts. Making the variable parts generic is more expensive than code generation.
A generic application usually performs less well than its specific counterparts. If performance is crucial, your
project plan should include an assessment of this risk.

Developing a Derived Application


You might find the following general guidelines useful.
Start specific, then generalize. Write a specific version of your application first. This version should work
in one set of conditions. When you are satisfied that it is working correctly, you can make some of it derive
from a model. Extend the derived parts gradually.
For example, design a Web site that has a specific set of Web pages before you design a Web application
that presents pages that are defined in a model.
Model the variant aspects. Identify the aspects that will vary, either between one deployment and another,
or over time as requirements change. These are the aspects that should be derived from a model.
For example, if the set of Web pages and links between them changes, but the style and format of the pages
is always the same, then the model should describe the links, but does not have to describe the format of the
pages.
Separate concerns. If the variable aspects can be divided into independent areas, use separate models for
each area. Using ModelBus, you can define operations that affect both models, and constraints between
them.
For example, use one model to define navigation between the Web pages and a different model to define
the layout of the pages.
Model the requirement, not the solution. Design the model so that it describes the user requirements.
By contrast, do not design the notation according to the variable aspects of the implementation.
For example, the Web navigation model should represent Web pages and hyperlinks between them. The
Web navigation model should not represent fragments of HTML or classes in your application.
Generate or interpret? If the requirements for a particular deployment will rarely change, generate
program code from the model. If the requirements might frequently change, or might co-exist in more than
one variant in the same deployment, write the application so that it can read and interpret a model.
For example, if you use your Web site model to develop a series of different and separately-installed Web
sites, then you should generate the code of the site from the model. But it you use your model to control a
site that changes every day, then it is better to write a Web server that reads the model and presents the site
accordingly.
UML or DSL? Consider creating your modeling notation by using stereotypes to extend UML. Define a DSL
if there is no UML diagram that fits the purpose. But avoid breaking the standard semantics of UML.
For example, a UML class diagram is a collection of boxes and arrows; with this notation you can in theory
define anything. But we do not recommend that you use the class diagram except where you are in fact
describing a set of types. For example, you could adapt class diagrams to describe different types of Web
pages.

See Also
Generating Code from a Domain-Specific Language
How to: Open a Model from File in Program Code
Design-Time Code Generation by using T4 Text Templates
Structure your modeling solution
11/7/2017 6 min to read Edit Online

To use models effectively in a development project, the team members must be able to work on models of
different parts of the project at the same time. This topic suggests a scheme for dividing the application into
different parts that correspond to the layers in an overall layering diagram.
To start on a project or subproject quickly, it is useful to have a project template that follows the project structure
that you have chosen. This topic describes how to create and use such a template.
This topic assumes that you are working on a project that is large enough to require several team members, and
perhaps has several teams. The code and models of the project are stored on a source control system such as
Team Foundation Server. At least some team members use Visual Studio to develop models, and other team
members can view the models by using other Visual Studio versions.
To see which versions of Visual Studio support each tool and modeling feature, see Version support for
architecture and modeling tools.

Solution structure
In a medium or large project, the structure of the team is based on the structure of the application. Each team uses
a Visual Studio solution.
To divide an application into layers
1. Base the structure of your solutions on the structure of your application, such as web application, service
application, or desktop application. A variety of common architectures is discussed in Application
Archetypes in the Microsoft Application Architecture Guide.
2. Create a Visual Studio solution, which we will call the Architecture solution. This solution will be used to
create the overall design of the system. It will contain models but no code.
Add a dependency diagram to this solution. On the dependency diagram, draw the architecture you have
chosen for your application. For example, the diagram might show these layers and the dependencies
between them: Presentation; Business logic; and Data.
3. Create a separate Visual Studio solution for each layer in the Architecture dependency diagram.
These solutions will be used to develop the code of the layers.
4. Create models that will represent the designs of the layers and the concepts that are common to all the
layers. Arrange the models so that all the models can be seen from the Architecture solution, and the
relevant models can be seen from each layer.
You can achieve this by using either of the following procedures. The first alternative creates a separate
modeling project for each layer, and the second creates a single modeling project that is shared between
the layers.
To u s e a s e p a ra t e mo d e l i n g p ro j e c t f o r e a c h l a y e r

a. Create a modeling project in each layer solution.


This model will contain diagrams that describe the requirements and design of that layer. It can also
contain dependency diagrams that show nested layers.
You now have a model for each layer, plus a model for the application architecture. Each model is
contained in its own solution. This enables team members to work on the layers at the same time.
b. To the Architecture solution, add the modeling project of each layer solution. To do this, open the
Architecture solution. In Solution Explorer, right-click the solution node, point to Add, and then click
Existing Project. Navigate to the modeling project (.modelproj) in one layer solution.
Each model is now visible in two solutions: its "home" solution and the Architecture solution.
c. To the modeling project of each layer, add a dependency diagram. Start with a copy of the
Architecture dependency diagram. You can delete parts that are not dependencies of the dependency
diagram.
You can also add dependency diagrams that represent the detailed structure of this layer.
These diagrams are used to validate the code that is developed in this layer.
d. In the Architecture solution, edit the requirements and design models of all the layers by using Visual
Studio.
In each layer solution, develop the code for that layer, referring to the model. If you are content to do
the development without using the same computer to update the model, you can read the model
and develop code by using versions of Visual Studio that cannot create models. You can also
generate code from the model in these versions.
This method guarantees that no interference will be caused by developers who edit the layer models
at the same time.
However, because the models are separate, it is difficult to refer to common concepts. Each model
must have its own copy of the elements on which it is dependent from other layers and the
architecture. The dependency diagram in each layer must be kept in sync with the Architecture
dependency diagram. It is difficult to maintain synchronization when these elements change,
although you could develop tools to accomplish this.
To u s e a s e p a ra t e p a c k a g e f o r e a c h l a y e r

a. In the solution for each layer, add the Architecture modeling project. In Solution Explorer, right-click
the solution node, point to Add, and then click Existing Project. The single modeling project can
now be accessed from every solution: the Architecture project, and the development project for each
layer.
b. In the shared model, create a package for each layer: In Solution Explorer, select the modeling
project. In UML Model Explorer, right-click the model root node, point to Add, and then click
Package.
Each package will contain diagrams that describe the requirements and design of the corresponding
layer.
c. If required, add local dependency diagrams for the internal structure of each layer.
This method allows the design elements of each layer to refer directly to those of the layers and
common architecture on which it depends.
Although concurrent work on different packages can cause some conflicts, they are fairly easy to
manage because the packages are stored in separate files.

Creating architecture templates


In practice, you will not create all your Visual Studio solutions at the same time, but add them as the project
progresses. You will probably also use the same solution structure in future projects. To help you create new
solutions quickly, you can create a solution or project template. You can capture the template in a Visual Studio
Integration Extension (VSIX) so that it is easy to distribute and to install on other computers.
For example, if you frequently use solutions that have Presentation, Business, and Data layers, you can configure a
template that will create new solutions that have that structure.
To create a solution template
1. Download and install the Export Template Wizard, if you have not already done this.
2. Create the solution structure that you want to use as a starting point for future projects.
3. On the File menu, click Export Template as VSIX. The Export Template as VSIX Wizard opens.
4. Following the instructions in the wizard, select the projects that you want to include in the template, provide
a name and description for the template, and specify an output location.

NOTE
The material in this topic is abstracted and paraphrased from the Visual Studio Architecture Tooling Guidance, written by the
Visual Studio ALM Rangers, which is a collaboration between Most Valued Professionals (MVPs), Microsoft Services, and the
Visual Studio product team and writers. Click here to download the complete Guidance package.

Related materials
Organizing and Managing Your Models - video by Clint Edmondson.
Visual Studio Architecture Tooling Guidance - Further guidance on managing models in a team

See Also
Use models in your development process
Visual Studio Architecture Tooling Guidance
10/18/2017 1 min to read Edit Online

This Architecture Tooling Guidance is a downloadable package of practical guidance about the use of the modeling
tools in Visual Studio 2010. It presents a high-level view of how to use all the modeling tools in an integrated
manner, and discusses the most common scenarios in which the tools are used.
The content was created by the Visual Studio ALM Rangers, a special group that has members from the Visual
Studio Product Team, Microsoft Services, Microsoft Most Valued Professionals (MVPs), and Visual Studio
Community Leads.
Click here to download the Architecture Tooling Guidance..
Validate your system during development
10/18/2017 1 min to read Edit Online

Visual Studio can help keep your software consistent with the users' requirements and with the architecture of
your system.
To see which versions of Visual Studio support each of these features, see Version support for architecture and
modeling tools.

Key Tasks
Use the following tasks to validate your software.

TASKS ASSOCIATED TOPICS

Make sure your software meets the users' requirements: - Develop tests from a model

You can use requirements and architectural models to help


you organize the tests of your system and its components.
This practice helps ensure that you test the requirements that
are important to the users and other stakeholders, and it
helps you update the tests quickly when the requirements
change.

Make sure that your software remains consistent with the - Create dependency diagrams from your code
intended design of your system: - Validate code with dependency diagrams

Dependency diagrams describe the intended dependencies


between the components of your application. During
development, you can verify that the actual dependencies in
the code conform to the intended design.

External Resources
CATEGORY LINKS

Videos Channel 9: Doug Seven: Code Understanding and System


Design with Visual Studio 2010

Channel 9: Architecting an Application using a


Dependency Diagram

MSDN How Do I Series: How to Validate Code using


Dependency Diagrams

Forums - Visual Studio Visualization & Modeling Tools


- Visual Studio Visualization & Modeling SDK (DSL Tools)

Blogs - Visual Studio ALM + Team Foundation Server Blog

Technical Articles and Journals MSDN Architecture Center


See Also
Testing the application
Model user requirements
Analyzing and Modeling Architecture

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Develop tests from a model
11/7/2017 11 min to read Edit Online

You can use requirements and architectural models to help you organize the tests of your system and its
components. This practice helps ensure that you test the requirements that are important to the users and other
stakeholders, and it helps you update the tests quickly when the requirements change. If you use Microsoft Test
Manager, you can also maintain links between the models and the tests.
To see which versions of Visual Studio support these features, see Version support for architecture and modeling
tools.

System and Subsystem Testing


System testing, also known as acceptance testing, means testing whether the users' needs are being met. Such
tests are concerned about the externally visible behavior of the system instead of the internal design.
System tests are very valuable when extending or redesigning a system. They help you avoid introducing bugs
when you change the code.
When you plan any change or extension to a system, it is helpful to start with a set of system tests that run on the
existing system. Then you can extend or adjust the tests to test the new requirements, make the changes to the
code, and rerun the complete set of tests.
When you develop a new system, you can begin to create tests as soon as development begins. By defining tests
before you develop each feature, you can capture the requirements discussions in a very specific way.
Subsystem testing applies the same principles to the major components of a system. Each component is tested
separately from other components. Subsystem tests focus on the behavior visible at the component's user
interfaces or API.

Deriving System Tests from a Requirements Model


You can create and maintain a relationship between system tests and a requirements model. To establish this
relationship, you write tests that correspond to the main elements of the requirements model. Visual Studio helps
you maintain that relationship by letting you create links between the tests and parts of the model. For more
information about requirements models, see Model user requirements.
Write Tests for Each Use Case
If you use Microsoft Test Manager, you can create a group of tests for each use case that you defined in your
requirements model. For example, if you have a use case Order a Meal, which includes Create Order and Add Item
to Order, you can create tests for both the overall and the more detailed of these use cases.
These guidelines might be helpful:
Each use case should have several tests, for main paths and exceptional outcomes.
When you describe a use case in the requirements model, it is more important to define its postcondition,
that is, the goal that is achieved, than to describe in detail the procedures the user follows in order to
achieve the goal. For example, the postcondition of Order a Meal might be that a Restaurant is preparing a
meal for a Customer and that the Customer has paid. The postcondition is the criterion that your tests
should verify.
Base separate tests on the separate clauses of the postcondition. For example, create separate tests for
notifying the restaurant of the order, and for taking payment from the customer. This separation has these
advantages:
Changes in different aspects of the requirements frequently occur independently. By separating the
tests into different aspects in this manner, you make it easier to update the tests when requirements
change.
If the development plan implements one aspect of the use case before another, you can enable the
tests separately as development progresses.
When you design the tests, separate the choice of test data from the code or script that determines whether
the postcondition has been achieved. For example, a test of a simple arithmetic function might be: Input 4;
verify that the output is 2. Instead, design the script as: Choose an input; multiply the output by itself, and
verify that the result is the original input. This style enables you to vary the test inputs without changing the
main body of the test.
Linking tests to use cases
If you are using Test Manager to design and run your tests, you can organize your tests under requirement, use
case, or user story work items. You can link these work items to use cases in your model. This enables you to
quickly trace requirements changes to the tests, and helps you track the progress of each use case.
To l i n k t e s t s t o a u s e c a s e

1. In Test Manager, create a requirement and base a test suite on it.


The requirement that you create is a work item in Team Foundation Server. It might be a User Story,
Requirement, or Use Case work item, depending on the process template that your project uses with Team
Foundation. For more information, see Track work using Visual Studio Team Services or Team Foundation
Server.
2. Link the requirement work item to one or more use cases in your model.
In a use case diagram, right-click a use case and then click Link to Work Item.
3. Add to the test suite, test cases that verify the use cases.
Usually, each user story or requirement work item will link to several use cases in your model, and each use
case will link to several user stories or requirements. This is because each user story or requirement covers
a set of tasks that develop several use cases. For example, in an early iteration of your project, you might
develop the basic user story in which a customer can choose items from a catalog and have them delivered.
In a later iteration, the story might be that the user pays when completing the order, and the supplier
receives the money after it sends the goods. Each story adds a clause to the postcondition of the Order
Goods use case.
You can create separate links from requirements to the clauses of the postcondition by writing those
clauses in separate comments on the use case diagram. You can link each comment to a requirement work
item, and link the comment to the use case on the diagram.
Base Tests on the Requirements Types
The types, that is, the classes, interfaces and enumerations, of a requirements model describe the concepts and
relationships in terms of how users think and communicate about their business. It excludes types concerned only
with the internal design of the system.
Design your tests in terms of these requirements types. This practice helps you ensure that when changes to the
requirements are discussed, it is easy to relate the changes to the necessary changes in the tests. It makes it
possible to discuss the tests and their intended results directly with end-users and other stakeholders. This means
that the users' needs can be maintained outside the development process, and avoids the inadvertent design of
the tests around possible flaws in the design.
For manual tests, this practice involves adhering to the vocabulary of the requirements model in the test scripts.
For automated tests, this practice involves using the requirements class diagrams as a basis for your test code, and
creating accessor and updater functions to link the requirement model to the code.
For example, a requirements model might include types Menu, Menu Item, Order, and associations between them.
This model represents the information that is stored and dealt with by the meal ordering system, but does not
represent the complexities of its implementation. In the working system, there might be several different
realizations of each type, in databases, in user interfaces and on APIs. In a distributed system, there might be
several variants of each instance stored in different parts of the system at the same time.
To test a use case such as Add Item to Order, a test method could include code similar to this:

Order order = ... ; // set up an order


// Store prior state:
int countBefore = order.MenuItems.Count;
// Perform use case:
MenuItem chosenItem = ...; // choose an item
AddItemToOrder (chosenItem, order);
// Verify part of postcondition:
int countAfter = order.MenuItems.Count;
Assert (countAfter == countBefore = 1);

Notice that this test method uses the classes of the requirements model. Associations and attributes are realized as
.NET properties.
To make this work, the properties of the classes must be defined as read-only functions or accessors, which access
the system to retrieve information about its current state. Methods that simulate use cases such as
AddItemToOrder must drive the system through its API or through a layer underneath its user interface. The
constructors of test objects such as Order and MenuItem must also drive the system to create corresponding
items inside the system.
Many of the accessors and updaters will already be available through the application's normal API. But some
additional functions might have to be written in order to enable the tests. These additional accessors and updaters
are sometimes known as 'test instrumentation'. Because they depend on the internal design of the system, it is the
responsibility of the system's developers to provide them, whereas the testers write the code of the tests in terms
of the requirements model.
When you write automated tests, you can use Generic Tests to wrap the accessors and updaters.
Tests for Business Rules
Some requirements are not directly related to any one use case. For example, the DinnerNow business allows
customers to choose from many Menus, but requires that in every Order, all the chosen Items shall be from a
single Menu. This business rule can be expressed as an invariant about the associations between Orders, Menus,
and Items in the requirements class model.
An invariant rule of this kind governs not only all the use cases that are currently defined, but also any other use
cases that will be defined later. Therefore, it is useful to write it separately from any use case, and to test it
separately from the use cases.

Deriving Subsystem Tests from Models


In the high-level design of a large system, you can identify components or subsystems. These represent parts that
can be separately designed, or are located on different computers, or are reusable modules that can be
recombined in many ways.
You can apply to each major component the same principles as you use for the complete system. In a large
project, each component can have its own requirements model. In smaller projects, an architectural model or high-
level design can be created to show the major components and their interactions. For more information, see
Model your app's architecture.
In either case, you can establish a relationship between the model elements and the subsystem tests in the same
manner as you would between the requirements model and the system tests.
Isolate Components with Provided and Required Interfaces
It is useful to identify all the dependencies that a component has on other parts of your system or external
services, and to represent these as Required Interfaces. This exercise usually leads to some redesign that leaves the
component much more decoupled and easily separable from the rest of your design.
An advantage of this decoupling is that the component can be executed for testing by replacing with mock objects
the services it usually uses. These are components that are set up for the purposes of testing. A mock component
provides the interface that your component requires, responding to queries with simulated data. The mock
components form part of a complete test harness that you can connect to all the interfaces of the component.
A benefit of mock testing is that you can develop your component while the other components whose services it
will use are still under development.

Maintain the Relationships between Tests and Model


In a typical project that performs an iteration every few weeks, a requirements review is held near the beginning of
each iteration. The meeting discusses the features that are to be delivered in the next iteration. A requirements
model can be used to help discuss the concepts, scenarios, and sequences of actions that will be developed. The
business stakeholders set priorities, the developers make estimates, and the testers ensure the expected behavior
of each feature is captured correctly.
Writing tests is the most effective way to define a requirement, and is also an effective way to ensure that a person
has a clear understanding of what is required. However, whereas writing tests takes too long to do during a
specification workshop, creating models can be done much more rapidly.
From a testing point of view, a requirements model can be seen as a shorthand for the tests. Therefore, it's
important to maintain the relationship between tests and model throughout the project.

Attaching Test Cases to Model Elements


If your project uses Test Manager, you can link tests to the elements in your model. This lets you quickly find the
tests affected by a change in the requirements, and helps you track the extent to which a requirement has been
realized.
You can link tests to all kinds of element. Here are some examples:
Link a use case to the tests that exercise it.
Write the clauses of a use case postcondition, or goal, onto comments that are linked to the use case, and
then link tests to each comment.
Write invariant rules in comments on class diagrams or activity diagrams, and link them to tests.
Link tests to an activity diagram, or to individual activities.
Link a test suite to the component or subsystem it tests.
To link tests to a model element or relationship
1. In Test Manager, create a requirement and base a test suite on it.
The requirement that you create is a work item in Team Foundation Server. It might be a User Story,
Requirement, or Use Case work item, depending on the process template that your project uses with Team
Foundation. For more information, see Track work using Visual Studio Team Services or Team Foundation
Server.
2. Link the requirement work item to one or more elements in your model.
In a modeling diagram, right-click an element, comment or relationship and then click Link to Work Item.
3. Add to the test suite, test cases that verify the requirement expressed in the model element.

See Also
Create models for your app
Model user requirements
Model your app's architecture
Analyzing and Modeling Architecture
Validate code with dependency diagrams
10/18/2017 11 min to read Edit Online

Latest news: See this blog post.


Video: Validate your architecture dependencies in real time

Why use dependency diagrams?


To make sure that code doesn't conflict with its design, validate your code with dependency diagrams in Visual
Studio. This can help you:
Find conflicts between dependencies in your code and dependencies on the dependency diagram.
Find dependencies that might be affected by proposed changes.
For example, you can edit the dependency diagram to show potential architecture changes and then
validate the code to see the affected dependencies.
Refactor or migrate code to a different design.
Find code or dependencies that require work when you move the code to a different architecture.
Requirements
Visual Studio
Visual Studio on your Team Foundation Build server to validate code automatically with Team
Foundation Build
A solution that has a modeling project with a dependency diagram. This dependency diagram must be
linked to artifacts in Visual C# .NET or Visual Basic .NET projects that you want to validate. See Create
dependency diagrams from your code.
To see which versions of Visual Studio support this feature, see Version support for architecture and
modeling tools.
You can validate code manually from an open dependency diagram in Visual Studio or from a command
prompt. You can also validate code automatically when running local builds or Team Foundation Build.
See Channel 9 Video: Design and validate your architecture using dependency diagrams.

IMPORTANT
If you want to run layer validation with Team Foundation Build, you must also install the same version of Visual Studio on
your build server.

See if an item supports validation


Include other .NET assemblies and projects for validation
Validate code manually
Validate code automatically
Troubleshoot layer validation issues
Understand and resolve layer validation errors

Live dependency validation


In this release of Visual Studio, dependency validation occurs in real time, and errors are shown immediately in
the Visual Studio Error List window.
Live validation is supported for C# and Visual Basic.NET.
To enable full solution analysis when using live dependency validation, open the options settings from
the gold bar that appears in the Error List.
You can permanently dismiss this gold bar if you are not interested in seeing all the architectural
issues in your solution.
If you don't enable full solution analysis, the analysis is done only for the files being edited.
When upgrading projects to enable live validation, a dialog shows the progress of the conversion.
When updating a project for live dependency validation, the version of the NuGet package is upgraded to
be the same for all projects, and is the highest version in use.
Adding a new dependency validation project triggers a project update.

See if an item supports validation


You can link layers to Web sites, Office documents, plain text files, and files in projects that are shared across
multiple apps, but the validation process won't include them. Validation errors won't appear for references to
projects or assemblies that are linked to separate layers when no dependencies appear between those layers.
Such references are not considered dependencies unless the code uses those references.
1. On the dependency diagram, select one or more layers, right-click your selection, and then click View
Links.
2. In Layer Explorer, look at the Supports Validation column. If the value is false, the item does not
support validation.

Include other .NET assemblies and projects for validation


When you drag items to the dependency diagram, references to the corresponding .NET assemblies or projects
are added automatically to the Layer References folder in the modeling project. This folder contains references
to the assemblies and projects that are analyzed during validation. You can include other .NET assemblies and
projects for validation without manually dragging them to the dependency diagram.
1. In Solution Explorer, right-click the modeling project or the Layer References folder, and then click
Add Reference.
2. In the Add Reference dialog box, select the assemblies or projects, and then click OK.

Validate code manually


If you have an open dependency diagram that is linked to solution items, you can run the Validate shortcut
command from the diagram. You can also use the command prompt to run the msbuild command with the
/p:ValidateArchitecture custom property set to True. For example, as you make changes in the code, perform
layer validation regularly so that you can catch dependency conflicts early.
To validate code from an open dependency diagram
1. Right-click the diagram surface, and then click Validate Architecture.
NOTE
By default, the Build Action property on the dependency diagram (.layerdiagram) file is set to Validate so that
the diagram is included in the validation process.

The Error List window reports any errors that occur. For more information about validation errors, see
Understand and resolve layer validation errors.
2. To view the source of each error, double-click the error in the Error List window.

NOTE
Visual Studio might show a code map instead of the source of the error. This occurs when either the code has a
dependency on an assembly that is not specified by the dependency diagram, or the code is missing a
dependency that is specified by the dependency diagram. Review the code map or the code to determine whether
the dependency should exist. For more information about code maps, see Map dependencies across your
solutions.

3. To manage errors, see Manage validation errors.


To validate code at the command prompt
1. Open the Visual Studio command prompt.
2. Choose one of the following:
To validate code against a specific modeling project in the solution, run MSBuild with the following
custom property.

msbuild <FilePath+ModelProjectFileName>.modelproj /p:ValidateArchitecture=true

or -
Browse to the folder that contains the modeling project (.modelproj) file and the
dependency diagram and then run MSBuild with the following custom property:

msbuild /p:ValidateArchitecture=true

To validate code against all modeling projects in the solution, run MSBuild with the following
custom property:

msbuild <FilePath+SolutionName>.sln /p:ValidateArchitecture=true

or -
Browse to the solution folder, which must contain a modeling project that contains a
dependency diagram, and then run MSBuild with the following custom property:

msbuild /p:ValidateArchitecture=true

Any errors that occur will be listed. For more information about MSBuild, see MSBuild and
MSBuild Task.
For more information about validation errors, see Understand and resolve layer validation errors.
Manage validation errors
During the development process, you might want to suppress some of the reported conflicts during validation.
For example, you might want to suppress errors that you are already addressing or that are not relevant to your
particular scenario. When you suppress an error, it is a good practice to log a work item in Team Foundation.

WARNING
You must already be connected to TFS Source Code Control (SCC) to create or link to a work item. If you try to open a
connection to a different TFS SCC, Visual Studio closes the current solution automatically. Ensure that you are already
connected to the appropriate SCC before attempting to create or link to a work item. In later releases of Visual Studio, the
menu commands are not available if you are not connected to an SCC.

To c r e a t e a w o r k i t e m fo r a v a l i d a t i o n e r r o r

In the Error List window, right-click the error, point to Create Work Item, and then click the type of work
item that you want to create.
Use these tasks to manage validation errors in the Error List window:

TO FOLLOW THESE STEPS

Suppress selected errors during validation Right-click the one or multiple selected errors, point to
Manage Validation Errors, and then click Suppress Errors.

The suppressed errors appear with strikethrough formatting.


When you run validation the next time, these errors will not
appear.

Suppressed errors are tracked in a .suppressions file for the


corresponding dependency diagram file.

Stop suppressing selected errors Right-click the selected suppressed error or errors, point to
Manage Validation Errors, and then click Stop
Suppressing Errors.

The selected suppressed errors will appear when you run


validation the next time.

Restore all suppressed errors in the Error List window Right-click anywhere in the Error List window, point to
Manage Validation Errors, and then click Show All
Suppressed Errors.

Hide all suppressed errors from the Error List window Right-click anywhere in the Error List window, point to
Manage Validation Errors, and then click Hide All
Suppressed Errors.

Validate code automatically


You can perform layer validation every time that you run a local build. If your team uses Team Foundation Build,
you can perform layer validation with gated check-ins, which you can specify by creating a custom MSBuild task,
and use build reports to collect validation errors. To create gated check-in builds, see Use a gated check-in build
process to validate changes.
To validate code automatically during a local build
Use a text editor to open the modeling project (.modelproj) file, and then include the following property:

<ValidateArchitecture>true</ValidateArchitecture>
- or -
1. In Solution Explorer, right-click the modeling project that contains the dependency diagram or
diagrams, and then click Properties.
2. In the Properties window, set the modeling project's Validate Architecture property to True.
This includes the modeling project in the validation process.
3. In Solution Explorer, click the dependency diagram (.layerdiagram) file that you want to use for
validation.
4. In the Properties window, make sure that the diagram's Build Action property is set to Validate.
This includes the dependency diagram in the validation process.
To manage errors in the Error List window, see Manage Validation Errors.
To validate code automatically during a Team Foundation Build
1. In Team Explorer, double-click the build definition, and then click Process.
2. Under Build process parameters, expand Compilation, and type the following in the MSBuild
Arguments parameter:
/p:ValidateArchitecture=true

For more information about validation errors, see Understand and resolve layer validation errors. For
more information about Team Foundation Build, see:
Build the application
Use the Default Template for your build process
Modify a Legacy Build that is Based on UpgradeTemplate.xaml
Customize your build process template
Monitor Progress of a Running Build

Troubleshoot layer validation issues


The following table describes layer validation issues and their resolution. These issues differ from errors that
result from conflicts between the code and the design. For more information about these errors, see Understand
and resolve layer validation errors.

ISSUE POSSIBLE CAUSE RESOLUTION

Validation errors do not occur as Validation does not work on Add a new dependency diagram to the
expected. dependency diagrams that are copied modeling project.
from other dependency diagrams in
Solution Explorer and that are in the Copy the elements from the source
same modeling project. dependency dependency diagram to the new
diagrams that are copied in this way diagram.
contain the same references as the
original dependency diagram.

Understanding and Resolving Layer Validation Errors


When you validate code against a dependency diagram, validation errors occur when the code conflicts with the
design. For example, the following conditions might cause validation errors to occur:
An artifact is assigned to the wrong layer. In this case, move the artifact.
An artifact, such as a class, uses another class in a way that conflicts with your architecture. In this case,
refactor the code to remove the dependency.
To resolve these errors, update the code until no more errors appear during validation. You can perform
this task in an iterative manner.
The following section describes the syntax that is used in these errors, explains the meaning of these
errors, and suggests what you can do to resolve or manage them.

SYNTAX DESCRIPTION

ArtifactN(ArtifactTypeN) ArtifactN is an artifact that is associated with a layer on the


dependency diagram.

ArtifactTypeN is the type of ArtifactN, such as a Class or


Method, for example:

MySolution.MyProject.MyClass.MyMethod(Method)

NamespaceNameN The name of a namespace.

LayerNameN The name of a layer on the dependency diagram.

DependencyType The type of dependency relationship between Artifact1 and


Artifact2. For example, Artifact1 has a Calls relationship with
Artifact2.

ERROR SYNTAX ERROR DESCRIPTION

DV0001: Invalid Dependency This issue is reported when a code element (namespace,
type, member) mapped to a Layer references a code element
mapped to another layer, but there is no dependency arrow
between these layers in the dependency validation diagram
containing this layers. This is a dependency constraint
violation.

DV1001: Invalid namespace name This issue is reported on a code element associated with a
layer which "Allowed Namespace Names" property does not
contain the namespace in which this code element is defined.
This is a naming constraint violation. Note that the syntax of
"Allowed Namespace Names" is to be a semi-colon list of
namespaces in which code elements associated with are
layer are permitted to be defined.

DV1002: Dependency on unreferenceable namespace This issue is reported on a code element associated with a
layer and referencing another code element defined in a
namespace which is defined in the "Unreferenceable
Namespace" property of the layer. This is a naming
constraint violation. Note that the "Unreferenceable
Namespaces" property is defined as a Semi-colon separated
list of namespaces that should not be referenced in code
elements associated with this layer.
ERROR SYNTAX ERROR DESCRIPTION

DV1003: Disallowed namespace name This issue is reported on a code element associated with a
layer which "Disallowed Namespace Names" property
contains the namespace in which this code element is
defined. This is a naming constraint violation. Note that the
"Disallowed namespace name" property is defined as a Semi-
colon separated list of namespaces in which code elements
associated with this Layer should not be defined.

DV3001: Missing Link Layer 'LayerName' links to 'Artifact' which cannot be found.
Are you missing an assembly reference?

DV9001: Architectural analysis found internal errors Results might not be complete. For more information, see
the detailed build event log or output window.

See Also
Validate your system during development
Video: Validate your architecture dependencies in real time
Extend dependency diagrams
11/7/2017 1 min to read Edit Online

You can write code to create and update dependency diagrams, and to validate the structure of your program code
against dependency diagrams in Visual Studio. You can add commands that appear in the shortcut (context) menu
of the diagrams, customize drag-and-drop gestures, and access the layer model from text templates. You can
package these extensions into a Visual Studio Integration Extension (VSIX) and distribute them to other Visual
Studio users.
For more information about dependency diagrams, see:
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Create dependency diagrams from your code
Validate code with dependency diagrams

Requirements
You must have the following installed on the computer where you want to develop your layer extensions:
Visual Studio
Visual Studio SDK
Modeling SDK for Visual Studio

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

You must have a suitable version of Visual Studio installed on the computer where you want to run your layer
extensions. For more information, see Deploy a layer model extension.
To see which versions of Visual Studio support dependency diagrams, see Version support for architecture and
modeling tools.

In This Section
Add commands and gestures to dependency diagrams
Add custom architecture validation to dependency diagrams
Add custom properties to dependency diagrams
Navigate and update layer models in program code
Deploy a layer model extension
Troubleshoot extensions for dependency diagrams

See Also
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Create dependency diagrams from your code
Validate code with dependency diagrams
Add commands and gestures to dependency
diagrams
11/7/2017 6 min to read Edit Online

You can define context menu commands and gesture handlers on dependency diagrams in Visual Studio. You can
package these extensions into a Visual Studio Integration Extension (VSIX) that you can distribute to other Visual
Studio users.
You can define several command and gesture handlers in the same Visual Studio project if you want. You can also
combine several such projects into one VSIX. For example, you could define a single VSIX that includes layer
commands, and a domain-specific language.

NOTE
You can also customize architecture validation, in which users' source code is compared with dependency diagrams. You
should define architecture validation in a separate Visual Studio project. You can add it to the same VSIX as other extensions.
For more information, see Add custom architecture validation to dependency diagrams.

Requirements
See Requirements.

Defining a Command or Gesture in a New VSIX


The quickest method of creating an extension is to use the project template. This places the code and the VSIX
manifest into the same project.
To define an extension by using a project template
1. Create a project in a new solution, by using the New Project command on the File menu.
2. In the New Project dialog box, under Modeling Projects, select either Layer Designer Command
Extension or Layer Designer Gesture Extension.
The template creates a project that contains a small working example.
3. To test the extension, press CTRL+F5 or F5.
An experimental instance of Visual Studio starts. In this instance, create a dependency diagram. Your
command or gesture extension should work in this diagram.
4. Close the experimental instance and modify the sample code. For more information, see Navigate and
update layer models in program code.
5. You can add more command or gesture handlers to the same project. For more information, see one of the
following sections:
Defining a Menu Command
Defining a Gesture Handler
6. To install the extension in the main instance of Visual Studio, or on another computer, find the .vsix file in
bin\\. Copy it to the computer where you want to install it, and then double-click it. To uninstall it, use
**Extensions and Updates* on the Tools menu.
Adding a Command or Gesture to a separate VSIX
If you want to create one VSIX that contains commands, layer validators, and other extensions, we recommend that
you create one project to define the VSIX, and separate projects for the handlers.
To add layer extensions to a separate VSIX
1. Create a Class Library project in a new or existing Visual Studio solution. In the New Project dialog box,
click Visual C# and then click Class Library. This project will contain command or gesture handler classes.

NOTE
You can define more than one command or gesture handler class in one class library, but you should define layer
validation classes in a separate class library.

2. Identify or create a VSIX project in your solution. A VSIX project contains a file that is named
source.extension.vsixmanifest. To add a VSIX project:
a. In the New Project dialog box, expand Visual C#, then click Extensibility, and then click VSIX
Project.
b. In Solution Explorer, right-click the VSIX project and then click Set as Startup Project.
c. Click Select Editions and make sure that Visual Studio is checked.
3. In source.extension.vsixmanifest, under Assets, add the command or gesture handler project as a MEF
component.
a. In the Assets.tab, choose New.
b. At Type, select Microsoft.VisualStudio.MefComponent.
c. At Source, select Project in current solution and select the name of your command or gesture
handler project.
d. Save the file.
4. Return to the command or gesture handler project, and add the following project references.

REFERENCE WHAT THIS ALLOWS YOU TO DO

Program Files\Microsoft Visual Studio Create and edit layers


[version]\Common7\IDE\Extensions\Microsoft\Architecture
Tools\ExtensibilityRuntime\Microsoft.VisualStudio.Architecture
Tools.Extensibility.Layer.dll

Microsoft.VisualStudio.Uml.Interfaces Create and edit layers

Microsoft.VisualStudio.ArchitectureTools.Extensibility Modify shapes on diagrams

System.ComponentModel.Composition Define components using Managed Extensibility Framework


(MEF)

Microsoft.VisualStudio.Modeling.Sdk.[version] Define modeling extensions

Microsoft.VisualStudio.Modeling.Sdk.Diagrams.[version] Update shapes and diagrams

1. Edit the class file in the C# class library project to contain the code for your extension. For more information,
see one of the following sections:
Defining a Menu Command
Defining a Gesture Handler
See also Navigate and update layer models in program code.
2. To test the feature, press CTRL+F5 or F5. An experimental instance of Visual Studio opens. In this instance,
create or open a dependency diagram.
3. To install the VSIX in the main instance of Visual Studio, or on another computer, find the .vsix file in the
bin directory of the VSIX project. Copy it to the computer where you want to install the VSIX. Double-click
the VSIX file in Windows Explorer (File Explorer in Windows 8).
To uninstall it, use Extensions and Updates on the Tools menu.

Defining a Menu Command


You can add more menu command definitions to an existing gesture or command project. Each command is
defined by a class that has the following characteristics:
The class is declared as follows:
[LayerDesignerExtension]

[Export(typeof(ICommandExtension))]

public class MyLayerCommand : ICommandExtension { ... }

The namespace and the name of the class are unimportant.


The methods that implement ICommandExtension are as follows:
string Text {get;} - The label that appears in the menu.
void QueryStatus(IMenuCommand command) - called when the user right-clicks the diagram, and
determines whether the command should be visible and enabled for the user's current selection.
void Execute(IMenuCommand command) - called when the user selects the command.
To determine the current selection, you can import IDiagramContext :
[Import]

public IDiagramContext DiagramContext { get; set; }

...

DiagramContext.CurrentDiagram.SelectedShapes.Count()...

For more information, see Navigate and update layer models in program code.
To add a new command, create a new code file that contains the following sample. Then test and edit it.

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using System.ComponentModel.Composition;
using System.Linq;

namespace MyLayerExtension // Change to your preference.


{
// This is a feature for dependency diagrams:
[LayerDesignerExtension]
[LayerDesignerExtension]
// This feature is a menu command:
[Export(typeof(ICommandExtension))]
// Change the class name to your preference:
public class MyLayerCommand : ICommandExtension
{
[Import]
public IDiagramContext DiagramContext { get; set; }

[Import]
public ILinkedUndoContext LinkedUndoContext { get; set; }

// Menu command label:


public string Text
{
get { return "Duplicate layers"; }
}

// Called when the user right-clicks the diagram.


// Defines whether the command is visible and enabled.
public void QueryStatus(IMenuCommand command)
{
command.Visible =
command.Enabled = DiagramContext.CurrentDiagram
.SelectedShapes.Count() > 0;
}

// Called when the user selects the command.


public void Execute(IMenuCommand command)
{
// A selection of starting points:
IDiagram diagram = this.DiagramContext.CurrentDiagram;
ILayerModel lmodel = diagram.GetLayerModel();
foreach (ILayer layer in lmodel.Layers)
{ // All layers in model.
}
// Updates should be performed in a transaction:
using (ILinkedUndoTransaction t =
LinkedUndoContext.BeginTransaction("copy selection"))
{
foreach (ILayer layer in
diagram.SelectedShapes
.Select(shape=>shape.GetLayerElement())
.Where(element => element is ILayer))
{
ILayer copy = lmodel.CreateLayer(layer.Name + "+");
// Position the shapes:
IShape originalShape = layer.GetShape();
copy.GetShape().Move(
originalShape.XPosition + originalShape.Width * 1.2,
originalShape.YPosition);
}
t.Commit();
}
}
}
}

Defining a Gesture Handler


A gesture handler responds when the user drags items onto the dependency diagram, and when the user double-
clicks anywhere in the diagram.
To your existing command or gesture handler VSIX project, you can add a code file that defines a gesture handler:
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using System.ComponentModel.Composition;
using System.Linq;
namespace MyLayerExtensions // change to your preference
{
[LayerDesignerExtension]
[Export(typeof(IGestureExtension))]
public class MyLayerGestureHandler : IGestureExtension
{
}
}

Notice the following points about gesture handlers:


The members of IGestureExtension are as follows:
OnDoubleClick - called when the user double-clicks anywhere on the diagram.
CanDragDrop - called repeatedly as the user moves the mouse while dragging an item onto the diagram. It
must work quickly.
OnDragDrop - called when the user drops an item onto the diagram.
The first argument to each method is an IShape , from which you can get the layer element. For example:

public void OnDragDrop(IShape target, IDataObject data)


{
ILayerElement element = target.GetLayerElement();
if (element is ILayer)
{
// ...
}
}

Handlers for some types of dragged item are already defined. For example, the user can drag items from
Solution Explorer onto a dependency diagram. You cannot define a drag handler for these types of item. In
these cases, your DragDrop methods will not be invoked.

See Also
Navigate and update layer models in program code
Add custom architecture validation to dependency diagrams
Add custom architecture validation to dependency
diagrams
10/18/2017 8 min to read Edit Online

In Visual Studio, users can validate the source code in a project against a layer model so that they can verify that
the source code conforms to the dependencies on a dependency diagram. There is a standard validation
algorithm, but you can define your own validation extensions.
When the user selects the Validate Architecture command on a dependency diagram, the standard validation
method is invoked, followed by any validation extensions that have been installed.

NOTE
In a dependency diagram, the main purpose of validation is to compare the diagram with the program code in other parts
of the solution.

You can package your layer validation extension into a Visual Studio Integration Extension (VSIX), which you can
distribute to other Visual Studio users. You can either place your validator in a VSIX by itself, or you can combine it
in the same VSIX as other extensions. You should write the code of the validator in its own Visual Studio project,
not in the same project as other extensions.

WARNING
After you have created a validation project, copy the example code at the end of this topic and then edit that to your own
needs.

Requirements
See Requirements.

Defining a Layer Validator in a New VSIX


The quickest method of creating a validator is to use the project template. This places the code and the VSIX
manifest into the same project.
To define an extension by using a project template
1. Create a project in a new solution, by using the New Project command on the File menu.
2. In the New Project dialog box, under Modeling Projects, select Layer Designer Validation Extension.
The template creates a project that contains a small example.
WARNING
To makethe template work properly:
Edit calls to LogValidationError to remove the optional arguments errorSourceNodes and
errorTargetNodes .
If you use custom properties, apply the update mentioned in Add custom properties to dependency
diagrams.

3. Edit the code to define your validation. For more information, see Programming Validation.
4. To test the extension, see Debugging Layer Validation.

NOTE
Your method will be called only in specific circumstances, and breakpoints will not work automatically. For more
information, see Debugging Layer Validation.

5. To install the extension in the main instance of Visual Studio, or on another computer, find the .vsix file in
bin\\. Copy it to the computer where you want to install it, and then double-click it. To uninstall it, use
**Extensions and Updates* on the Tools menu.

Adding a Layer Validator to a Separate VSIX


If you want to create one VSIX that contains layer validators, commands, and other extensions, we recommend
that you create one project to define the VSIX, and separate projects for the handlers.
To add layer validation to a separate VSIX
1. Create a Class Library project in a new or existing Visual Studio solution. In the New Project dialog box,
click Visual C# and then click Class Library. This project will contain the layer validation class.
2. Identify or create a VSIX project in your solution. A VSIX project contains a file that is named
source.extension.vsixmanifest. If you have to add a VSIX project, follow these steps:
a. In the New Project dialog box, choose Visual C#, Extensibility, VSIX Project.
b. In Solution Explorer, on the shortcut menu of the VSIX project, Set as Startup Project.
3. In source.extension.vsixmanifest, under Assets, add the layer validation project as a MEF component:
a. Choose New.
b. In the Add New Asset dialog box, set:
Type = Microsoft.VisualStudio.MefComponent
Source = A project in current solution
Project = your validator project
4. You must also add it as a layer validation:
a. Choose New.
b. In the Add New Asset dialog box, set:
Type = Microsoft.VisualStudio.ArchitectureTools.Layer.Validator. This is not one of the
options in the drop-down list. You must enter it from the keyboard.
Source = A project in current solution
Project = your validator project
5. Return to the layer validation project, and add the following project references:

REFERENCE WHAT THIS ALLOWS YOU TO DO

Microsoft.VisualStudio.GraphModel.dll Read the architecture graph

Microsoft.VisualStudio.ArchitectureTools.Extensibility.Code Read the code DOM associated with layers


Schema.dll

Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer Read the Layer model


.dll

Microsoft.VisualStudio.ArchitectureTools.Extensibility Read and update shapes and diagrams.

System.ComponentModel.Composition Define the validation component using Managed


Extensibility Framework (MEF)

Microsoft.VisualStudio.Modeling.Sdk.[version] Define modeling extensions

6. Copy the example code at the end of this topic into the class file in the validator library project to contain
the code for your validation. For more information, see Programming Validation.
7. To test the extension, see Debugging Layer Validation.

NOTE
Your method will be called only in specific circumstances, and breakpoints will not work automatically. For more
information, see Debugging Layer Validation.

8. To install the VSIX in the main instance of Visual Studio, or on another computer, find the .vsix file in the
bin directory of the VSIX project. Copy it to the computer where you want to install the VSIX. Double-click
the VSIX file in Windows Explorer. (File Explorer in Windows 8.)
To uninstall it, use Extensions and Updates on the Tools menu.

Programming Validation
To define a layer validation extension, you define a class that has the following characteristics:
The overall form of the declaration is as follows:
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.CodeSchema;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;
using Microsoft.VisualStudio.GraphModel;
...
[Export(typeof(IValidateArchitectureExtension))]
public partial class Validator1Extension :
IValidateArchitectureExtension
{
public void ValidateArchitecture(Graph graph)
{
GraphSchema schema = graph.DocumentSchema;
...
} }

When you discover an error, you can report it by using LogValidationError() .

WARNING
Do not use the optional parameters of LogValidationError .

When the user invokes the Validate Architecture menu command, the layer runtime system analyses the
layers and their artifacts to produce a graph. The graph has four parts:
The layer models of the Visual Studio solution that are represented as nodes and links in the graph.
The code, project items, and other artifacts that are defined in the solution and represented as nodes, and
links that represent the dependencies discovered by the analysis process.
Links from the layer nodes to the code artifact nodes.
Nodes that represent errors discovered by the validator.
When the graph has been constructed, the standard validation method is called. When this is complete, any
installed extension validation methods are called in unspecified order. The graph is passed to each
ValidateArchitecture method, which can scan the graph and report any errors that it finds.

NOTE
This is not the same as the validation process that can be used in domain-specific languages.

Validation methods should not change the layer model or the code that is being validated.
The graph model is defined in Microsoft.VisualStudio.GraphModel. Its principal classes are GraphNode and
GraphLink.
Each Node and each Link has one or more Categories which specify the type of element or relationship that it
represents. The nodes of a typical graph have the following categories:
Dsl.LayerModel
Dsl.Layer
Dsl.Reference
CodeSchema_Type
CodeSchema_Namespace
CodeSchema_Type
CodeSchema_Method
CodeSchema_Field
CodeSchema_Property
Links from layers to elements in the code have the category "Represents".

Debugging Validation
To debug your layer validation extension, press CTRL+F5. An experimental instance of Visual Studio opens. In this
instance, open or create a layer model. This model must be associated with code, and must have at least one
dependency.
Test with a Solution that contains Dependencies
Validation is not executed unless the following characteristics are present:
There is at least one dependency link on the dependency diagram.
There are layers in the model that are associated with code elements.
The first time that you start an experimental instance of Visual Studio to test your validation extension,
open or create a solution that has these characteristics.
Run Clean Solution before Validate Architecture
Whenever you update your validation code, use the Clean Solution command on the Build menu in the
experimental solution, before you test the Validate command. This is necessary because the results of validation
are cached. If you have not updated the test dependency diagram or its code, the validation methods will not be
executed.
Launch the Debugger Explicitly
Validation runs in a separate process. Therefore, the breakpoints in your validation method will not be triggered.
You must attach the debugger to the process explicitly when validation has started.
To attach the debugger to the validation process, insert a call to System.Diagnostics.Debugger.Launch() at the start
of your validation method. When the debugging dialog box appears, select the main instance of Visual Studio.
Alternatively, you can insert a call to System.Windows.Forms.MessageBox.Show() . When the message box appears, go
to the main instance of Visual Studio and on the Debug menu click Attach to Process. Select the process that is
named Graphcmd.exe.
Always start the experimental instance by pressing CTRL+F5 (Start without Debugging).
Deploying a Validation Extension
To install your validation extension on a computer on which a suitable version of Visual Studio is installed, open
the VSIX file on the target computer. To install on a computer on which Team Foundation Build is installed, you
must manually extract the VSIX contents into an Extensions folder. For more information, see Deploy a layer
model extension.

Example code
using System;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.CodeSchema;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;
using Microsoft.VisualStudio.GraphModel;

namespace Validator3
{
[Export(typeof(IValidateArchitectureExtension))]
public partial class Validator3Extension : IValidateArchitectureExtension
{
/// <summary>
/// Validate the architecture
/// </summary>
/// <param name="graph">The graph</param>
public void ValidateArchitecture(Graph graph)
{
if (graph == null) throw new ArgumentNullException("graph");

// Uncomment the line below to debug this extension during validation


// System.Windows.Forms.MessageBox.Show("Attach 2 to GraphCmd.exe with process id " +
System.Diagnostics.Process.GetCurrentProcess().Id);

// Get all layers on the diagram


foreach (GraphNode layer in graph.Nodes.GetByCategory("Dsl.Layer"))
{
System.Threading.Thread.Sleep(100);
// Get the required regex property from the layer node
string regexPattern = "^[a-zA-Z]+$"; //layer[customPropertyCategory] as string;
if (!string.IsNullOrEmpty(regexPattern))
{
Regex regEx = new Regex(regexPattern);

// Get all referenced types in this layer including those from nested layers so each
// type is validated against all containing layer constraints.
foreach (GraphNode containedType in layer.FindDescendants().Where(node =>
node.HasCategory("CodeSchema_Type")))
{
// Check the type name against the required regex
CodeGraphNodeIdBuilder builder = new CodeGraphNodeIdBuilder(containedType.Id, graph);
string typeName = builder.Type.Name;
if (!regEx.IsMatch(typeName))
{
// Log an error
string message = string.Format(CultureInfo.CurrentCulture,
Resources.InvalidTypeNameMessage, typeName);
this.LogValidationError(graph, typeName + "TypeNameError", message,
GraphErrorLevel.Error, layer);
}
}
}

}
}
}

See Also
Extend dependency diagrams
Add custom properties to dependency diagrams
10/18/2017 3 min to read Edit Online

When you write extension code for dependency diagrams, you can store values with any element on a dependency
diagram. The values will persist when the diagram is saved and re-opened. You can also have these properties
appear in the Properties window so that users can see and edit them. For example, you could let users specify a
regular expression for each layer, and write validation code to verify that the names of classes in each layer
conform to the pattern specified by the user.

Properties not visible to the user


If you just want your code to attach values to any element in a dependency diagram, you don't need to define a
MEF component. There is a dictionary named Properties in ILayerElement. Simply add marshalable values to the
dictionary of any layer element. They will be saved as part of the dependency diagram. For more information, see
Navigate and update layer models in program code.

Properties that the user can edit


Initial preparation

IMPORTANT
To make properties appear, you must make the following change on each computer where you want layer properties to be
visible.
1. Run Notepad by using Run as Administrator. Open
%ProgramFiles%\Microsoft Visual Studio [version]\Common7\IDE\Extensions\Microsoft\Architecture
Tools\ExtensibilityRuntime\extension.vsixmanifest

a. Inside the Content element, add:

<MefComponent>Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer.Provider.dll</MefComponen
t>

a. Under the Visual Studio Tools section of the Visual Studio application start menu, open Developer Command
Prompt.
Enter:
devenv /rootSuffix /updateConfiguration

devenv /rootSuffix Exp /updateConfiguration

a. Restart Visual Studio.

Make sure your code is in a VSIX project


If your property is part of a command, gesture, or validation project, you don't need to add anything. The code for
your custom property should be defined in a Visual Studio Extensibility project defined as a MEF component. For
more information, see Add commands and gestures to dependency diagrams or Add custom architecture
validation to dependency diagrams.
Define the custom property
To create a custom property, define a class like this:
[Export(typeof(IPropertyExtension))]
public class MyProperty
: PropertyExtension<ILayerElement>
{
// Implement the interface.
}

You can define properties on ILayerElement or any of its derived classes, which include:
ILayerModel - the model
ILayer - each layer
ILayerDependencyLink - the links between layers
ILayerComment

ILayerCommentLink

Example
The following code is a typical custom property descriptor. It defines a Boolean property on the layer model (
ILayerModel ) that lets the user provide values for a custom validation method.

using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer;

namespace MyNamespace
{
/// <summary>
/// Custom properties are added to the Layer Designer via a custom
/// Property Descriptor. We have to export this Property Descriptor
/// using MEF to make it available in the Layer Designer.
/// </summary>
[Export(typeof(IPropertyExtension))]
public class AllTypesMustBeReferencedProperty
: PropertyExtension<ILayerModel>
{
/// <summary>
/// Each custom property must have a unique name.
/// Usually we use the full name of this class.
/// </summary>
public static readonly string FullName =
typeof(AllTypesMustBeReferencedProperty).FullName;

/// <summary>
/// Construct the property. Notice the use of FullName.
/// </summary>
public AllTypesMustBeReferencedProperty()
: base(FullName)
{ }

/// <summary>
/// The display name is shown in the Properties window.
/// We therefore use a localizable resource.
/// </summary>
public override string DisplayName
{
get { return Strings.AllTypesMustBeReferencedDisplayName; }
}

/// <summary>
/// Description shown at the bottom of the Properties window.
/// We use a resource string for easier localization.
/// </summary>
public override string Description
{
get { return Strings.AllTypesMustBeReferencedDescription; }
}

/// <summary>
/// This is called to set a new value for this property. We must
/// throw an exception if the value is invalid.
/// </summary>
/// <param name="component">The target ILayerElement</param>
/// <param name="value">The new value</param>
public override void SetValue(object component, object value)
{
ValidateValue(value);
base.SetValue(component, value);
}
/// <summary>
/// Helper to validate the value.
/// </summary>
/// <param name="value">The value to validate</param>
private static void ValidateValue(object value)
{ }

public override Type PropertyType


{ get { return typeof(bool); } }

/// <summary>
/// The segment label of the properties window.
/// </summary>
public override string Category
{
get
{
return Strings.AllTypesMustBeReferencedCategory;
}
}
}
}

See Also
Extend dependency diagrams
Navigate and update layer models in program code
11/7/2017 3 min to read Edit Online

This topic describes the elements and relationships in layer models, which you can navigate and update by using
program code. For more information about dependency diagrams from the user's point of view, see Dependency
Diagrams: Reference and Dependency Diagrams: Guidelines.
The Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer model described in this topic is a facade on a
more general Microsoft.VisualStudio.GraphModel model. If you are writing a menu command or gesture
extension, use the Layer model. If you are writing a layer validation extension, it is easier to use the GraphModel .

Transactions
When you update a model, consider enclosing the changes in a ILinkedUndoTransaction . This groups your
changes into one transaction. If any of the changes fails, the whole transaction will be rolled back. If the user
undoes a change, all the changes will be undone together.

using (ILinkedUndoTransaction t =
LinkedUndoContext.BeginTransaction("a name"))
{
// Make changes here ....
t.Commit(); // Don't forget this!
}

Containment

Layers (ILayer) and the layer model (ILayerModel) can contain Comments and Layers.
A layer ( ILayer ) can be contained in a layer model ( ILayerModel ) or it can be nested within another ILayer .
To create a comment or a layer, use the creation methods on the appropriate container.

Dependency Links
A dependency link is represented by an object. It can be navigated in either direction:
To create a dependency link, call source.CreateDependencyLink(target) .

Comments
Comments can be contained inside layers or the layer model, and can also be linked to any layer element:

A comment can be linked to any number of elements, including none.


To get the comments that are attached to a layer element, use:

ILayerModel model = diagram.GetLayerModel();


IEnumerable<ILayerComment> comments =
model.Comments.Where(comment =>
comment.Links.Any(link => link.Target == layerElement));

Cau t i on

The Comments property of an ILayer gets comments that are contained within the ILayer . It does not get the
comments that are linked to it.
Create a comment by invoking CreateComment() on the appropriate container.
Create a link by using CreateLink() on the comment.

Layer Elements
All the types of element that can be contained in a model are layer elements:
Properties
Each ILayerElement has a string dictionary named Properties . You can use this dictionary to attach arbitrary
information to any layer element.

Artifact References
An artifact reference (ILayerArtifactReference) represents the link between a layer and a project item such as a file,
class, or folder. The user creates artifacts when they create a layer or add to it by dragging items from Solution
Explorer, Class View, or Object Browser onto a dependency diagram. Any number of artifact references can be
linked to a layer.
Each row in Layer Explorer displays an artifact reference. For more information, see Create dependency diagrams
from your code.
The principal types and methods concerned with artifact references are as follows:
ILayerArtifactReference. The Categories property indicates what kind of artifact is referenced, such as a class,
executable file, or assembly. Categories determines how the Identifier identifies the target artifact.
CreateArtifactReferenceAsync creates an artifact reference from an Project or ProjectItem. This is an asynchronous
operation. Therefore, you usually provide a callback that is called when the creation is complete.
Layer Artifact References should not be confused with Artifacts in use case diagrams.

Shapes and Diagrams


Two objects are used to represent each element in a layer model: an ILayerElement, and an IShape. The IShape
represents the position and size of the shape on the diagram. In layer models, every ILayerElement has one
IShape , and every IShape on a dependency diagram has one ILayerElement . IShape is also used for UML
models. Therefore, not every IShape has a layer element.
In the same manner, the ILayerModel is displayed on one IDiagram.
In the code of a custom command or gesture handler, you can get the current diagram and the current selection
of shapes from the DiagramContext import:
public class ... {
[Import]
public IDiagramContext DiagramContext { get; set; }
...
public void ... (...)
{ IDiagram diagram = this.DiagramContext.CurrentDiagram;
ILayerModel model = diagram.GetLayerModel();
if (model != null)
{ foreach (ILayer layer in model.Layers) { ... }}
foreach (IShape selected in diagram.SelectedShapes)
{ ILayerElement element = selected.GetLayerElement();
if (element != null) ... }}

IShape and IDiagram are also used to display UML models.

See Also
Add commands and gestures to dependency diagrams
Add custom architecture validation to dependency diagrams
Add custom properties to dependency diagrams
Dependency Diagrams: Reference
Dependency Diagrams: Guidelines
Deploy a layer model extension
10/18/2017 1 min to read Edit Online

Other users of Visual Studio can install layer modeling extensions that you create by using Visual Studio.

Installing your extension


Your extension is compiled to a VSIX file, which you can install on other computers. You can also install it on your
development computer, to make the extension available in the main instance of Visual Studio.
To install the extension
1. In the project that contains source.vsix.manifest, open bin\\* in File Explorer.
2. Copy the *.vsix file to the computer on which you want to install the extension.
3. On the target computer, double-click the *.vsix file in Windows Explorer.
The VSIX installer opens.
To uninstall the extension
1. In Visual Studio, on the Tools menu, click Extensions and Updates.
2. Click the name of the extension and then click Uninstall.

Installing an Extension on a Team Foundation Build Server


Team Foundation Build servers do not normally have Visual Studio installed, and so you cannot install the VSIX by
double-clicking it. The installation of Team Foundation Build includes some components that allow a VSIX
extension to run, but you must install the extension manually.
To install your layer extension on a Team Foundation Build Server
1. Copy the .vsix files from your development computer to the Team Foundation Build computer.
Place the VSIX file in one of the following locations:
To install for all users and services:
%ProgramFiles%\Microsoft Visual Studio [version]\Common7\IDE\Extensions\Microsoft
To install only for the network service that runs Team Foundation Build:
%WinDir%\ServiceProfiles\NetworkService\AppData\Local\Microsoft\VisualStudio\
[version]\Extensions\Microsoft
If you have configured Team Foundation Build to run in interactive mode as a particular user, you
can install just for that user:
%LocalAppData%\Microsoft\VisualStudio\[version]\Extensions\Microsoft

NOTE
%LocalAppData% is typically DriveName:UsersUserNameAppDataLocal.

2. Expand each VSIX file into a folder in the same location:


a. Change the file name extension from .vsix to .zip.
b. Extract the content of the .zip file to a folder.
c. Delete the .zip file
3. Restart Team Foundation Build.
Troubleshoot extensions for dependency diagrams
10/18/2017 1 min to read Edit Online

This topic addresses some problems that you might encounter when you create layer model extensions.
When I press F5 to debug my extension, my commands, gesture handlers, validation extensions, or custom properties do not appear
on dependency diagrams in the Experimental instance of Visual Studio
1. Open your extension solution in the Experimental instance of Visual Studio, and on the Build menu, click
Rebuild Solution.
2. Press F5 or CTRL+F5 to start the experimental instance of Visual Studio. Open a dependency diagram and
test your extension.
Continue with the next procedure if necessary.
An old version of my extension runs.
1. Make sure that no experimental instance of Visual Studio is running.
2. Delete the following folder: %LocalAppData%\Microsoft\VisualStudio\[version]\ComponentModelCache

NOTE
%LocalAppData% is typically DriveName:\Users\UserName\AppData\Local.

Continue with the next procedure if necessary.


An old version of my validation results appears, or my validation method is not called.
1. In the experimental instance of Visual Studio, on the Build menu, click Clean Solution. This clears the
cached results of the previous validation analysis.
2. Make sure that the layers in your model are associated with code elements, and that there is at least one
dependency link in the model. Validation is not invoked if there is nothing to validate.
3. Regular breakpoints might not work in a validation method, because it runs in a separate process. You must
insert a call to System.Diagnostics.Debugger.Launch() if you want to step through your method.
4. In source.extension.vsixmanifest in your layer validation project, make sure that you have added both a
MEF Component item and a Custom Extension Type item under Content.

See Also
Extend dependency diagrams
Modeling SDK for Visual Studio - Domain-Specific
Languages
10/18/2017 2 min to read Edit Online

By using the Modeling SDK for Visual Studio, you can create powerful model-based development tools that you
can integrate into Visual Studio. In the same manner, you can create one or more model definitions and integrate
them into a set of tools.
At the heart of MSDK is the definition of a model that you create to represent concepts in your business area. You
can surround the model with a variety of tools, such as a diagrammatic view, the ability to generate code and other
artifacts, commands for transforming the model, and the ability to interact with code and other objects in Visual
Studio. As you develop the model, you can combine it with other models and tools to form a powerful toolset that
is centered on your development.
MSDK lets you develop a model quickly in the form of a domain-specific language (DSL). You begin by using a
specialized editor to define a schema or abstract syntax together with a graphical notation. From this definition,
VMSDK generates:
A model implementation with a strongly-typed API that runs in a transaction-based store.
A tree-based explorer.
A graphical editor in which users can view the model or parts of it that you define.
Serialization methods that save your models in readable XML.
Facilities for generating program code and other artifacts using text templating.
You can customize and extend all of these features. Your extensions are integrated in such a way that you
can still update the DSL definition and re-generate the features without losing your extensions.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Related blog posts


For guidance on advanced techniques and troubleshooting, visit Visual Studio DSL & Modeling Tools Extensibility
forum.

In This Section
Getting Started with Domain-Specific Languages
Understanding Models, Classes and Relationships
How to Define a Domain-Specific Language
Customizing and Extending a Domain-Specific Language
Validation in a Domain-Specific Language
Writing Code to Customise a Domain-Specific Language
Generating Code from a Domain-Specific Language
Understanding the DSL Code
Customizing File Storage and XML Serialization
Deploying Domain-Specific Language Solutions
Creating a Windows Forms-Based Domain-Specific Language
Creating a WPF-Based Domain-Specific Language
How to: Extend the Domain-Specific Language Designer
Supported Visual Studio Editions for Visualization & Modeling SDK
How to: Migrate a Domain-Specific Language to a New Version
API Reference for Modeling SDK for Visual Studio
Getting Started with Domain-Specific Languages
10/18/2017 17 min to read Edit Online

This topic explains the basic concepts in defining and using a domain-specific language (DSL) created with the
Modeling SDK for Visual Studio.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

If you are new to DSLs, we recommend that you work through the DSL Tools Lab, which you can find in this site:
Visualizaton and Modeling SDK

What can you do with a Domain-Specific Language?


A domain-specific language is a notation, usually graphical, that is designed to be used for a particular purpose. By
contrast, languages such as UML are general-purpose. In a DSL, you can define the types of model element and
their relationships, and how they are presented on the screen.
When you have designed a DSL, you can distribute it as part of a Visual Studio Integration Extension (VSIX)
package. Users work with the DSL in Visual Studio:

The notation is only part of a DSL. Together with the notation, your VSIX package includes tools that users can
apply to help them edit and generate material from their models.
One of the principal applications of DSLs is to generate program code, configuration files, and other artifacts.
Especially in large projects and product lines, where several variants of a product will be created, generating many
of the variable aspects from DSLs can provide a large increase in reliability and a very rapid response to
requirements changes.
The rest of this overview is a walkthrough that introduces the basic operations of creating and using a domain-
specific language in Visual Studio.

Prerequisites
To define a DSL, you must have installed the following components:

Visual Studio http://go.microsoft.com/fwlink/?LinkId=185579


Visual Studio SDK http://go.microsoft.com/fwlink/?LinkId=185580

Modeling SDK for Visual Studio

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Creating a DSL Solution


To create a new domain-specific language, you create a new Visual Studio solution by using the Domain-Specific
Language project template.
To create a DSL solution
1. On the File menu, point to New, and then click Project.
2. Under Project types, expand the Other Project Types node, and click Extensibility.
3. Click Domain-Specific Language Designer.

4. In the Name box, type FamilyTree. Click OK.


The Domain-Specific Language Wizard opens, and displays a list of template DSL solutions.
Click each template to see a description,
The templates are useful starting points. Each of them provides a complete working DSL, which you can edit
to suit your needs. Ordinarily, you would choose the template nearest what you want to create.
5. For this walkthrough, choose the Minimal Language template.
6. Enter a file name extension for your DSL in the appropriate wizard page. This is the extension that files
containing instances of your DSL will use.
Choose an extension that is not associated with any application in your computer, or in any computer
where you want to install the DSL. For example, docx and htm would be unacceptable file name
extensions.
The wizard will warn you if the extension that you have entered is being used as a DSL. Consider
using a different file name extension. You can also reset the Visual Studio SDK Experimental instance
to clear out old experimental designers. Click Start, click All Programs, Microsoft Visual Studio
2010 SDK, Tools, and then Reset the Microsoft Visual Studio 2010 Experimental instance.
7. Inspect the other pages and then click Finish.
A solution is generated that contains two projects. They are named Dsl and DslPackage. A diagram file
opens that is named DslDefinition.dsl.

NOTE
Most of the code that you can see in the folders in the two projects is generated from DslDefinition.dsl. For this
reason, most modifications to your DSL are made in this file.

The user interface now resembles the following picture.

This solution defines a domain specific language. For more information, see Overview of the Domain-
Specific Language Tools User Interface.

The important parts of the DSL solution


Notice the following aspects of the new solution.
Dsl\DslDefinition.dsl This is the file that you see when you create a DSL solution. Almost all the code in
the solution is generated from this file, and most of the changes that you make to a DSL definition are made
here. For more information, see Working with the Working with the DSL Definition Diagram.
Dsl project This project contains code that defines the domain-specific language.
DslPackage project This project contains code that allows instances of the DSL to be opened and edited in
Visual Studio.

Running the DSL


You can run the DSL solution as soon as you have created it. Later, you can modify the DSL definition gradually,
running the solution again after each change.
To experiment with the DSL
1. Click Transform All Templates in the Solution Explorer toolbar. This regenerates most of the source code
from DslDefinition.dsl.

NOTE
Whenever you change DslDefinition.dsl, you must click Transform All Templates before you rebuild the solution.
You can automate this step. For more information, see How to Automate Transform All Templates.
2. Press F5, or on the Debug menu, click Start Debugging.
The DSL builds and is installed in the experimental instance of Visual Studio.
An experimental instance of Visual Studio starts. The experimental instance takes its settings from a
separate subtree of the registry, where Visual Studio extensions are registered for debugging purposes.
Normal instances of Visual Studio do not have access to extensions registered there.
3. In the experimental instance of Visual Studio, open the model file named Test from Solution Explorer.
- or -
Right-click the Debugging project, point to Add, and then click Item. In the Add Item dialog box, select the
file type of your DSL.
The model file opens as a blank diagram.
The toolbox opens and displays tools appropriate to the diagram type.
4. Use the tools to create shapes and connectors on the diagram.
a. To create shapes, drag from the Example Shape tool onto the diagram.
b. To connect two shapes, click the Example Connector tool, click the first shape, and then click the
second shape.
5. Click the labels of the shapes to change them.
Your experimental Visual Studio will resemble the following example:

The Content of a Model


The content of a file that is an instance of a DSL is called a model. The model contains modelelements and links
between the elements. The DSL definition specifies what types of model elements and links can exist in the model.
For example, in a DSL created from the Minimal Language template, there is one type of model element, and one
type of link.
The DSL definition can specify how the model appears on a diagram. You can choose from a variety of styles of
shapes and connectors. You can specify that some shapes appear inside other shapes.
You can view a model as a tree in the Explorer view while you are editing a model. As you add shapes to the
diagram, the model elements also appear in the explorer. The explorer can be used even if there is no diagram.
If you cannot see the Explorer in the debugging instance of Visual Studio, on the View menu point to Other
Windows, and then click <Your Language> Explorer.
The API of your DSL
Your DSL generates an API that allows you to read and update models that are instances of the DSL. One
application of the API is to generate text files from a model. For more information, see Design-Time Code
Generation by using T4 Text Templates.
In the Debugging solution, open the template files with extension ".tt". These samples demonstrate how you can
generate text from models, and allow you to test the API of your DSL. One of the samples is written in Visual Basic,
the other in Visual C#.
Under each template file is the file that it generates. Expand the template file in Solution Explorer, and open the
generated file.
The template file contains a short segment of code that lists all the elements in the model.
The generated file contains the result.
When you change a model file, you will see corresponding changes in generated files after you regenerate the files.
To r e g e n e r a t e t e x t fi l e s a ft e r y o u c h a n g e t h e m o d e l fi l e

1. In the experimental instance of Visual Studio, save the model file.


2. Make sure that the file name parameter in each .tt file refers to the model file that you are using for
experiments. Save the .tt file.
3. Click Transform All Templates in the toolbar of Solution Explorer.
- or -
Right-click the templates that you want to regenerate and then click Run Custom Tool.
You can add any number of text template files to a project. Each template generates one result file.

NOTE
When you change the DSL definition, the sample text template code will not work, unless you update it.

For more information, see Generating Code from a Domain-Specific Language and Writing Code to Customise a
Domain-Specific Language.

Customizing the DSL


When you want to modify the DSL definition, close the experimental instance and update the definition in the main
Visual Studio instance.

NOTE
After you have modified the DSL definition, you might lose information in the test models that you have created by using
earlier versions. For example, the debugging solution contains a file that is named Sample, which contains some shapes and
connectors. After you start to develop your DSL definition, they will not be visible, and they will be lost when you save the
file.

You can make a wide variety of extensions to your DSL. The following examples will give you an impression of the
possibilities.
After each change, save the DSL definition, click Transform All Templates in Solution Explorer, and then press
F5 to experiment with the changed DSL.
Rename the Types and Tools
Rename the existing domain classes and relationships. For example, starting from a Dsl Definition created from the
Minimal Language template, you could perform the following renaming operations, to make the DSL represent
family trees.
To r e n a m e d o m a i n c l a sse s, r e l a t i o n sh i p s a n d t o o l s

1. In the DslDefinition diagram, rename ExampleModel to FamilyTreeModel, ExampleElement to Person,


Targets to Parents, and Sources to Children. You can click each label to change it.

2. Rename the element and connector tools.


a. Open the DSL Explorer window by clicking the tab under Solution Explorer. If you cannot see it, on
the View menu point to Other Windows and then click DSL Explorer. DSL Explorer is visible only
when the DSL Definition diagram is the active window.
b. Open the Properties window and position it so that you can see DSL Explorer and Properties at the
same time.
c. In DSL Explorer, expand Editor, Toolbox Tabs, <your DSL>, and then Tools.
d. Click ExampleElement. This is the toolbox item that is used to create elements.
e. In the Properties window, change the Name property to Person.
Notice that the Caption property also changes.
f. In the same manner, change the name of the ExampleConnector tool to ParentLink. Alter the
Caption property so that it is not a copy of the Name property. For example, enter Parent Link.
3. Rebuild the DSL.
a. Save the DSL Definition file.
b. Click Transform All Templates in the toolbar of Solution Explorer
c. Press F5. Wait until the experimental instance of Visual Studio appears.
4. In the Debugging solution in the experimental instance of Visual Studio, open a test model file. Drag
elements onto it from the toolbox. Notice that the tool captions and the type names in DSL Explorer have
changed.
5. Save the model file.
6. Open a .tt file and replace occurrences of the old type and property names with the new names.
7. Make sure that the file name that is specified in the .tt file specifies your test model.
8. Save the .tt file. Open the generated file to see the result of running the code in the .tt file. Verify that it is
correct.
Add Domain Properties to Classes
Add properties to a domain class, for example to represent the years of birth and death of a Person.
To make the new properties visible on the diagram, you must add decorators to the shape that displays the model
element. You must also map the properties to the decorators.
To a d d p r o p e r t i e s a n d d i sp l a y t h e m
1. Add the properties.
a. In the DSL Definition diagram, right-click the Person domain class, point to Add, and then click
Domain Property.
b. Type a list of new property names, such as Birth and Death. Press Enter after each one.
2. Add decorators that will display the properties in the shape.
a. Follow the gray line that extends from the Person domain class to the other side of the diagram. This
is a diagram element map. It links the domain class to a shape class.
b. Right-click this shape class, point to Add, and then click Text Decorator.
c. Add two decorators with names such as BirthDecorator and DeathDecorator.
d. Select each new decorator, and in the Properties window, set the Position field. This determines
where the domain property value will be displayed on the shape. For example, set InnerBottomLeft
and InnerBottomRight.

3. Map the decorators to the properties.


a. Open the DSL Details window. It is usually in a tab next to the Output window. If you cannot see it, on
the View menu, point to Other Windows, and then click DSL Details.
b. On the DSL definition diagram, click the line that connects the Person domain class to the shape
class.
c. In DSL Details, on the Decorator Maps tab, click the check box on an unmapped decorator. In
Display Property, select the domain property to which you want it mapped. For example, map
BirthDecorator to Birth.
4. Save the DSL, click Transform All Templates, and press F5.
5. In a sample model diagram, verify that you can now click the positions you chose and type values into them.
In addition, when you select a Person shape, the Properties window displays the new properties Birth and
Death.
6. In a .tt file, you can add code that obtains the properties of each person.
Define New Classes
You can add domain classes and relationships to a model. For example, you could create a new class to represent
towns, and a new relationship to represent that a person lived in a town.
To make the different types distinct on a model diagram, you can map the domain classes to different kinds of
shape, or to shapes with different geometry and colors.
To a d d a n d d i sp l a y a n e w d o m a i n c l a ss

1. Add a domain class and make it a child of the model root.


a. In the DSL Definition diagram, click the Embedding Relationship tool, click the root class
FamilyTreeModel, and then click in an empty part of the diagram.
A new domain class appears, that is connected to the FamilyTreeModel with an embedding
relationship.
Set its name, for example Town.

NOTE
Every domain class except the root of the model must be the target of at least one embedding relationship,
or it must inherit from a class that is the target of an embedding. For this reason, it is frequently convenient
to create a domain class by using the Embedding Relationship tool.

b. Add a domain property to the new class, for example Name.


2. Add a reference relationship between Person and Town.
a. Click the Reference Relationship tool, click Person and then click Town.

NOTE
Reference relationships represent cross-references from one part of the model tree to another.
3. Add a shape to represent towns on the model diagrams.
a. Drag a Geometry Shape from the toolbox to the diagram and rename it, for example TownShape.
b. In the Properties window, set the Appearance fields of the new shape, such as Fill Color and
Geometry.
c. Add a Decorator to display the name of the town, and rename it NameDecorator. Set its Position
property.
4. Map the Town domain class to the TownShape.
a. Click the Diagram Element Map tool, then click the Town domain class, and then the TownShape
shape class.
b. In the Decorator Maps tab of the DSL Details window with the map connector selected, check
NameDecorator and set Display Property to Name.
5. Create a connector to display the relationship between Person and Towns.
a. Drag a Connector from the toolbox to the diagram. Rename it and set its appearance properties.
b. Use the Diagram Element Map tool to link the new connector to the relationship between Person
and Town.

6. Create an element tool for making a new Town.


a. In DSL Explorer, expand Editor then Toolbox Tabs.
b. Right-click <your DSL> and then click Add New Element Tool.
c. Set the Name property of the new tool, and set its Class property to Town.
d. Set the Toolbox Icon property. Click [...] and in the File name field, select an icon file.
7. Create a connector tool for making a link between towns and people.
a. Right-click <your DSL> and then click Add New Connector Tool.
b. Set the Name property of the new tool.
c. In the ConnectionBuilder property, select the builder that contains the name of the Person-Town
relationship.
d. Set the Toolbox Icon.
8. Save the DSL Definition, click Transform All Templates, and then press F5.
9. In the experimental instance of Visual Studio, open a test model file. Use the new tools to create towns and
links between towns and persons. Notice that you can only create links between the correct types of
element.
10. Create code that lists the town in which each person lives. Text templates are one of the places where you
can run such code. For example, you could modify the existing Sample.tt file in the Debugging solution so
that it contains the following code:

<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"


debug="true" #>
<#@ output extension=".txt" #>
<#@ FamilyTree processor="FamilyTreeDirectiveProcessor" requires="fileName='Sample.ftree'" #>

<#
foreach (Person person in this.FamilyTreeModel.People)
{
#>
<#= person.Name #><#if (person.Town != null) {#> of <#= person.Town.Name #> <#}#>

<#
foreach (Person child in person.Children)
{
#>
<#= child.Name #>
<#
}
}
#>

When you save the *.tt file, it will create a subsidiary file that contains the list of people and their residences.
For more information, see Generating Code from a Domain-Specific Language.

Validation and Commands


You could develop this DSL further by adding validation constraints. These constraints are methods that you can
define, that make sure that the model is in a correct state. For example, you could define a constraint to make sure
that the birth date of a child is later than that of its parents. The validation feature displays a warning if the DSL
user tries to save a model that breaks any of the constraints. For more information, see Validation in a Domain-
Specific Language.
You can also define menu commands that the user can invoke. Commands can modify the model. They can also
interact with other models in Visual Studio and with external resources. For more information, see How to: Modify
a Standard Menu Command.

Deploying the DSL


To allow other users to use the domain-specific language, you distribute a Visual Studio Extension (VSIX) file. This
is created when you build the DSL solution.
Locate the .vsix file in the bin folder of your solution. Copy it to the computer on which you want to install it. On
that computer, double-click the VSIX file. The DSL can be used in all instances of Visual Studio on that computer.
You can use the same procedure to install the DSL on your own computer so that you do not have to use the
experimental instance of Visual Studio.
For more information, see Deploying Domain-Specific Language Solutions.

Removing old Experimental DSLs


If you have created experimental DSLs that you no longer want, you can remove them from your computer by
resetting the Visual Studio Experimental instance.
This will remove from your computer all experimental DSLs and other experimental Visual Studio extensions.
These are extensions that have been executed in debugging mode.
This procedure does not remove DSLs or other Visual Studio extensions that have been fully installed by executing
the VSIX file.
To reset the Visual Studio Experimental instance
1. Click Start, click All Programs, Microsoft Visual Studio 2010 SDK, Tools, and then Reset the Microsoft
Visual Studio 2010 Experimental instance.
2. Rebuild any experimental DSLs or other experimental Visual Studio extensions that you still want to use.

See Also
Understanding Models, Classes and Relationships
How to Define a Domain-Specific Language

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Understanding Models, Classes and Relationships
10/18/2017 9 min to read Edit Online

A domain-specific language (DSL) is defined by its DSL Definition file, together with any custom program code
that you might write. Most of the program code in the DSL solution is generated from this file.
This topic explains the central features of the DSL definition.

The DSL Definition


When you open Dsl\DslDefinition.dsl , your Visual Studio window resembles the following picture.

The most important information in the DSL Definition is displayed in the DSL Definition diagram. Additional
information, which is also part of DslDefinition.dsl, is displayed in DSL Explorer, which usually appears at the side
of the diagram. You work with the diagram for the most frequent tasks, and with DSL Explorer for more advanced
customizations.
The DSL Definition diagram shows the domain classes that define model elements, and the relationships that
define links between model elements. It also shows the shapes and connectors that are used to display the model
elements to the user.

When you select an item in the DSL definition, either on the diagram or in DSL Explorer, information about it is
displayed in the Properties window. Additional information may be displayed in the DSL Details window.
Models are instances of DSLs
A model is an instance of your DSL created by a user. A model contains model elements, which are instances of
the domain classes that you define, and links between the elements, which are instances of the domain
relationships that you define. A model can also have shapes and connectors, which display the model elements
and links on a diagram. The DSL definition includes the shape classes, connector classes, and a class for the
diagram.
A DSL Definition is also known as a domain model. A DSL Definition or domain model is the design-time
representation of the domain-specific language, whereas the model is the run-time instantiation of the domain-
specific language.

Domain Classes define Model Elements


Domain classes are used to create the various elements in the domain, and domain relationships are the links
between the elements. They are the design-time representation of the elements and links that will be instantiated
by the users of the design-specific language when they create their models.
This illustration shows a model that has been created by the user of a music library DSL. Music albums are
represented by boxes that contain lists of songs. Artists are represented by round-cornered boxes, and are
connected to the albums to which they have contributed.

The DSL Definition separates two aspects. The appearance of the model elements on the model diagram is defined
by using shape classes and connector classes. The information carried in the model is defined using domain
classes and domain relationships.
The following illustration shows the domain classes and relationships in the DSL Definition of the Music Library.

The illustration shows four domain classes: Music, Album, Artist and Song. The domain classes define domain
properties such as Name, Title, and so on. In the instance model, the values of some of these properties are
displayed on the diagram.
Between the classes are domain relationships: MusicHasAlbums, MusicHasArtists, AlbumbHasSongs, and
ArtistAppearedOnAlbums. The relationships have multiplicities such as 1..1, 0..*. For example, every Song must be
related to exactly one Album through the AlbumHasSongs relationship. Every Album can have any number of
Songs.
Rearranging the DSL Definition Diagram
Notice that a domain class can appear several times on the DSL Definition diagram, as Album does in this picture.
There is always one main view, and there can be some reference views.
To rearrange the DSL Definition diagram, you can:
Swap main and reference views by using the Bring Tree Here and Split Tree commands. Right-click a
single domain class to see these commands.
Re-order the domain classes and shape classes by pressing Ctrl+Up and Ctrl+Down.
Collapse or expand classes using the icon at the upper-right of each shape.
Collapse parts of the tree by clicking the minus sign (-) at the bottom of a domain class.

Inheritance
Domain classes can be defined using inheritance. To create an inheritance derivation, click the Inheritance tool,
click the derived class, and then click the base class. A model element has all the properties that are defined on its
own domain class, together with all the properties inherited from the base class. It also inherits its roles in
relationships.
Inheritance can also be used between Relationships, Shapes, and Connectors. Inheritance must keep within the
same group. A shape cannot inherit from a domain class.

Domain Relationships
Model elements can be linked by relationships. Links are always binary; they link exactly two elements. However,
any element can have many links to other objects, and there can even be more than one link between the same
pair of elements.
Just as you can define different classes of elements, you can define different classes of links. The class of a link is
called a domain relationship. A domain relationship specifies what classes of element its instances can connect.
Each end of a relationship is called a role, and the domain relationship defines names for the two roles, as well as
for the relationship itself.
There are two kinds of domain relationships: embedding relationships and reference relationships. On the DSL
Definition diagram, embedding relationships have solid lines at each role, and reference relationships have
dashed lines.
Embedding Relationships
Every element in a model, except for its root, is the target of one embedding link. Therefore, the whole model
forms a single tree of embedding links. An embedding relationship represents containment or ownership. Two
model elements that are related in this way are also known as parent and child. The child is said to be embedded
in the parent.
Embedding links are not usually shown explicitly as connectors on a diagram. Instead, they are usually
represented by containment. The root of the model is represented by the diagram, and elements embedded in it
are displayed as shapes on the diagram.
In the example, the root class Music has an embedding relationship MusicHasAlbums to Album, which has an
embedding AlbumHasSongs to Song. Songs are displayed as items in a list inside each Album. Music also has an
embedding MusicHasArtists to the Artist class, whose instances also appear as shapes on the diagram.
By default, embedded elements are automatically deleted when their parents are deleted.
When a model is saved to file in XML form, embedded elements are nested inside their parents, unless you have
customized the serialization.

NOTE
Embedding is not the same as inheritance. Children in an embedding relationship do not inherit the parent's properties. An
embedding is a type of link between model elements. Inheritance is a relationship between classes, and does not create links
between model elements.

Embedding rules
Every element in an instance model must be the target of exactly one embedding link, except for the root of the
model.
Therefore, every non-abstract domain class, except the root class, must be the target of at least one embedding
relationship, or it must inherit an embedding from a base class. A class can be the target of two or more
embeddings, but its instance model elements can only have one parent at a time. The multiplicity from target to
source must be 0..1 or 1..1.
The Explorer Displays the Embedding Tree
Your DSL Definition also creates an explorer, which users see alongside their model diagram.

The explorer shows all the elements in the model, even those for which you have not defined any shapes. It shows
elements and embedding relationships, but not reference relationships.
To see the values of the domain properties of an element, the user selects an element, either in the model diagram
or in the model explorer, and opens the Properties window. It displays all the domain properties, including those
that are not displayed on the diagram. In the example, each Song has both a Title and a Genre, but only the value
of the Title is shown on the diagram.

Reference Relationships
A reference relationship represents any kind of relationship that is not embedding.
Reference relationships are typically displayed on a diagram as connectors between shapes.
In the XML representation of the model, a reference link between two elements is represented using monikers.
That is, monikers are names that uniquely identify each element in the model. The XML node for each model
element contains a node that specifies the name of the relationship and the moniker of the other element.

Roles
Every domain relationship has two roles, a source role and a target role.
In the following picture, the line between the Publisher domain class and the PublisherCatalog domain
relationship is the source role. The line between the domain relationship and the Album domain class is the target
role.

The names associated with a relationship are especially important when you write program code that traverses
the model. For example, when you build the DSL solution, the generated class Publisher has a property Catalog
that is a collection of Albums. The class Album has a property Publisher that is a single instance of the class
Publisher.
When you create a relationship in a DSL Definition, the property and relationship names are given default values.
However, you can change them.

Multiplicities
Multiplicities specify how many elements can have the same role in a domain relationship. In the example, the
zero-to-many (0..*) multiplicity setting on the Catalog role specifies that any instance of the Publisher domain
class can have as many PublisherCatalog relationship links as you want to give it.
Configure the multiplicity of a role either by typing on the diagram or by modifying the Multiplicity property in
the Properties window. The following table describes the settings for this property.

MULTIPLICITY TYPE DESCRIPTION

0..* (Zero to many) Each instance of the domain class can have multiple instances
of the relationship or no instances of the relationship.

0..1 (Zero to one) Each instance of the domain class can have no more than one
instance of the relationship or no instances of the
relationship.

1..1 (One) Each instance of the domain class can have one instance of
the relationship. You cannot create more than one instance of
this relationship from any instance of the role class. If
validation is enabled, a validation error will appear when any
instance of the role class has no instance of the relationship.

1..* (One to many) Each instance of the class on the role that has this multiplicity
can have multiple instances of the relationship, and each
instance must have at least one instance of the relationship. If
validation is enabled, a validation error will appear when any
instance of the role class has no instance of the relationship.

Domain Relationships as Classes


A link is represented in the Store as an instance of LinkElement, which is a derived class of ModelElement. You can
define these properties in the domain model diagram on domain relationships.
You can also make a relationship the source or target of other relationships. In the domain model diagram, right-
click the domain relationship and then click Show As Class. An additional class box will appear. You can then
connect relationships to it.
You can define a relationship partly by inheritance, just as you can with domain classes. Select the derived
relationship and set Base Relationship in the Properties window.
A derived relationship specializes its base relationship. The domain classes that it links should be derived from or
the same as the classes linked by the base relationship. When a link of the derived relationship is created in a
model, it is an instance of both the derived and the base relationships. In program code, you can navigate to the
opposite end of the link using the properties generated either by the base or by the derived class.

See Also
Domain-Specific Language Tools Glossary
How to Define a Domain-Specific Language
10/18/2017 32 min to read Edit Online

To define a domain-specific language (DSL), you create a Visual Studio solution from a template. The key part of
the solution is the DSL Definition diagram, which is stored in DslDefinition.dsl. The DSL Definition defines the
classes and shapes of the DSL. After you modify and adding to these elements, you can add program code to
customize the DSL in more detail.
If you are new to DSLs, we recommend that you work through the DSL Tools Lab, which you can find in this
site: Visualizaton and Modeling SDK

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.

Selecting a Template Solution


To define a DSL, you must have installed the following components:

Visual Studio http://go.microsoft.com/fwlink/?LinkId=185579

Visual Studio SDK http://go.microsoft.com/fwlink/?LinkId=185580

Visual Studio Visualization and Modeling SDK

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.

To create a new domain-specific language, you create a new Visual Studio solution using the Domain-Specific
Language project template.
To create a DSL solution
1. Create a solution with the Domain-Specific Language template, which can be found under Other
Project Types/Extensibility in the New Project dialog box.
When you click OK, the Domain-Specific Language Wizard opens and displays a list of template DSL
solutions.
2. Click each template to see a description. Choose the solution that most closely resembles what you want
to create.
Each DSL template defines a basic working DSL. You will edit this DSL to fit your own requirements.
Click each sample for more information.
Select Task Flow to create a DSL that has swimlanes. Swimlanes are vertical or horizontal
partitions of the diagram.
Select Component Models to create a DSL that has ports. Ports are small shapes on the edge of
a larger shape.
Select Class Diagrams to define a DSL that has compartment shapes. Compartment shapes
contain lists of items.
Select Minimal Language in other cases, or if you are uncertain.
Select Minimal WinForm Designer or Minimal WPF Designer to create a DSL that is displayed
on a Windows Forms or WPF surface. You will have to write code to define the editor. For more
information, see the following topics:
Creating a Windows Forms-Based Domain-Specific Language
Creating a WPF-Based Domain-Specific Language
3. Enter a file name extension for your DSL in the appropriate wizard page. This is the extension that files
containing instances of your DSL will use.
Choose a file name extension that is not associated with any application in your computer, or in
any computer where you want to install the DSL. For example, docx and htm would be
unacceptable file name extensions.
The wizard will warn you if the extension that you have entered is being used as a DSL. Consider
using a different file name extension. You can also reset the Visual Studio SDK Experimental
instance to clear out old experimental designers. Click Start, click All Programs, Microsoft
Visual Studio 2010 SDK, Tools, and then Reset the Microsoft Visual Studio 2010
Experimental instance.
4. You can either adjust the settings on the other pages, or leave the default values.
5. Click Finish.
The wizard creates a solution that contains two or three projects, and generates code from the DSL
definition.
The user interface now resembles the following picture.

This solution defines a domain specific language. For more information, see Overview of the Domain-
Specific Language Tools User Interface.
Test the Solution
The template solution provides a working DSL, which you can modify or use as it is.
To test the solution, press F5 or CTRL+F5. A new instance of Visual Studio opens in experimental mode.
In the new instance of Visual Studio, in Solution Explorer, open the Sample file. It opens as a diagram, with a
toolbox.
If you run a solution that you have created from the Minimal Language template, your experimental Visual
Studio will resemble the following example:

Experiment with the tools. Create elements and connect them.


Close the experimental instance of Visual Studio.

NOTE
When you have modified the DSL, you will no longer be able to see the shapes on the Sample test file. However, you will
be able to create new elements.

Modifying the template DSL


Rename and keep some or all of the domain classes and shape classes in the template DSL definition. Your new
class names should be valid CLR names, without spaces or punctuation.
It is especially useful to keep these classes:
The root class appears at the upper-left of the DSL Definition diagram, under Classes and
Relationships. Rename it to a name different from the DSL. For example, a DSL named MusicLibrary
might have a root class named Music.
The diagram class appears at the lower right of the DSL Definition diagram, in the Diagram Elements
column. You might have to scroll to the right to see it. It is typically named YourDslDiagram.
If you used the Task Flow template and you want to create diagrams with swimlanes, keep and rename
the Actor domain class and ActorSwimlane shape.
Delete or rename other classes to suit your requirements.

Patterns for Defining a DSL


We recommend that you develop a DSL by adding or adjusting one or two features at a time. Add a feature, run
the DSL and test it, and then add one or two more features. A typical feature of your DSL might be:
A domain class, the embedding relationship that connects the element to the model, the shape required
to display elements of that class on the diagram, and the element tool that lets users create elements.
The domain properties of a domain class and the decorators that display them on a shape.
A reference relationship and the connector that displays it on the diagram and the connector tool that
lets users create links.
A customization that requires program code, such as a validation constraint or a menu command.
The following sections describe how to construct the most useful kinds of DSL features. There are many
other patterns with which a DSL can be constructed, but these are the most frequently used.

NOTE
After adding a feature, do not forget to click Transform All Templates in the toolbar of Solution Explorer before you
build and running your DSL.

The following figure shows classes and relationships part of the DSL that is used as an example in this topic.

The next figure is an example model of this DSL:


NOTE
"Model" refers to an instance of your DSL that users create, and typically is displayed as a diagram. This topic discusses
both the DSL Definition diagram and the model diagrams that appear when your DSL is used.

Defining Domain Classes


Domain Classes represent the concepts of your DSL. The instances are model elements. For example in a
MusicLibrary DSL you might have Domain Classes named Album and Song.
To create a domain class, you can drag from the Named Domain Class tool to the diagram, and then rename
the class.
For more information, see Properties of Domain Classes.
Create an Embedding Relationship for each Domain Class
Every domain class except the root class must be the target of at least one embedding relationship, or it must
inherit from a class that is the target of an embedding relationship.
In a model, every model element is a node in a single tree of embedding relationships. The source and target of
an embedding relationship are frequently referred to as parent and child.
The selection of a parent for a domain class depends on how you want its elements' lifetimes to depend on
other elements. If a node of a tree is deleted, its sub-tree is usually deleted as well. Classes of element that have
an independent existence are therefore embedded directly under the root class.
Typically, if you display an element inside another element, you want to indicate an owner relationship. In that
case, the most appropriate parent class is the class of the container. The exception is when the item that you see
inside a container is actually just a reference link to an independent element. In that case, deleting the container
deletes the reference but not its target.
In the patterns of DSL definition described in this topic, we will assume that elements displayed inside a
container will be deleted when the container is deleted. More complex schemes are possible, and can be
achieved by defining rules.

HOW ELEMENT IS DISPLAYED PARENT (EMBEDDING) CLASS EXAMPLE IN DSL SOLUTION TEMPLATE

Shape on diagram. Root class of DSL. Minimal Language.

Swimlane. Task Flow: Actor class.


HOW ELEMENT IS DISPLAYED PARENT (EMBEDDING) CLASS EXAMPLE IN DSL SOLUTION TEMPLATE

Shape in swimlane. Domain class of elements that are Task Flow: Task class.
displayed as swimlanes.

Item in list in shape, where item is Domain class that is mapped to the Class diagram: Attribute class.
deleted if container is deleted. container shape.
Component diagram: Port class.
Port on edge of shape.

Item in list, not deleted if container is Root class of DSL.


deleted.
The list displays reference links.

Not directly displayed. The class of which it forms part.

In the Music Library example, Albums are displayed as rectangles in which the titles of Songs are listed.
Therefore the parent of Album is the root class Music, and the parent of Song is Album.
To create a domain class and its embedding at the same time, click the Embedding Relationship tool, then
click the parent class, and then click on a blank part of the diagram.
It is not usually necessary to adjust the name of the embedding relationship and its roles, because they will
track the class names automatically.
For more information, see Properties of Domain Relationships and Properties of Domain Roles.

NOTE
Embedding is not the same as inheritance. Children in an embedding relationship do not inherit features from their
parents.

Add Domain Properties to each Domain Class


Domain properties store values. Examples are: Name, Title, Publication Date.
Click Domain Properties in the class, press the ENTER key, and then type the name of a property. The default
type of a domain property is String. If you want to change the type, select the domain property, and set the
Type in the Properties window. If the type that you want is not in the drop-down list, see Adding Property
Types.
Set an Element Name property. Select a domain property that can be used to identify elements in the
language explorer. For example, in the Song domain class that you could select the Title domain property. In the
Properties window, set Is Element Name to true .
Create Derived Domain Classes
If you want a domain class to have variants that inherit its properties and relationships, create classes that
derive from it. For example, Album might have derived classes WMA and MP3.
Create the derived class using the Domain Class tool.
Click the Inheritance tool, click the derived class, and then click the base class.
Consider setting the Inheritance Modifier of the base class to abstract. If you think that you might need
instances of the base class, consider instead creating a separate derived class for them.
Derived classes inherit the properties and roles of their base classes.
Tidy the DSL Definition Diagram
When you add relationships, some of your classes will appear in more than one place. To reduce the number of
appearances and make the diagram wider, right-click the target class of a relationship, and then click Bring
Tree Here. For the opposite effect, right-click the target class of a relationship and click Split Tree. If you do not
see these menu commands, make sure that only the domain class is selected.
Use CTRL+Up and CTRL+Down to move domain classes and shape classes.
Test the domain classes
To t e st t h e n e w D o m a i n C l a sse s

1. Click Transform All Templates in the toolbar of Solution Explorer, to generate the DSL designer code.
You can automate this step. For more information, see How to Automate Transform All Templates.
2. Build and run the DSL. Press F5 or CTRL+F5 to run a new instance of Visual Studio in experimental
mode. In the experimental instance of Visual Studio, open or create a file that has the file name extension
of your DSL.
3. Open the Explorer. At the side of the diagram is the language explorer window, which is usually named
YourLanguage Explorer. If you do not see this window, it might be on a tab underneath Solution
Explorer. If you cannot find it, on the View menu, point to Other Windows, and then click
YourLanguageExplorer.
Your explorer presents a tree view of the model.
4. Create new elements. Right-click the root node at the top, and then click Add NewYourClass.
A new instance of your class appears in your language Explorer.
5. Verify that each instance has a different name when you create new instances. This will occur only if you
have set the Is Element Name flag on a domain property.
6. Examine the domain properties. With an instance of your class selected, inspect the Properties
window. It should show the domain properties that you defined on this domain class.
7. Save the file, close it, and re-open it. All the instances you created should be visible in the explorer,
after you expand the nodes.

Defining Shapes on the Diagram


You can define classes of elements that appear on a diagram as rectangles, ellipses, or icons.
To define a class of elements that appear as shapes on a diagram
1. Define and test a domain class as described in Defining Domain Classes .
The parent of the class should be the root class. That is, there should be an embedding
relationship between the root class and the new domain class.
If your diagram has swimlanes, the parent can be the domain class that is mapped to a swimlane.
Before continuing with this procedure, see Defining a DSL that has Swimlanes.
2. Add a shape class to represent the elements on the model diagram. Drag from one of the following
tools onto the DSL Definition diagram:
Geometry Shape provides a rectangle or ellipse.
Image Shape displays an image that you provide.
Compartment Shape is a rectangle that contains one or more lists of items.
Rename the shape class, which will appear on the right side of the DSL Definition diagram, under
Shapes and Connectors.
3. Define an image, if you created an image shape.
a. Create an image file of any size. BMP, JPEG, GIF and EMF formats are supported.
b. In Solution Explorer, add the file to the solution under Dsl\Resources.
c. Return to the DSL Definition diagram and select the new image shape class.
d. In the Properties window, click the Image property.
e. In the Select Image dialog box, click the drop-down menu under File name, and select the
image.
4. Add text decorators to the shape, to display the domain properties.
To display the name or title of the model element, you will probably need at least one text decorator.
Right-click the header of the shape class, point to Add, and then click Text Decorator. Set the name of
the decorator, and in the Properties window set its Position.
5. Connect each shape with a Diagram Element Map to the domain class that it should display.
Click the Diagram Element Map tool, then click the domain class, then click the shape class.
6. Map the properties to the text decorators.
a. Select the gray line between the domain class and the shape class that represents the diagram
element map.
b. In the DSL Details window, click the Decorator Maps tab. If you do not see the DSL Details
window, on the View menu, point to Other Windows and then click DSL Details. It is frequently
necessary to raise the top of this window to see all its content.
c. Select the name of a decorator. Under Display property, select the name of a property of the
domain class. Repeat this for each decorator.
If you want to display a property of a related element, click the drop-down tree navigator under
Path to display property.
d. Make sure that a check mark appears alongside each decorator name.
7. Make a toolbox item for creating elements of the domain class.
a. In DSL Explorer, expand the Editor node and all its sub-nodes.
b. Right-click the node under Toolbox Tabs that has the same name as your DSL, for example
MusicLibrary. Click Add Element Tool.

NOTE
If you right-click the Tools node, you will not see Add Element Tool. Instead, click the node above it.

c. In the Properties window with the new element tool selected, set Class to the domain class that
you have recently added.
d. Set Caption and Tooltip.
e. Set Toolbox Icon to an icon that will appear in the toolbox. You can set it to a new icon or an icon
already used for another tool.
To create a new icon, open Dsl\Resources in Solution Explorer. Copy and paste one of the
existing element tool BMP files. Rename the pasted copy, and then double-click to edit it.
Return to the DSL Definition diagram, select the tool, and in the Properties window click [...] in
Toolbox Icon. In the Select Bitmap dialog box, select your .BMP file from the drop-down menu.
For more information, see Properties of Geometry Shapes and Properties of Image Shapes.
To Test Shapes
1. Click Transform All Templates in the toolbar of Solution Explorer, to generate the DSL designer code.
2. Build and run the DSL. Press F5 or CTRL+F5 to run a new instance of Visual Studio in experimental
mode. In the experimental instance of Visual Studio, open or create a file that has the file name extension
of your DSL.
3. Verify that the element tools appear on the toolbox.
4. Create shapes by dragging from a tool onto the model diagram.
5. Verify that each text decorator appears, and that:
a. You can edit it, unless you have set the Is UI Read Only flag on the domain property.
b. When you edit the property either in the Properties window or in the decorator, the other view is
updated.
After you first test a shape, you might want to adjust some its properties and add some more advanced
features. For more information, see Customizing and Extending a Domain-Specific Language.

Defining Reference Relationships


You can define a reference relationship between any source domain class and any target domain class.
Reference relationships are typically displayed on a diagram as connectors, which are lines between shapes.
For example, if music Albums and Artists are displayed as shapes on your diagram, you could define a
relationship named ArtistsAppearedOnAlbums that links Artists to the Albums on which they have worked. See
the example in the figure.

Reference relationships can also link elements of the same type. For example, in a DSL representing a family
tree, the relationship between parents and their children is a reference relationship from Person to Person.
Define a Reference Relationship
Click the Reference Relationship tool, then click the source domain class of the relationship, and then click the
target domain class. The target class can be the same as the source class.
Each relationship has two roles, represented by the line at each side of the relationship box. You can select each
role and set its properties in the Properties window.
Consider renaming the roles. For example, in a relationship between Person and Person, you might want to
change the default names to Parents and Children, Manager and Subordinates, Teacher and Student, and so on.
Adjust the multiplicities of each role, if it is necessary. If you want each Person to have at most one
Manager, set the multiplicity that appears below the Manager label on the diagram to 0..1.
Add domain properties to the relationship. In the figure, the Artist-Album relationship has a property of
role.
Set the Allows Duplicates property of the relationship, if more than one link of the same class can exist
between the same pair of model elements. For example, you could allow a Teacher to teach more than one
Subject to the same Student.
For more information, see Properties of Domain Relationships and Properties of Domain Roles.
Define a Connector to Display the Relationship
A connector displays a line between two shapes on the model diagram.
Drag the Connector tool onto the DSL definition diagram.
Add text decorators if you want to display labels on the connector. Set their positions. To let the user move a
text decorator, set its Is Moveable property.
Use the Diagram Element Map tool to link the connector to the reference relationship.
With the diagram element map selected, open the DSL Details window, and open the Decorator Maps tab.
Select each Decorator and set Display property to the correct domain property.
Make sure that a check mark appears next to each item in the Decorators list.
Define a Connection Builder Tool
In the DSL Explorer window, expand the Editor node and all its subnodes.
Right-click the node that has the same name as your DSL, and then click Add New Connection Tool.
While the new tool is selected, in the Properties window:
Set the Caption and Tooltip.
Click Connection Builder and select the appropriate builder for the new relationship.
Set Toolbox Icon to the icon that you want to appear in the toolbox. You can set it to a new icon or an
icon already used for another tool.
To create a new icon, open Dsl\Resources in Solution Explorer. Copy and paste one of the existing
element tool BMP files. Rename the pasted copy, and then double-click to edit it.
Return to the DSL Definition diagram, select the tool, and in the Properties window click [...] in Toolbox
Icon. In the Select Bitmap dialog box, select your .BMP file from the drop-down menu.
To Te st a R e fe r e n c e R e l a t i o n sh i p a n d C o n n e c t o r

1. Click Transform All Templates in the toolbar of Solution Explorer, to generate the DSL designer code.
2. Build and run the DSL. Press F5 or CTRL+F5 to run a new instance of Visual Studio in experimental
mode. In the experimental instance of Visual Studio, open or create a file that has the file name extension
of your DSL.
3. Verify that the connection tool appears on the toolbox.
4. Create shapes by dragging from a tool onto the model diagram.
5. Create connections between the shapes. Click the connector tool, click a shape, and then click another
shape.
6. Verify that you cannot create connections between inappropriate classes. For example, if your
relationship is between Albums and Artists, verify that you cannot link Artists to Artists.
7. Verify that the multiplicities are correct. For example, verify that you cannot connect a Person
to more than one manager.
8. Verify that each text decorator appears, and that:
a. You can edit it, unless you have set the Is UI Read Only flag on the domain property.
b. When you edit the property either in the Properties window or in the decorator, the other view is
updated.
After you first test a connector, you might want to adjust some its properties and add some more
advanced features. For more information, see Customizing and Extending a Domain-Specific Language.

Defining Shapes that Contain Lists: Compartment Shapes


A compartment shape contains one or more lists of items. For example, in a Music Library DSL, you could use
compartment shapes to represent music Albums. In each Album, there is a list of Songs.

In the simplest method of achieving this effect in a DSL definition, you define one domain class for the
container, and one domain class for each list. The container class is mapped to the compartment shape.

For more information, see Properties of Compartment Shapes.


To define a Compartment Shape
1. Create the container domain class. Click the Embedding Relationship tool, click the root class of the
model, and then click a blank part of the DSL definition diagram. This creates the domain class that is
named Album in the example figure.
Alternatively instead of embedding in the root class, you can embed the container in a domain class that
is mapped to a swimlane.
Add a domain property such as Name to the class, and set its Is Element Name flag in the Properties
window.
2. Create the list item domain class. Click the Embedding Relationship tool, click the container class
(Album) and then click a blank part of the diagram. This creates the domain class that is named Song in
the example figure.
Add a domain property such as Title to the class, and set its Is Element Name flag.
Add other domain properties.
Add another list item domain class for each list that you want to display.
3. To mix several types of item in the list, create classes that inherit from the list class. Make the list
class abstract by setting its Inheritance Modifier.
For example, if you want classical music to be sorted by composer instead of artist, you could create two
subclasses of Song, ClassicalSong and NonClassicalSong.
4. Create the compartment shape. Drag from the Compartment Shape tool onto the DSL definition
diagram.
Add a text decorator and set its name.
Add a compartment and set its name.
5. To let the user hide the list compartments, right-click the compartment shape class, point to Add, and
then click Expand/Collapse Decorator. In the Properties window, set the position of the decorator.
6. Click the Diagram Element Map tool, click the container domain class, and then click the compartment
shape.
7. Select the diagram element map link between the domain class and the shape. In the DSL Details
window:
a. Click the Decorators tab. Click the name of the decorator and then select the appropriate item
under Display Property. Make sure that a check mark appears next to the name of the decorator.
b. Click the Compartment Maps tab.
Click the name of the compartment.
Under Displayed elements collection path, navigate to the list element class (Song). Click the
drop-down arrow to use the navigator tool.
Under Display Property, select the property that should be displayed in the list. In the example,
this is Title.

NOTE
By using the Path fields in the Decorator Map and Compartment map fields, you can make more complex relationships
between the domain classes and the compartment shape.

To define a tool for creating the shape


1. Make a toolbox item for creating elements of the domain class.
2. In DSL Explorer, expand the Editor node and all its sub-nodes.
3. Right-click the node under Toolbox Tabs that has the same name as your DSL, for example
MusicLibrary. Click Add Element Tool.
NOTE
If you right-click the Tools node, you will not see Add Element Tool. Instead, click the node above it.

4. In the Properties window with the new element tool selected, set Class to the domain class that you have
recently added.
5. Set Caption and Tooltip.
6. Set Toolbox Icon to an icon that will appear in the toolbox. You can set it to a new icon or an icon
already used for another tool.
To create a new icon, open Dsl\Resources in Solution Explorer. Copy and paste one of the existing
element tool .BMP files. Rename the pasted copy, and then double-click to edit it.
Return to the DSL Definition diagram, select the tool, and in the Properties window click [...] in Toolbox
Icon. In the Select Bitmap dialog box, select your BMP file from the drop-down menu.
To test a compartment shape
1. Click Transform All Templates in the toolbar of Solution Explorer, to generate the DSL designer code.
2. Build and run the DSL. Press F5 or CTRL+F5 to run a new instance of Visual Studio in experimental
mode. In the experimental instance of Visual Studio, open or create a file that has the file name extension
of your DSL.
3. Verify that the tool appears on the toolbox.
4. Drag the tool onto the model diagram. A shape is created.
Verify that the name of the element appears and is set automatically to a default value.
5. Right-click the header of the new shape, and then click Add Your List Item. In the example, the command
is Add Song.
Verify that an item appears in the list and that it has a new name.
6. Click one of the list items, and then examine the Properties window. You should see the properties of the
list items.
7. Open the language Explorer. Verify that you can see the container nodes with the list item nodes inside.

After you first test a compartment shape, you might want to adjust some of its properties and add some
more advanced features. For more information, see Customizing and Extending a Domain-Specific
Language.
Displaying a Reference Link in a Compartment
Usually, an element that you display in a compartment is a child of the element that is represented by the
compartment shape. But sometimes, you would like to display an element that is linked to it with a reference
relationship.
For example, we could add a second compartment to AlbumShape that displays a list of the Artists that are
linked to the Album.
In this case, the compartment should display the link, instead of the referenced element. This is because when
the user selects the item in the compartment and presses DELETE, you want the link to be deleted, not the
referenced element.
Nevertheless, you can have the name of the referenced element appear in the compartment.
The following procedure assumes that you have already created the domain class, the reference relationship,
the compartment shape, and the diagram element map, as described earlier in this section.
To d i sp l a y a r e fe r e n c e l i n k i n a c o m p a r t m e n t

1. Add a compartment to the compartment shape. On the DSL Definition diagram, right-click the
compartment shape class, point to Add, and then click Compartment.
2. Set Displayed elements collection path to navigate to the link, instead of its target element. Click the
drop-down menu and use the tree view to select the reference relationship instead of its target. In the
example, the relationship is ArtistAppearedOnAlbums.
3. Set Path to Display Property to navigate from the link to the target element. In the example, this is
Artist.
4. Set Display Property to the appropriate property of the target element, for example Name.
5. Transform All Templates, build and run the DSL, and open a test model.
6. In the model diagram, create the appropriate classes of shape, set their names, and create a link between
them. In the compartment shape, the names of linked elements should appear.
7. Select either the link or the item in the compartment shape. Both the link and the item should disappear.

Defining Ports on the Boundary of another Shape


A port is a shape that is located on the boundary of another shape.
Ports can also be used to provide a fixed connection point on another shape, to which the user can draw
connectors. In this case, you can make the port shape transparent.
To see an example that uses ports, select the Component Diagram template when you create a new DSL
solution. This example shows the main points that you can consider when you define ports:
There is a domain class that represents the container of the ports, Component .
There is a domain class that represents ports. In the example, this is ComponentPort .
There is an embedding relationship from the container domain class to the port domain class. For more
information, see Defining Domain Classes.
If you want different types of port to be mixed on the same container, you can create subclasses of the
port domain class. In the example, InPort and OutPort inherit from ComponentPort .
The container domain class can be mapped to any kind of shape. In the example, it is ComponentShape .
For more information, see Defining Shapes.
The port domain classes are mapped to port shapes. You can either map the derived classes to separate
port shape classes, or map the base class to one port shape class.
In other respects, port shapes behave as described in Defining Shapes.
For more information, see Properties of Port Shapes.
Defining a DSL that has Swimlanes
Swimlanes are a horizontal or vertical partition of a diagram. Each swimlane corresponds to a model element.
Your DSL definition requires one domain class for the swimlane elements.
The best way to create a DSL with swimlanes is to create a new DSL solution and choose the Task Flow solution
template. In the DSL Definition, the Actor class is the domain class mapped to the swimlane. Rename this and
the other classes to suit your project.
To add a class that will be displayed as a shape inside a swimlane, create an Embedding Relationship between
the swimlane class and your new class. Users will be able to drag elements from one swimlane to another, but
each element will always be inside a particular swimlane. In the Task Flow solution template, FlowElement is a
child of the swimlane class.
To add a class that will be displayed as a shape independently of swimlanes, create an Embedding Relationship
between the root class and your new class. Users will be able to place these shapes anywhere on the diagram,
including across the boundaries of swimlanes and outside the swimlanes. In the Task Flow solution template,
Comment is a child of the root class.
For more information, see Properties of Swimlanes.

Adding Property Types


Domain Enumerations and Literals
A domain enumeration is a type with several literal values.
To add a domain enumeration, right-click the root of the model in the DSL Explorer and then click Add New
Domain Enumeration. The element will appear in the DSL Explorer under the Domain Types node. This
element does not appear on the diagram.
To add enumeration literals to the domain enumeration, right-click the domain enumeration in the DSL
Explorer and then click Add New Enumeration Literal.
By default, a property that has an enumeration type can be set to only one value of the enumeration at a time. If
you want users and programmers to be able to set any combination of values - a "bit field" - set the IsFlags
property of the Enumeration.
External Types
When you set the type of a domain property, if you do not find the type you want in the Type drop-down list,
you can add an external type. For example, you could add the System.Drawing.Color type to the list.
To add a type, right-click the root of the model in DSL Explorer, and then click Add New External Type. In the
Properties window, set the name to Color and the namespace to System.Drawing. This type now appears in
DSL Explorer under Domain Types. You can choose it whenever you set the type of a domain property.

Customizing the DSL


Using the techniques described in this topic, you can quickly create a DSL with a diagrammatic notation, a
readable XML form, and the basic tools that are required to generate code and other artifacts.
There are two methods of extending the DSL definition:
1. Fine-tune the DSL by using more features of the DSL Definition. For example, you can make a single
connector tool that can create several types of connector, and you can control the rules by which deleting
one element also deletes related elements. These techniques are mostly achieved by setting values in the
DSL Definition, and some require a few lines of program code.
For more information, see Customizing and Extending a Domain-Specific Language.
2. Extend your modeling tools by using program code to achieve more advanced effects. For example, you
can create menu commands that can change the model, and you can create tools that integrate two or
more DSLs. VMSDK is designed specifically to make it easy to integrate your extensions with the code
that is generated from the DSL Definition. For more information, see Writing Code to Customise a
Domain-Specific Language.
Changing the DSL Definition
When you create any item in a DSL definition, many default values are set automatically. After they have been
set, you can change them. This simplifies the development of a DSL, while still allowing for powerful
customizations.
For example, when you map a shape to an element, the Parent Element Path of the mapping is automatically set
according to the embedding relationship of the domain class. However, if you later change the embedding
relationship, the parent element path is not changed automatically.
You should therefore be aware that when you change some relationships in your DSL Definition, it is not
unusual for errors to be reported either when you save the definition, or when you Transform All Templates.
Most of these errors are easy to fix. Double-click the error report to see the location of the error.
See also How to: Change the Namespace of a Domain-Specific Language.

Troubleshooting
The following table lists some of the most common problems that are encountered when you design a DSL,
together with suggestions for their solution. More advice is available on the Visualization Tools Extensibililty
Forum.

PROBLEM SUGGESTION

The changes I have made in the DSL Definition file have no Click Transform All Templates in the toolbar above
effect. Solution Explorer, and then rebuild the solution.

Shapes show the name of a decorator instead of the Set up the decorator mapping. On the DSL Definition
property value. diagram, click the diagram element map, which is the gray
line between the domain class and the shape class.

Open the DSL Details window. If you cannot see it, on the
View menu, point to Other Windows, and then click DSL
Details.

Click the Decorator Maps tab. Select the name of the


decorator. Make sure that the box next to it is checked.
Under Display property, select the name of a domain
property.

For more information, see Shapes on the Diagram.

In DSL Explorer, I cannot add to a collection. For example, Right-click the item above the node that you are trying.
when I right-click Tools, there is no "Add Tool" command in When you want to add to a list, the Add command is not in
the menu. the list node, but in its owner.

In the explorer for my DSL, I cannot add an element to a list.

I created a domain class, but I can't create instances in the Every domain class except the root must be the target of an
language explorer. embedding relationship.
PROBLEM SUGGESTION

In the explorer for my DSL, elements are shown only with In the DSL Definition, select a domain property of the class
their type names. and in the Properties window, set Is Element Name to true.

My DSL always opens in the XML editor. This can happen because of an error while the file was being
read. However, even after you fix that error, you must
explicitly reset the editor to be your DSL designer.

Right-click the project item, click Open With and select


YourLanguageDesigner (Default).

The toolbox of my DSL does not appear after I changed the Inspect and update
assembly names. DslPackage\GeneratedCode\Package.tt For more
information, see How to: Change the Namespace of a
Domain-Specific Language.

The toolbox of my DSL does not appear, but I have not Reset the experimental instance, and rebuild your solution.
changed the assembly name.
1. At the Windows Start menu, under All Programs,
Or, a message box appears reporting the failure to load an expand Visual Studio SDK, then Tools, and then click Reset
extension. the Microsoft Visual Studio Experimental Instance.
2. On the Visual StudioBuild menu, click Rebuild Solution.

See Also
Getting Started with Domain-Specific Languages
Creating a Windows Forms-Based Domain-Specific Language
Creating a WPF-Based Domain-Specific Language

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
Customizing and Extending a Domain-Specific
Language
10/31/2017 6 min to read Edit Online

Visual Studio Modeling and Visualization SDK (VMSDK) provides several levels at which you can define
modeling tools:
1. Define a domain-specific language (DSL) using the DSL Definition diagram. You can quickly create a DSL
with a diagrammatic notation, a readable XML form, and the basic tools that are required to generate
code and other artifacts.
For more information, see How to Define a Domain-Specific Language.
2. Fine-tune the DSL by using more advanced features of the DSL Definition. For example, you can make
additional links appear when the user creates an element. These techniques are mostly achieved in the
DSL Definition, and some require a few lines of program code.
3. Extend your modeling tools by using program code. VMSDK is designed specifically to make it easy to
integrate your extensions with the code that is generated from the DSL Definition. For more information,
see Writing Code to Customise a Domain-Specific Language.

NOTE
When you have updated the DSL Definitions file, do not forget to click Transform All Templates in the toolbar of
Solution Explorer before rebuilding your solution.

In this Section
TO ACHIEVE THIS EFFECT REFER TO THIS TOPIC

Allow the user to set the color and style properties of a Right-click the shape or connector class, point to Add
shape. Exposed, and click an item.

See Customizing Presentation on the Diagram.

Different classes of model element look similar on the Use inheritance between shapes or connector classes.
diagram, sharing properties such as initial height and width, Mappings between derived shapes and derived domain
color, tooltips. classes inherit the mapping details of the parents.

Or, map different domain classes to the same shape class.

A class of model element is displayed by different shapes Map more than one shape class to the same domain class.
contexts. When you build the solution, follow the error report and
provide the requested code to decide what shape to use.
TO ACHIEVE THIS EFFECT REFER TO THIS TOPIC

Shape color or other features such as font indicate current See Updating Shapes and Connectors to Reflect the Model.
state.
Create a rule that updates the exposed properties. See Rules
Propagate Changes Within the Model.

Or, use OnAssociatedPropertyChanged() to update non-


exposed features such as link arrows or font.

Icon on shape changes to indicate state. Set the visibility of the decorator mapping in the DSL Details
window. Locate several image decorators on the same
position. See Updating Shapes and Connectors to Reflect the
Model.

Or, override ImageField.GetDisplayImage() . See example


in ImageField.

Set a background image on any shape Override InitializeInstanceResources() to add an anchored


ImageField. See Customizing Presentation on the Diagram.

Nest shapes to any depth Set up a recursive embedding tree. Define BoundsRules to
contain the shapes. See Customizing Presentation on the
Diagram.

Attach connectors at fixed points on an element's boundary. Define embedded terminal elements, represented by small
ports on the diagram. Use BoundsRules to fix the ports in
place. See the Circuit Diagram sample at Visualization and
Modeling SDK.

Text field displays a value derived from other values. Map the text decorator to a Calculated or Custom Storage
domain property. For more information, see Calculated and
Custom Storage Properties.

Propagate changes between model elements, or between See Validation in a Domain-Specific Language.
shapes

Propagate changes to resources such as other Visual Studio See Event Handlers Propagate Changes Outside the Model.
extensions outside the store.

Property window displays properties of a related element. Set up Property Forwarding. See Customizing the Properties
Window.

Property categories The properties window is divided into sections called


categories. Set the Category of your domain properties.
Properties with the same category name will appear in the
same section. You can also set the Category of a
relationship role.

Control user access to domain properties Set Is Browsable false to prevent a domain property from
appearing in the Properties window at run time. You can still
map it to text decorators.

Is UI Read Only prevents users from changing a domain


property.

Program access to the domain property is not affected.


TO ACHIEVE THIS EFFECT REFER TO THIS TOPIC

Change the name, icon, and visibility of nodes in your DSL's See Customizing the Model Explorer.
model explorer.

Enable copy, cut and paste Set the Enable Copy Paste property of the Editor node in
DSL Explorer.

Copy reference links and their targets whenever an element Set the Propagates Copy property of the source role
is copied. For example, copy Comments attached to an item. (represented by the line at one side of the domain
relationship in the DSL Definition diagram).

Write code to override ProcessOnCopy to achieve more


complex effects.

See Customizing Copy Behavior.

Delete, reparent, or relink related elements when an element Set the Propagates Delete value of a relationship role. For
is deleted. more complex effects, override ShouldVisitRelationship
and ShouldVisitRolePlayer methods in the
MyDslDeleteClosure class, defined in DomainModel.cs

See Customizing Deletion Behavior

Preserve shape layout and appearance on copy and drag- Add the shapes and connectors to the copied
drop. ElementGroupPrototype . The most convenient method to
override is
ElementOperations.CreateElementGroupPrototype()

See Customizing Copy Behavior.

Paste shapes in a chosen location, such as the current cursor Override ClipboardCommandSet.ProcessOnCopy() to use
position. the location-specific version of
ElementOperations.Merge(). See Customizing Copy
Behavior.

Create additional links on paste Override


ClipboardCommandSet.ProcessOnPasteCommand()

Enable drag and drop from this diagram, other DSLs and See How to: Add a Drag-and-Drop Handler
Windows elements

Allow a shape or tool to be dragged onto a child shape, Define an Element Merge Directive on the target object
such as a port, as if it were dragged onto the parent. class, to forward the dropped object to the parent. See
Customizing Element Creation and Movement.

Allow a shape or tool to be dragged onto a shape and have Define an Element Merge Directive on the target domain
additional links or objects created. For example, to allow a class, and define the links to be generated. In complex cases,
comment to be dropped onto an item to which it is to be you can add custom code. See Customizing Element
linked. Creation and Movement.
TO ACHIEVE THIS EFFECT REFER TO THIS TOPIC

Create a group of elements with one tool. For example, a Override the toolbox initialization method in
component with a fixed set of ports. ToolboxHelper.cs. Create an Element Group Prototype (EGP)
containing the elements and their relationship links. See
Customizing Tools and the Toolbox.

Either include the principal and port shapes in the EGP, or


define BoundsRules to position the port shapes when the
EGP is instantiated. See BoundsRules Constrain Shape
Location and Size.

Use one connection tool to instantiate several types of Add Link Connect Directives (LCD) to the Connection Builder
relationship. that is invoked by the tool. The LCDs determine the type of
the relationship from the types of the two elements. To
make this depend on the states of the elements, you can
add custom code. See Customizing Tools and the Toolbox.

Sticky tools - the user can double-click any tool to create In DSL Explorer, select the Editor node. In the Properties
many shapes or connectors in succession. window, set Uses Sticky Toolbox Items.

Define menu commands See How to: Modify a Standard Menu Command

Constrain the model with validation rules See Validation in a Domain-Specific Language

Generate code, configuration files, or documents from a Generating Code from a Domain-Specific Language
DSL.

Customize how models are saved to file. See Customizing File Storage and XML Serialization

Save models to databases or other media. Override YourLanguageDocData

See Customizing File Storage and XML Serialization

Integrate several DSLs so that they work as part of one See Integrating Models by using Visual Studio Modelbus.
application.

Allow your DSL to be extended by third parties, and control Extend your DSL by using MEF
the extension.
Sharing Classes between DSLs by using a DSL Library

Defining a Locking Policy to Create Read-Only Segments

See Also
How to Define a Domain-Specific Language
Writing Code to Customise a Domain-Specific Language
Modeling SDK for Visual Studio - Domain-Specific Languages

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
Customizing Presentation on the Diagram
10/18/2017 1 min to read Edit Online

The diagram is the main presentation area of a domain-specific language designer. Usually, users will use the
diagram to construct their models. You can customize the diagram as described in the following topics.
Defining Shapes and Connectors
Controlling Color, Line Style, and other Shape Properties
Controlling the Visibility of an Icon or Decorator
Customizing Text and Image Fields
Setting a Background Image on a Diagram
Nesting Shapes
Embedding a Diagram in a Windows Form

See Also
Navigating and Updating a Model in Program Code
Defining Shapes and Connectors
10/18/2017 2 min to read Edit Online

There are several basic types of shapes that you can used to display information on a diagram in a domain-specific
language (DSL).

Basic Types of Shapes and Connectors


A DSL diagram shows a collection of shapes interlinked by lines or connectors. Typically, but not always:
Shapes are the visible representation of model elements.
Connectors represent reference relationships.
The diagram represents the model root instance.
Embedding relationships between model elements are shown by containment. For example, elements
representing component ports are embedded in the component.
These patterns are not enforced, but are more strongly supported. When you design a DSL, bear in mind
that the design of the embedding relationships should be influenced by how you want to present the model
on the screen. By contrast, the reference relationships should reflect the concepts of your business domain.
The following types of shapes are available:

SHAPE TYPE DESCRIPTION

Geometry shape General purpose rectangular or elliptical shape. You can


display text and icon decorators in specific positions relative to
the bounds of the shape.

To nest shapes inside geometry shapes, see Nesting Shapes.

Compartment shape Rectangle containing a header and compartments, like a UML


class. Each compartment can contain a list of text rows.

The rows typically represent elements embedded under the


element represented by the shape. For an example, create a
DSL from the Class Diagrams solution template.

Image shape Shape that displays an image.


SHAPE TYPE DESCRIPTION

Port shape A small rectangle designed to attach to the outline of another


shape. Typically used in component models.

The model element represented by a port is typically


embedded under the element represented by the parent
shape. For an example, create a DSL by using the Components
solution template.

By default, a port shape can slide along the sides of its parent.
You can define a Bounds Rule to constrain it to a particular
position.

By making a port shape very small and transparent, you can


use it to provide a fixed connection point on the surface of its
parent shape.

Swimlanes Swimlanes partition a diagram into horizontal or vertical


segments. The swimlane always stays underneath the other
shapes on the diagram.

Typically the model elements of the swimlane are parented on


the model root, and the other elements are parented on
them. For an example, create a DSL from the Task Flow
solution template.

Connectors The lines drawn between shapes typically represent reference


relationships. You can set options to make a connector
straight or rectilinear, and to have different types of
arrowhead.

Shape Inheritance
A shape can inherit from another shape. However, the shapes must be the same kind. For example, only a
geometry shape can inherit from a geometry shape. Inherited shapes have the compartments and decorators of
their base shape. Connectors can inherit from connectors.
Controlling Color, Line Style, and other Shape
Properties
10/18/2017 1 min to read Edit Online

Some shape properties such as color can be 'exposed' - that is, linked to a domain property of the shape. Others
have to be controlled directly.

Exposing a property
Some shape properties such as color can be linked to the value of a domain property.
In the DSL Definition, select a shape, connector or diagram class. On its context menu, choose Add Exposed, and
then choose the property you want, such as Fill Color.
The shape now has a domain property that you can set in program code or as a user.

Dynamically updating an exposed property


Typically you want to make the exposed property dependent on another property. For example, you might want a
shape to turn red whenever a particular domain property is less than zero. To make this dependency, create a rule.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
namespace ExampleNamespace
{
// Attribute associates the rule with class:
[RuleOn(typeof(CarShape), FireTime = TimeToFire.TopLevelCommit)]
// The rule is a class derived from one of the abstract rules:
class CarShapeAddRule : AddRule
{
// Override the abstract method:
public override void ElementAdded(ElementAddedEventArgs e)
{
base.ElementAdded(e);
CarShape shape = e.ModelElement as CarShape;
Store store = shape.Store;
// Ignore this call if we're currently loading a model:
if (store.TransactionManager.CurrentTransaction.IsSerializing)
return;
Car car = shape.ModelElement as Car;
// Code here propagates change as required - for example:
shape.FillColor = car.Somebool ? System.Drawing.Color.Red : System.Drawing.Color.Green;
}
}
// The rule must be registered:
public partial class ExampleDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(CarShapeAddRule));
// If you add more rules, list them here.
return types.ToArray();
}
}
}
Controlling the Visibility of an Icon or Decorator
10/18/2017 3 min to read Edit Online

A decorator is an icon or line of text that appears on a shape in a domain-specific language (DSL). You can make
the decorator appear and disappear depending on the state of properties in the model. For example, on a shape
representing a Person, you could have different icons that appear depending on the person's gender, number of
children, and so on.

Controlling the visibility of an icon or decorator


The following procedure assumes that you have already defined a shape and its mapping to a domain class. For
more information, see How to Define a Domain-Specific Language.
To control the visibility of an icon or text decorator
1. In the DSL Definition diagram, add to the shape class the icons or text decorators that you want to appear.
a. Right-click the shape class, point to Add, and then click the required type of decorator.
b. Set the decorator's Position property. More than one decorator can have the same position. For
example, you could have icons for male and female sharing the same position.
c. Set the Default Icon property of an icon decorator.
2. Select the diagram element map, which is the gray line between the shape class and the domain class on the
DSL Definition diagram.
3. In the DSL Details window, in the Decorator Maps tab, select a decorator. For example, the MaleDecorator.
4. Check the Visibility Filter box.
5. If the domain property that should control visibility is on the immediate domain class, leave Path to Filter
Property blank.
Otherwise, click the drop-down menu and navigate to the relationship or class where the property is located.
To avoid an error report, you should not navigate through a relationship marked with "*" in the
navigation tool.
6. Set the Filter Property to a domain property. For example, Gender.
7. In the Visibility Entries list, add values of this domain property for which the decorator should be visible.
For example, Male.
8. Repeat the steps for each icon.
9. Transform All Templates, build and run, and open a test diagram.
10. When you change the controlling property value, the decorators should appear and disappear.
Frequently, you want visibility to be controlled by a more complex formula than a simple set of values. For
example, to have an icon depend on the number of links of a particular type, or to make it depend on a
whether a number is in a particular range. In that case, use the following procedure.
To control the visibility of a decorator based on a formula
1. Add a calculated domain property to the domain class. In the Properties window, set the following values:
IsBrowsable = False - this hides the property from the user
Kind = Calculated - this means that you will provide code that calculates its value
Name for example DecoratorControl
Type = Boolean

For more information, see Calculated and Custom Storage Properties.


2. Make the new property control the decorator visibility.
a. Select the diagram element map, which is the gray line from the domain class to the shape. In the DSL
Details window, open the DecoratorMap tab.
b. Check the Visibility Filter box.
c. In Filter Property, select the control property DecoratorControl.
d. Under Visibility Entries, enter True .
3. Click Transform All Templates in the Solution Explorer toolbar.
4. Click Build Solution on the Build menu.
5. Double-click the error report that has appeared: "YourClass does not contain a definition for
GetDecoratorControlValue ...".
The text editor opens on Dsl\GeneratedCode\DomainClasses.cs. Above the highlighted error is a comment
that requests you to add a method.
6. Note the namespace, class and method that are missing. For example,
Company.FamilyTree.Person.GetDecoratorControlValue().
7. In a separate code file, write a partial class definition that contains the missing method. For example:

namespace Company.FamilyTree
{ partial class Person
{ bool GetDecoratorControlValue()
{
return this.Children.Count > 0;
} } }

For more information about customizing the model with program code, see Navigating and Updating a
Model in Program Code.
8. Rebuild and run the solution.

See Also
Defining Shapes and Connectors
Setting a Background Image on a Diagram
Navigating and Updating a Model in Program Code
Writing Code to Customise a Domain-Specific Language
Customizing Text and Image Fields
10/18/2017 8 min to read Edit Online

When you define a text decorator in a shape, it is represented by a TextField. For examples of the initialization of
TextFields and other ShapeFields, inspect Dsl\GeneratedCode\Shapes.cs in your DSL solution.
A TextField is an object that manages an area within a shape, such as the space assigned to a label. One TextField
instance is shared between many shapes of the same class. The TextField instance does not store the text of the
label separately for each instance: instead, the GetDisplayText(ShapeElement) method takes the shape as a
parameter, and can look up the text dependent on the current state of the shape and its model element.

How the appearance of a text field is determined


The DoPaint() method is called to displays the field on the screen. You can either override the default DoPaint(),
or you can override some of the methods that it calls. The following simplified version of the default methods can
help you understand how to override the default behavior:
// Simplified version:
public override void DoPaint(DiagramPaintEventArgs e, ShapeElement parentShape)
{
string text = GetDisplayText(shape);
StringFormat format = GetStringFormat(parentShape);
Brush brush = GetTextBrush(e.View, shape);
using (Font font = GetFont(shape))
{
e.Graphics.DrawString(text, font, brush, format);
}
}
// StringFormat determines whether the string is centered etc.
// To customize statically for all instances of this shape field,
// assign to DefaultStringFormat.
// To customize dynamically or per shape, override this:
public virtual StringFormat GetStringFormat(ShapeElement shape)
{ return DefaultStringFormat; }

// Override to customize the displayed string:


public virtual string GetDisplayText(ShapeElement shape)
{ return this.GetValue(shape).ToString(); }

// Brush determines the text color.


// To change the brush for every field, change the shape's styleset.
// To customize to a brush in the style set, override GetTextBrushId.
// To change the brush to non-standard color, override this.
// Should take account of whether selected.
public virtual Brush GetTextBrush(DiagramClientView view, ShapeElement shape)
{ return shape.StyleSet.GetBrush(this.GetTextBrushId(view, shape)); }

// Brush ID selects a brush from a StyleSet.


// Either return a member of DiagramBrushes
// or add your own brush to the shape or application's styleset.
// Override this to change dynamically or per instance.
// To change statically, just assign to default values.
public virtual StyleSetResourceId GetTextBrushId(DiagramClientView view, ShapeElement shape)
{ return IsSelected(view, shape) ? (view.Focused ? DefaultSelectedTextBrushId
: DefaultInactiveSelectedTextBrushId ) : DefaultTextBrushId ;
}

// Font determines the shape and size of the text.


// To change the font for every field, change the shape's styleset.
// To customize to a font in the style set, override GetFontId.
// To change the font to a non-standard font, override this.
public virtual Font GetFont(ShapeElement shape)
{ return shape.StyleSet.GetFont(GetFontId(shape)); }

// Selects a font from a styleset.


// Either return a member of DiagramFonts or
// add your own font to the shape or application's styleset.
// To change statically for all instances of this field,
// assign to DefaultFontId.
// To change per shape or dynamically, override this.
public virtual StyleSetResourceId GetFontId(ShapeElement parentShape)
{ return DefaultFontId; }

There are several other pairs of Getmethods and Default properties, such as
DefaultMultipleLine/GetMultipleLine() . You can assign a value to the Default property to change the value for all
instances of the shape field. To make the value vary from one shape instance to another, or dependent on the state
of the shape or its model element, override the Get method.

Static customizations
If you want to change every instance of this shape field, first find out whether you can set the property in the DSL
Definition. For example, you can set font size and style in the Properties window.
If not, then override the InitializeShapeFields method of your shape class, and assign a value to the appropriate
Default... property of the text field.

WARNING
To override InitializeShapeFields() , you must set the Generates Double Derived property of the shape class to
true in the DSL Definition.

In this example, a shape has a text field that will be used for user comments. We want to use the standard comment
font. Because it is a standard font from the style set, we can set the default font id:

partial class ExampleShape


{ protected override void InitializeShapeFields(IList<ShapeField> shapeFields)
{
// Fields set up according to DSL Definition:
base.InitializeShapeFields(shapeFields);
// Find and update comment field:
TextField commentField = ShapeElement.FindShapeField(shapeFields, "CommentDecorator") as TextField;
// Use the standard font for comments:
commentField.DefaultFontId = DiagramFonts.CommentText;

Dynamic customizations
To make the appearance vary dependent on the state of a shape or its model element, derive your own subclass of
TextField and override one or more Get... methods. You must also override your shape's InitializeShapeFields
method, and replace the instance of the TextField with an instance of your own class.
The following example makes the font of a text field dependent on the state of a Boolean domain property of the
shape's model element.
To run this example code, create a new DSL solution using the Minimal Language template. Add a Boolean domain
property AlternateState to the ExampleElement domain class. Add an icon decorator to the ExampleShape class,
and set its image to a bitmap file. Click Transform All Templates. Add a new code file in the DSL project, and
insert the following code.
To test the code, press F5 and, in the debugging solution, open a sample diagram. The default state of the icon
should appear. Select the shape and in the Properties window, change the value of the AlternateState property.
The font of the element name should change.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
...

partial class ExampleShape


{
/// <summary>
/// Compose a list of the fields in this shape.
/// Called once for each shape class.
/// </summary>
protected override void InitializeShapeFields(IList<ShapeField> shapeFields)
{
// Fields set up according to DSL Definition:
base.InitializeShapeFields(shapeFields);
// Replace the text field for NameDecorator:
TextField oldField = ShapeElement.FindShapeField(shapeFields, "NameDecorator") as TextField;
shapeFields.Remove(oldField);
// Replace with my text field based on DSL Definition values:
MyTextField newField = new MyTextField(oldField);
shapeFields.Add(newField);
}
}
/// <summary>
/// Dynamic font depends on state of model element.
/// </summary>
public class MyTextField : TextField
{
public MyTextField(TextField prototype)
: base(prototype.Name)
{
DefaultText = prototype.DefaultText;
DefaultFocusable = prototype.DefaultFocusable;
DefaultAutoSize = prototype.DefaultAutoSize;
AnchoringBehavior.MinimumHeightInLines = prototype.AnchoringBehavior.MinimumHeightInLines;
AnchoringBehavior.MinimumWidthInCharacters = prototype.AnchoringBehavior.MinimumWidthInCharacters;
DefaultAccessibleState = prototype.DefaultAccessibleState;
}

public override System.Drawing.Font GetFont(ShapeElement parentShape)


{
// Access the Boolean domain property of the model element:
if ((parentShape.ModelElement as ExampleElement).AlternateState)
return new System.Drawing.Font("Callisto", 14.0f,
System.Drawing.FontStyle.Italic |
System.Drawing.FontStyle.Bold);
else
return base.GetFont(parentShape);
}

Style sets
The preceding example shows how you can change the text field to any font that is available. However, a preferable
method is to change it to one of a set of styles that is associated with the shape or with the application. To do this,
you override GetFontId or GetTextBrushId().
Alternatively, consider changing the style set of your shape by overriding InitializeResources. This has the effect of
changing the fonts and brushes for all of the shape fields.

Customizing Image Fields


When you define an image decorator in a shape, and when you define an image shape, the area in which the shape
is displayed is managed by an ImageField. For examples of the initialization of ImageFields and other ShapeFields,
inspect Dsl\GeneratedCode\Shapes.cs in your DSL solution.
An ImageField is an object that manages an area within a shape, such as the space assigned to a decorator. One
ImageField instance is shared between many shapes of the same shape class. The ImageField instance does not
store a separate image for each shape: instead, the GetDisplayImage(ShapeElement) method takes the shape as a
parameter, and can look up the image dependent on the current state of the shape and its model element.
If you want special behavior such as a variable image, you can create your own class derived from ImageField.
To create a subclass of ImageField
1. Set the Generates Double Derived property of the parent shape class in your DSL Definition.
2. Override the InitializeShapeFields method of your shape class.
Create a new code file in the DSL project, and write a partial class definition for the shape class. Override
the method definition there.
3. Inspect the code of InitializeShapeFields in DSL\GeneratedCode\Shapes.cs.
In your override method, call the base method and then create an instance of your own image field class.
Use this to replace the regular image field in the shapeFields list.

Dynamic icons
This example makes an icon change dependent on the state of the shape's model element.

WARNING
This example demonstrates how to make a dynamic image decorator. But if you only want to switch between one or two
images depending on the state of a model variable, it is simpler to create several image decorators, locate them in the same
position on the shape, and then set the Visibility filter to depend on specific values of the model variable. To set this filter,
select the shape map in the DSL Definition, open the DSL Details window, and click the Decorators tab.

To run this example code, create a new DSL solution using the Minimal Language template. Add a Boolean domain
property AlternateState to the ExampleElement domain class. Add an icon decorator to the ExampleShape class,
and set its image to a bitmap file. Click Transform All Templates. Add a new code file in the DSL project, and
insert the following code.
To test the code, press F5 and, in the debugging solution, open a sample diagram. The default state of the icon
should appear. Select the shape and in the Properties window, change the value of the AlternateState property.
The icon should then appear rotated through 90 degrees, on that shape.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
...
partial class ExampleShape
{
/// <summary>
/// Compose a list of the fields in this shape.
/// Called once for each shape class.
/// </summary>
/// <param name="shapeFields"></param>
protected override void InitializeShapeFields(IList<ShapeField> shapeFields)
{
// Fields set up according to DSL Definition:
base.InitializeShapeFields(shapeFields);

// Replace the image field:


ShapeField oldField = ShapeElement.FindShapeField(shapeFields, "IconDecorator");
shapeFields.Remove(oldField);
// Must keep the same name:
MyImageField newField = new MyImageField(oldField.Name);
shapeFields.Add(newField);
newField.DefaultImage = (oldField as ImageField).DefaultImage.Clone() as System.Drawing.Image;
}
}

public class MyImageField : ImageField


{
public MyImageField(string tag) : base(tag) { }

/// <summary>
/// Get the image for this field in the given shape.
/// </summary>
public override System.Drawing.Image GetDisplayImage(ShapeElement parentShape)
{
ExampleElement element = parentShape.ModelElement as ExampleElement;
if (element.AlternateState == true)
return AlternateImage;
else
return base.GetDisplayImage(parentShape);
}

private System.Drawing.Image alternateImage;


public System.Drawing.Image AlternateImage
{
get
{
if (alternateImage == null)
{
// Alternate image is a copy of the default, rotated by 90 degrees:
alternateImage = this.DefaultImage.Clone() as System.Drawing.Image;
alternateImage.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
}
return alternateImage;
}
}
}
}

See Also
Defining Shapes and Connectors
Setting a Background Image on a Diagram
Navigating and Updating a Model in Program Code
Writing Code to Customise a Domain-Specific Language
Setting a Background Image on a Diagram
10/18/2017 2 min to read Edit Online

In Visual Studio Visualization and Modeling SDK, you can set the background image for a generated designer by
using custom code.

Setting the background image


To set a background image for a generated designer
1. Copy the image file that you want to use as the diagram's background into the Dsl\Resources directory for
the current project.
2. In Solution Explorer, right-click the Dsl\Resources folder, point to Add, and then click Existing Item.
3. In the Add Existing Item dialog box, browse to the Dsl\Resources folder.
4. In the Files of type list, click Image Files.
5. Click the image file that you copied to the directory, and then click Add.
6. Right-click Dsl, and click Properties to open the properties of the Dsl project.
7. On the Resources tab, click This project does not contain a default resources file. Click here to create
one.
8. Add the image file to the resource file by dragging the picture from Solution Explorer into the resources
window.
9. Open the File menu, and click the option to save the project properties.
10. Verify that the file Dsl\Properties\Resources.resx exists and has the file Resources.Designer.cs under it.
11. If Resources.Designer.cs is missing, click the file Resources.resx in Solution Explorer.
12. In the Properties window, set the Custom Tool property to ResXFileCodeGenerator .
13. In Solution Explorer, right-click the Dsl project, point to Add, and click New Folder.
14. Name the folder Custom.
15. Right-click the Custom folder, point to Add, and click New Item.
16. In the Add New Item dialog box, in the Templates list, click Code File.
17. In the Name box, type BackgroundImage.cs , and click Add.
18. Copy the following code to the BackgroundImage.cs file, adjusting namespace, diagram class name, and
image file resource name.
Replace "MyDiagramClass" with the name of the diagram partial class that is defined in
Dsl\GeneratedCode\Diagrams.cs. You can also retrieve the correct namespace from the file
Dsl\GeneratedCode\Diagrams.cs.
using System;
using Microsoft.VisualStudio.Modeling.Diagrams;

// Fix the namespace:


namespace Fabrikam.MyLanguage
{
// Fix the Diagram Class name - get it from GeneratedCode\Diagram.cs

public partial class Language29Diagram


{
protected override void InitializeInstanceResources()
{
// Fix the Resources namespace and the Image resource name:
ImageField backgroundField = new ImageField("background",
Fabrikam.MyLanguage.Properties.Resources.MyPicture);

backgroundField.DefaultFocusable = false;
backgroundField.DefaultSelectable = false;
backgroundField.DefaultVisibility = true;
backgroundField.DefaultUnscaled = false;

shapeFields.Add(backgroundField);

backgroundField.AnchoringBehavior
.SetTopAnchor(AnchoringBehavior.Edge.Top, 0.01);
backgroundField.AnchoringBehavior
.SetLeftAnchor(AnchoringBehavior.Edge.Left, 0.01);
backgroundField.AnchoringBehavior
.SetRightAnchor(AnchoringBehavior.Edge.Right, 0.01);
backgroundField.AnchoringBehavior
.SetBottomAnchor(AnchoringBehavior.Edge.Bottom, 0.01);

base.InitializeInstanceResources();
}
}
}

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

See Also
Defining Shapes and Connectors
Customizing Text and Image Fields
Navigating and Updating a Model in Program Code
Writing Code to Customise a Domain-Specific Language

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Nesting Shapes
10/18/2017 1 min to read Edit Online

See Jean-Marc Prieur's blog


Embedding a Diagram in a Windows Form
10/18/2017 2 min to read Edit Online

You can embed a DSL diagram in a Windows Control, which appears in the Visual Studio window.

Embedding a Diagram
To embed a DSL diagram in a Windows Control
1. Add a new User Control file to the DslPackage project.
2. Add a Panel control to the User Control. This panel will contain the DSL Diagram.
Add other controls that you require.
Set the Anchor properties of the controls.
3. In Solution Explorer, right-click the user control file and click View Code. Add this constructor and variable
to the code:

internal UserControl1(MyDSLDocView docView, Control content)


: this()
{
panel1.Controls.Add(content);
this.docView = docView;
}
private MyDSLDSLDocView docView;

4. Add a new file to the DslPackage project, with the following content:

using System.Windows.Forms;
namespace Company.MyDSL
{
partial class MyDSLDocView
{
private UserControl1 container;
public override IWin32Window Window
{
get
{
if (container == null)
{
// Put our own form inside the DSL window:
container = new UserControl1(this,
(Control)base.Window);
}
return container;
} } } }

5. To test the DSL, press F5 and open a sample model file. The diagram appears inside the control. The toolbox
and other features work normally.
Updating the Form using Store Events
1. In the form designer, add a ListBox named listBox1 . This will display a list of the elements in the model. It
will be kept in synchronism with the model using store events. For more information, see Event Handlers
Propagate Changes Outside the Model.
2. In the custom code file, override further methods to the DocView class:

partial class MyDSLDocView


{
/// <summary>
/// Register store event listeners.
/// This method is called when the model and diagram
/// have completed loading.
/// </summary>
protected override bool LoadView()
{
bool result = base.LoadView();
Store store = this.DocData.Store;
EventManagerDirectory emd = store.EventManagerDirectory;
DomainClassInfo componentClass = store.DomainDataDirectory.FindDomainClass(typeof(ExampleElement));
emd.ElementAdded.Add(componentClass, new EventHandler<ElementAddedEventArgs>(AddElement));
emd.ElementDeleted.Add(componentClass, new EventHandler<ElementDeletedEventArgs>(RemoveElement));

// Do the initial parts list:


container.SetUpFormFromModel();
return result;
}
/// <summary>
/// Listener method called on creation of each instance of
/// the ExampleElement class or its subclasses.
/// </summary>
private void AddElement(object sender, ElementAddedEventArgs e)
{
container.Add(e.ModelElement as ExampleElement);
}
/// <summary>
/// Listener method called after deletion of each instance of
/// the ExampleElement class or its subclasses.
/// </summary>
private void RemoveElement(object sender, ElementDeletedEventArgs e)
{
container.Remove(e.ModelElement as ExampleElement);
}

3. In the code behind the user control, insert methods to listen for elements added and removed:
public partial class UserControl1 : UserControl { ...

private ExampleModel modelRoot;

internal void Add(ExampleElement e) { UpdatePartsList(); }


internal void Remove(ExampleElement e) { UpdatePartsList(); }

internal void SetUpFormFromModel()


{
modelRoot = this.docView.CurrentDiagram.ModelElement as ExampleModel;
UpdatePartsList();
}

private void UpdatePartsList()


{
StringBuilder builder = new StringBuilder();
listBox1.Items.Clear();
foreach (ExampleElement c in modelRoot.Elements)
{
listBox1.Items.Add(c.Name);
}
}

4. To test the DSL, press F5 and in the experimental instance of Visual Studio, open a sample model file.
Notice that the list box shows a list of the elements in the model, and that it is correct after any addition or
deletion, and after Undo and Redo.

See Also
Navigating and Updating a Model in Program Code
Writing Code to Customise a Domain-Specific Language
Customizing Tools and the Toolbox
10/18/2017 12 min to read Edit Online

You must define toolbox items for the elements that you want to let users add to their models. There are two kinds
of tools: element tools and connection tools. In the generated designer, a user can select an element tool to drag
shapes to the diagram, and can select a connection tool to draw links between the shapes. In general, element tools
let users add instances of domain classes to their models, and connection tools let them add instances of domain
relationships.
In this topic:
How the Toolbox is Defined
Customizing Element Tools
Creating Groups of Elements from a Tool
Customizing Connection Tools

How the toolbox is defined


In the DSL Explorer, expand the Editor node and the nodes underneath it. Typically you will see a hierarchy that
resembles this:

Editor
Toobox Tabs
MyDsl //a tab
Tools
ExampleElement // an element tool
ExampleRelationship // a connection tool

In this part of DSL Explorer, you can:


Create new tabs. Tabs define the section headings in the toolbox.
Create new tools.
Copy and paste tools.
Move tools up or down in the list.
Delete tabs and tools.

IMPORTANT
To add or paste items in a DSL Explorer, right-click the grandparent of the new node. For example, to add a tool, right-click
the tab, and not the Tools node. To add a tab, right-click the Editor node.

The Toolbox Icon property of every tool references a 16x16 bitmap file. These files are usually kept in the
Dsl\Resources folder.
The Class property of an element tool refers to a concrete domain class. By default, the tool will create instances of
this class. However, you can write code to have the tool create groups of elements, or elements of different types.
The Connection Builder property of a connection tool refers to a connection builder, which defines what types of
elements the tool can connect, and what relationships it creates between them. Connection builders are defined as
nodes in the DSL Explorer. Connection builders are created automatically when you define domain relationships,
but you can write code to customize them.
To add a tool to the toolbox
1. You usually create an element tool after you have created a shape class and mapped it to a domain class.
You usually create a connector tool after you have created a connector class and mapped it to a reference
relationship.
2. In DSL Explorer, expand the Editor node and the Toolbox Tabs node.
Right-click a toolbox tab node, and then click Add New Element Tool or Add New Connection Tool.
3. Set the Toolbox Icon property to refer to a 16x16 bitmap.
If you want to define a new icon, create a bitmap file in Solution Explorer in the Dsl\Resources folder. The
file should have the following property values: Build Action = Content; Copy to Output Directory = Do
not copy.
4. For an element tool: Set the Class property of the tool to refer to a concrete domain class that is mapped
to a shape.
For a connector tool: Set the Connection Builder property of the tool to one of the items that are offered
in the drop-down list. Connection builders are automatically created when you map a connector to a
domain relationship. If you have recently created a connector, you would normally select the associated
connection builder.
5. To test the DSL, press F5 or CTRL+F5, and in the experimental instance of Visual Studio, open a sample
model file. The new tool should appear on the toolbox. Drag it onto the diagram to verify that it creates a
new element.
If the tool does not appear, stop the experimental Visual Studio. In the Windows Start menu, run Reset the
Microsoft Visual Studio 2010 Experimental Instance. On the Visual StudioBuild menu, click Rebuild
Solution. Then test the DSL again.

Customizing Element Tools


By default, the tool will create a single instance of the specified class, but you can vary this in two ways:
Define Element Merge Directives on other classes, enabling them to accept new instances of this class, and
enabling them to create additional links when the new element is created. For example, you could allow the
user to drop a Comment onto another element, and thereby create a reference link between the two.
These customizations also affect what happens when the user pastes or drags and drops an element.
For more information, see Customizing Element Creation and Movement.
Write code to customize the tool so that it can create groups of elements. The tool is initialized by methods
in ToolboxHelper.cs that you can override. For more information, see Creating Groups of Elements from a
Tool.

Creating Groups of Elements from a Tool


Each element tool contains a prototype of the elements that it should create. By default, each element tool creates a
single element, but it is also possible to create a group of related objects with one tool. To do this, you initialize the
tool with an ElementGroupPrototype that contains the related items.
The following example is taken from a DSL in which there is a type Transistor. Each Transistor has three named
Terminals. The element tool for Transistors stores a prototype containing four model elements and three
relationship links. When the user drags the tool onto the diagram, the prototype is instantiated and linked to the
model root.
This code overrides a method that is defined in Dsl\GeneratedCode\ToolboxHelper.cs.
For more information about customizing the model by using program code, see Navigating and Updating a Model
in Program Code.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

public partial class CircuitsToolboxHelper


{
/// <summary>
/// Toolbox initialization, called for each element tool on the toolbox.
/// This version deals with each Component subtype separately.
/// </summary>
/// <param name="store"></param>
/// <param name="domainClassId">Identifies the domain class this tool should instantiate.</param>
/// <returns>prototype of the object or group of objects to be created by tool</returns>
protected override ElementGroupPrototype CreateElementToolPrototype(Store store, Guid domainClassId)
{
if (domainClassId == Transistor.DomainClassId)
{
Transistor transistor = new Transistor(store);

transistor.Base = new ComponentTerminal(store);


transistor.Collector = new ComponentTerminal(store);
transistor.Emitter = new ComponentTerminal(store);

transistor.Base.Name = "base";
transistor.Collector.Name = "collector";
transistor.Emitter.Name = "emitter";

// Create an ElementGroup for the Toolbox.


ElementGroup elementGroup = new ElementGroup(store.DefaultPartition);
elementGroup.AddGraph(transistor, true);
// AddGraph includes the embedded parts

return elementGroup.CreatePrototype();
}
else
{
return base.CreateElementToolPrototype(store, domainClassId);
} } }

Customizing Connection Tools


Usually, you create an element tool when you create a new connector class. Alternatively, you can overload one
tool by allowing the types of the two ends to determine the type of the relationship. For example, you could define
one connection tool that could create both Person-Person relationships and Person-Town relationships.
Connection tools invoke connection builders. Use connection builders to specify how users can link elements in the
generated designer. Connection builders specify the elements that can be linked and the kind of link that is created
between them.
When you create a reference relationship between domain classes, a connection builder is automatically created.
You can use this connection builder when you map a connection tool. For more information about how to create
connection tools, see Configuring the Toolbox.
You can modify the default connection builder so that it can deal with a different range of source and target types,
and create different types of relationship.
You also can write custom code for connection builders to specify the source and target classes for the connection,
define the type of connection to be made, and take other actions associated with the creation of a connection.
The Structure of Connection Builders
Connection builders contain one or more link connect directives, which specify the domain relationship and the
source and target elements. For example, in the Task Flow solution template, you can see the
CommentReferencesSubjectsBuilder in the DSL Explorer. This connection builder contains one link connect
directive named CommentReferencesSubjects, which is mapped to the domain relationship
CommentReferencesSubjects. This link connect directive contains a source role directive that points to the
Comment domain class, and a target role directive that points to the FlowElement domain class.

Using Connection Builders to Restrict Source and Target Roles


You can use connection builders to restrict the occurrence of certain classes in either the source role or the target
role of a given domain relationship. For example, you may have a base domain class that has a domain
relationship to another domain class, but you may not want all derived classes of the base class to have the same
roles in that relationship. In the Task Flow solution, there are four concrete domain classes (StartPoint, EndPoint,
MergeBranch, and Synchronization) that inherit directly from the abstract domain class FlowElement, and two
concrete domain classes (Task and ObjectInState) that inherit indirectly from it. There is also a Flow reference
relationship that takes FlowElement domain classes in both its source role and target role. However, an instance
of an EndPoint domain class should not be the source of an instance of a Flow relationship, nor should an
instance of a StartPoint class be the target of an instance of a Flow relationship. The FlowBuilder connection
builder has a link connect directive named Flow that specifies which domain classes can play the source role (Task,
MergeBranch, StartPoint, and Synchronization) and which can play the target role(MergeBranch, Endpoint,
and Synchronization).
Connection Builders with Multiple Link Connect Directives
You can add more than one link connect directive to a connection builder. This can help you hide some of the
complexities of the domain model from users and keep the Toolbox from getting too cluttered. You can add link
connect directives for several different domain relationships to a single connection builder. However, you should
combine domain relationships when they perform approximately the same function.
In the Task Flow solution, the Flow connection tool is used to draw instances of both the Flow and the
ObjectFlow domain relationships. The FlowBuilder connection builder has, in addition to the Flow link connect
directive described earlier, two link connect directives named ObjectFlow. These directives specify that an instance
of an ObjectFlow relationship may be drawn between instances of the ObjectInState domain class, or from an
instance of an ObjectInState to an instance of a Task, but not between two instances of a Task, or from an
instance of a Task to an instance of an ObjectInState. However, an instance of a Flow relationship may be drawn
between two instances of a Task. If you compile and run the Task Flow solution, you can see that drawing a Flow
from an instance of an ObjectInState to an instance of a Task creates an instance of an ObjectFlow, but drawing
a Flow between two instances of a Task creates an instance of a Flow.
Custom Code for Connection Builders
There are four checkboxes in the user interface that define different types of customization of connection builders:
the Custom accept check box on a source or target role directive
the Custom connect check box on a source or target role directive
the Uses custom connect check box on a connect directive
the Is Custom property of the connection builder
You have to provide some program code to make these customizations. To discover what code you must
supply, check one of these boxes, click Transform All Templates, and then build your solution. An error
report will result. Double-click the error report to see a comment that explains what code you should add.

NOTE
To add custom code, create a partial class definition in a code file separate from the code files in the GeneratedCode folders.
To avoid losing your work, you should not edit the generated code files. For more information, see Overriding and Extending
the Generated Classes.

Creating Custom Connection code


In each link connect directive, the Source role directives tab defines from what types you can drag. Similarly, the
Target role directives tab defines to what types you can drag. For each type, you can further specify whether to
allow the connection (for that link connect directive) by setting the Custom Accept flag and then supplying the
extra code.
You can also customize what occurs when the connection is made. For example, you can customize just the case
where the drag occurs to or from a particular class, all the cases that one link connect directive governs, or the
whole FlowBuilder connection builder. For each of these options, you can set custom flags at the appropriate level.
When you transform all templates and try to build the solution, error messages direct you to comments that are in
the generated code. These comments identify what you must supply.
In the Components Diagram sample, the connection builder for the Connection domain relationship is customized
to restrict the connections that can be made between ports. The following illustration shows that you can make
connections only from OutPort elements to InPort elements, but you can nest components inside each other.
Connection Coming in to an OutPort from a Nested Component

Therefore, you might want to specify that a connection can come from a nested component to an OutPort. To
specify such a connection, you set Uses Custom Accept on the InPort type as source role and the OutPort type
as target role in the DSL Details window as shown in the following illustrations:
Link Connect Directive in DSL Explorer

Link Connect Directive in DSL Details Window


You must then provide methods in the ConnectionBuilder class:

public partial class ConnectionBuilder


{
/// <summary>
/// OK if this component has children
/// </summary>
private static bool CanAcceptInPortAsSource(InPort candidate)
{
return candidate.Component.Children.Count > 0;
}

/// <summary>
/// Only if source is on parent of target.
/// </summary>
private static bool CanAcceptInPortAndInPortAsSourceAndTarget (InPort sourceInPort, InPort
targetInPort)
{
return sourceInPort.Component == targetInPort.Component.Parent;
}
// And similar for OutPorts...

For more information about customizing the model by using program code, see Navigating and Updating a Model
in Program Code.
You can use similar code, for example, to prevent users from creating loops with parent-child links. These
restrictions are considered 'hard' constraints because users cannot violate them at any time. You can also create
'soft' validation checks that users can bypass temporarily by creating invalid configurations that they cannot save.
Good Practice in Defining Connection Builders
You should define one connection builder to create different types of relationships only if they are conceptually
related. In the task flow sample, you use the same builder to create flows between tasks and also between tasks
and objects. However, it would be confusing to use the same builder to create relationships between comments
and tasks.
If you define a connection builder for multiple types of relationships, you should ensure that it cannot match more
than one type from the same pair of source and target objects. Otherwise, the results will be unpredictable.
You use custom code to apply 'hard' constraints, but you should consider whether users should be able to
temporarily make invalid connections. If they should, you can modify the constraints so that connections are not
validated until users try to save changes.

See Also
Customizing Element Creation and Movement
Customizing Copy Behavior
How to: Add a Drag-and-Drop Handler
Navigating and Updating a Model in Program Code
Circuit Diagrams sample DSL
Customizing Element Creation and Movement
10/18/2017 14 min to read Edit Online

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 (EMD) 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 by 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 to be 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.
The prototype 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 EMD is to decide how an object or group of objects should be merged into a
particular location in the model. In particular, it decides what relationships should be instantiated to link
the merged group into the model. You can also customize it to set properties and to create additional
objects.

The role of an Element Merge Directive


An EMD is generated automatically when you define an embedding relationship. This default EMD creates
an instance of the relationship when users add new child instances to the parent. You can modify these
default EMDs, for example by adding custom code.
You can also add your own EMDs in the DSL definition, to let users drag or paste different combinations of
merged and receiving classes.
Defining an Element Merge Directive
You can add element merge directives to domain classes, domain relationships, shapes, connectors, and
diagrams. You can add or find them in DSL Explorer under the receiving domain class. The receiving class is the
domain class of the element that is already in the model, and onto which the new or copied element will be
merged.

The Indexing Class is the domain class of elements that can be merged into members of the receiving class.
Instances of subclasses of the Indexing Class will also be merged by this EMD, unless you set Applies to
subclasses to False.
There are two kinds of merge directive:
A Process Merge directive specifies the relationships by which the new element should be linked into the
tree.
A Forward Merge directive redirects the new element to another receiving element, typically a parent.
You can add custom code to merge directives:
Set Uses custom accept to add your own code to determine whether a particular instance of the indexing
element should be merged into the target element. When the user drags from the toolbox, the "invalid"
pointer shows if your code disallows the merge.
For example, you could allow the merge only when the receiving element is in a particular state.
Set Uses custom merge to add provide own code to define the changes that are made to the model when
the merge is performed.
For example, you could set properties in the merged element by using data from its new location in the
model.

NOTE
If you write custom merge code, it affects only merges that are performed by using this EMD. If there are other EMDs that
merge the same type of object, or if there is other custom code that creates these objects without using the EMD, then
they will not be affected by your custom merge code.
If you want to make sure that a new element or new relationship is always processed by your custom code, consider
defining an AddRule on the embedding relationship and a DeleteRule on the element's domain class. For more
information, see Rules Propagate Changes Within the Model.

Example: Defining an EMD without custom code


The following example lets users create an element and a connector at the same time by dragging from the
toolbox onto an existing shape. The example adds an EMD to the DSL Definition. Before this modification, users
can drag tools onto the diagram, but not onto existing shapes.
Users can also paste elements onto other elements.
To let users create an element and a connector at the same time
1. Create a new DSL by using the Minimal Language solution template.
When you run this DSL, it lets you create shapes and connectors between the shapes. You cannot drag a
new ExampleElement shape from the toolbox onto an existing shape.
2. To let users merge elements onto ExampleElement shapes, create a new EMD in the ExampleElement
domain class:
a. In DSL Explorer, expand Domain Classes. Right-click ExampleElement and then click Add New
Element Merge Directive.
b. Make sure that the DSL Details window is open, so that you can see the details of the new EMD.
(Menu: View, Other Windows, DSL Details.)
3. Set the Indexing class in the DSL Details window, to define what class of elements can be merged onto
ExampleElement objects.

For this example, select ExampleElements , so that the user can drag new elements onto existing elements.
Notice that the Indexing class becomes the name of the EMD in DSL Explorer.
4. Under Process merge by creating links, add two paths:
a. One path links the new element to the parent model. The path expression that you need to enter
navigates from the existing element, up through the embedding relationship to the parent model.
Finally, it specifies the role in the new link to which the new element will be assigned. The path is as
follows:
ExampleModelHasElements.ExampleModel/!ExampleModel/.Elements

b. The other path links the new element to the existing element. The path expression specifies the
reference relationship and the role to which the new element will be assigned. This path is as
follows:
ExampleElementReferencesTargets.Sources

You can use the path navigation tool to create each path:
c. Under Process merge by creating links at paths, click <add path>.
d. Click the drop-down arrow to the right of the list item. A tree view appears.
e. Expand the nodes in the tree to form the path that you want to specify.
5. Test the DSL:
a. Press F5 to rebuild and run the solution.
Rebuilding will take longer than usual because the generated code will be updated from text
templates to conform to the new DSL Definition.
b. When the experimental instance of Visual Studio has started, open a model file of your DSL. Create
some example elements.
c. Drag from the Example Element tool onto an existing shape.
A new shape appears, and it is linked to the existing shape with a connector.
d. Copy an existing shape. Select another shape and paste.
A copy of the first shape is created. It has a new name and it is linked to the second shape with a
connector.
Notice the following points from this procedure:
By creating Element Merge Directives, you can allow any class of element to accept any other. The EMD is
created in the receiving domain class, and the accepted domain class is specified in the Index class field.
By defining paths, you can specify what links should be used to connect the new element to the existing
model.
The links that you specify should include one embedding relationship.
The EMD affects both creation from the toolbox and also paste operations.
If you write custom code that creates new elements, you can explicitly invoke the EMD by using the
ElementOperations.Merge method. This makes sure that your code links new elements into the model in the
same way as other operations. For more information, see Customizing Copy Behavior.

Example: Adding Custom Accept code to an EMD


By adding custom code to an EMD, you can define more complex merge behavior. This simple example prevents
the user from adding more than a fixed number of elements to the diagram. The example modifies the default
EMD that accompanies an embedding relationship.
To write Custom Accept code to restrict what the user can add
1. Create a DSL by using the Minimal Language solution template. Open the DSL Definition diagram.
2. In DSL Explorer, expand Domain Classes, ExampleModel , Element Merge Directives. Select the element
merge directive that is named ExampleElement .
This EMD controls how the user can create new ExampleElement objects in the model, for example by
dragging from the toolbox.
3. In the DSL Details window, select Uses custom accept.
4. Rebuild the solution. This will take longer than usual because the generated code will be updated from the
model.
A build error will be reported, similar to: "Company.ElementMergeSample.ExampleElement does not
contain a definition for CanMergeExampleElement..."
You must implement the method CanMergeExampleElement .
5. Create a new code file in the Dsl project. Replace its content with the following code and change the
namespace to the namespace of your project.
using Microsoft.VisualStudio.Modeling;

namespace Company.ElementMergeSample // EDIT.


{
partial class ExampleModel
{
/// <summary>
/// Called whenever an ExampleElement is to be merged into this ExampleModel.
/// This happens when the user pastes an ExampleElement
/// or drags from the toolbox.
/// Determines whether the merge is allowed.
/// </summary>
/// <param name="rootElement">The root element in the merging EGP.</param>
/// <param name="elementGroupPrototype">The EGP that the user wants to merge.</param>
/// <returns>True if the merge is allowed</returns>
private bool CanMergeExampleElement(ProtoElementBase rootElement, ElementGroupPrototype
elementGroupPrototype)
{
// Allow no more than 4 elements to be added:
return this.Elements.Count < 4;
}
}
}

This simple example restricts the number of elements that can be merged into the parent model. For more
interesting conditions, the method can inspect any of the properties and links of the receiving object. It can
also inspect the properties of the merging elements, which are carried in a ElementGroupPrototype. For
more information about ElementGroupPrototypes , see Customizing Copy Behavior. For more information
about how to write code that reads a model, see Navigating and Updating a Model in Program Code.
6. Test the DSL:
a. Press F5 to rebuild the solution. When the experimental instance of Visual Studio opens, open an
instance of your DSL.
b. Create new elements in several ways:
a. Drag from the Example Element tool onto the diagram.
b. In the Example Model Explorer, right-click the root node and then click Add New
Example Element.
c. Copy and paste an element on the diagram.
c. Verify that you cannot use any of these ways to add more than four elements to the model. This is
because they all use the Element Merge Directive.

Example: Adding Custom Merge code to an EMD


In custom merge code, you can define what happens when the user drags a tool or pastes onto an element. There
are two ways to define a custom merge:
1. Set Uses Custom Merge and supply the required code. Your code replaces the generated merge code.
Use this option if you want to completely redefine what the merge does.
2. Override the MergeRelate method, and optionally the MergeDisconnect method. To do this, you must set
the Generates Double Derived property of the domain class. Your code can call the generated merge
code in the base class. Use this option if you want to perform additional operations after the merge has
been performed.
These approaches only affect merges that are performed by using this EMD. If you want to affect all ways
in which the merged element can be created, an alternative is to define an AddRule on the embedding
relationship and a DeleteRule on the merged domain class. For more information, see Rules Propagate
Changes Within the Model.
To override MergeRelate
1. In the DSL definition, make sure that you have defined the EMD to which you want to add code. If you
want, you can add paths and define custom accept code as described in the previous sections.
2. In the DslDefinition diagram, select the receiving class of the merge. Typically it is the class at the source
end of an embedding relationship.
For example, in a DSL generated from the Minimal Language solution, select ExampleModel .
3. In the Properties window, set Generates Double Derived to true.
4. Rebuild the solution.
5. Inspect the content of Dsl\Generated Files\DomainClasses.cs. Search for methods named MergeRelate
and examine their contents. This will help you write your own versions.
6. In a new code file, write a partial class for the receiving class, and override the MergeRelate method.
Remember to call the base method. For example:

partial class ExampleModel


{
/// <summary>
/// Called when the user drags or pastes an ExampleElement onto the diagram.
/// Sets the time of day as the name.
/// </summary>
/// <param name="sourceElement">Element to be added</param>
/// <param name="elementGroup">Elements to be merged</param>
protected override void MergeRelate(ModelElement sourceElement, ElementGroup elementGroup)
{
// Connect the element according to the EMD:
base.MergeRelate(sourceElement, elementGroup);

// Custom actions:
ExampleElement mergingElement = sourceElement as ExampleElement;
if (mergingElement != null)
{
mergingElement.Name = DateTime.Now.ToLongTimeString();
}
}
}

To write Custom Merge code


1. In Dsl\Generated Code\DomainClasses.cs, inspect methods named MergeRelate . These methods create
links between a new element and the existing model.
Also, inspect methods named MergeDisconnect . These methods unlink an element from the model when it
is to be deleted.
2. In DSL Explorer, select or create the Element Merge Directive that you want to customize. In the DSL
Details window, set Uses Custom Merge.
When you set this option, the Process Merge and Forward Merge options are ignored. Your code is used
instead.
3. Rebuild the solution. It will take longer than usual because the generated code files will be updated from
the model.
Error messages will appear. Double-click the error messages to see the instructions in the generated code.
These instructions ask you to supply two methods, MergeRelate YourDomainClass and MergeDisconnect
YourDomainClass
4. Write the methods in a partial class definition in a separate code file. The examples you inspected earlier
should suggest what you need.
Custom merge code will not affect code that creates objects and relationships directly, and it will not affect
other EMDs. To make sure that your additional changes are implemented regardless of how the element is
created, consider writing an AddRule and a DeleteRule instead. For more information, see Rules
Propagate Changes Within the Model.

Redirecting a Merge Operation


A forward merge directive redirects the target of a merge operation. Typically, the new target is the embedding
parent of the initial target.
For example, in a DSL that was created with the component diagram template, Ports are embedded in
Components. Ports are displayed as small shapes on the edge of a component shape. The user creates ports by
dragging the Port tool onto a Component shape. But sometimes, the user mistakenly drags the Port tool onto an
existing port, instead of the component, and the operation fails. This is an easy mistake when there are several
existing ports. To help the user to avoid this nuisance, you can allow ports to be dragged onto an existing port, but
have the action redirected to the parent component. The operation works as if the target element were the
component.
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. However, 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
unintentionally 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
Navigating and Updating a Model in Program Code
Customizing Tools and the Toolbox
Circuit Diagrams sample DSL
Customizing Copy Behavior
10/18/2017 14 min to read Edit Online

In a domain-specific language (DSL) created with the Visual Studio Visualization and Modeling SDK, you can alter
what happens when the user copies and pastes elements.

Standard Copy and Paste Behavior


To enable copying, set the Enable Copy Paste property of the Editor node in DSL Explorer.
By default, when the user copies elements to the clipboard, the following elements are also copied:
Embedded descendants of the selected elements. (That is, elements that are the targets of embedding
relationships that are sourced at copied elements.)
Relationship links between the copied elements.
This rule applies recursively to the copied elements and links.

The copied elements and links are serialized and stored in an ElementGroupPrototype (EGP), which is
placed on the clipboard.
An image of the copied elements is also placed on the clipboard. This allows the user to paste into other
applications such as Word.
The user can paste copied elements onto a target that can accept the elements according to the DSL
Definition. For example, in a DSL generated from the components solution template, the user can paste
ports onto components, but not onto the diagram; and can paste components onto the diagram, but not
onto other components.

Customizing Copy and Paste Behavior


For more information about customizing the model by using program code, see Navigating and Updating a
Model in Program Code.
Enable or disable copy, cut, and paste.
In DSL Explorer, set the Enable Copy Paste property of the Editor node.
Copy links to the same target. For example, to have a copied comment box linked to the same subject element.
Set the Propagates Copy property of the role to Propagate copy to link only. For more information, see
Customizing Link Copy Behavior.
Copy linked elements. For example, when you copy a new element, copies of any linked comment boxes are
made as well.
Set the Propagates Copy property of the role to Propagate copy to link and opposite role player. For more
information, see Customizing Link Copy Behavior.
Rapidly duplicate elements by copying and pasting. Normally, the item you just copied is still selected, and
you cannot paste the same type of element onto it.
Add an Element Merge Directive to the domain class, and set it to forward merges to the parent class. This will
have the same effect on drag operations. For more information, see Customizing Element Creation and
Movement.
- or -
Select the diagram before pasting the elements, by overriding ClipboardCommandSet.ProcessOnPasteCommand() . Add
this code in a custom file in the DslPackage project:

namespace Company.MyDsl {
using System.Linq;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Shell;
partial class MyDslClipboardCommandSet
{
protected override void ProcessOnMenuPasteCommand()
{
// Deselect the current selection after copying:
Diagram diagram = (this.CurrentModelingDocView as SingleDiagramDocView).Diagram;
this.CurrentModelingDocView
.SelectObjects(1, new object[] { diagram }, 0);
}
} }

Create additional links when the user pastes onto a selected target. For example, when a comment box is
pasted onto an element, a link is made between them.
Add an Element Merge Directive to the target domain class, and set it to process the merge by adding links. This
will have the same effect on drag operations. For more information, see Customizing Element Creation and
Movement.
- or -
Override ClipboardCommandSet.ProcessOnPasteCommand() to create the additional links after calling the base
method.
Customize the formats in which elements can be copied to external applications - for example, to add a
border to the bitmap form.
Override MyDsl ClipboardCommandSet.ProcessOnMenuCopyCommand() in the DslPackage project.
Customize how elements are copied to the clipboard by the copy command, but not in a drag
operation.
Override MyDsl ClipboardCommandSet.CopyModelElementsIntoElementGroupPrototype() in the DslPackage project.
Preserve shape layout through copy and paste.
When the user copies multiple shapes, you can preserve their relative positions when they are pasted. This
technique is demonstrated by the example at VMSDK: Circuit Diagrams sample.
To achieve this effect, add the shapes and connectors to the copied ElementGroupPrototype. The most convenient
method to override is ElementOperations.CreateElementGroupPrototype(). To do this, add the following code to
the Dsl project:
public class MyElementOperations : DesignSurfaceElementOperations
{
// Create an EGP to add to the clipboard.
// Called when the elements to be copied have been
// collected into an ElementGroup.
protected override ElementGroupPrototype CreateElementGroupPrototype(ElementGroup elementGroup,
ICollection<ModelElement> elements, ClosureType closureType)
{
// Add the shapes and connectors:
// Get the elements already in the group:
ModelElement[] mels = elementGroup.ModelElements
.Concat(elementGroup.ElementLinks) // Omit if the paste target is not the diagram.
.ToArray();
// Get their shapes:
IEnumerable<PresentationElement> shapes =
mels.SelectMany(mel =>
PresentationViewsSubject.GetPresentation(mel));
elementGroup.AddRange(shapes);

return base.CreateElementGroupPrototype
(elementGroup, elements, closureType);
}

public MyElementOperations(IServiceProvider serviceProvider, ElementOps1Diagram diagram)


: base(serviceProvider, diagram)
{ }
}

// Replace the standard ElementOperations


// singleton with your own:
partial class MyDslDiagram // EDIT NAME
{
/// <summary>
/// Singleton ElementOperations attached to this diagram.
/// </summary>
public override DesignSurfaceElementOperations ElementOperations
{
get
{
if (singleton == null)
{
singleton = new MyElementOperations(this.Store as IServiceProvider, this);
}
return singleton;
}
}
private MyElementOperations singleton = null;
}

Paste shapes in a chosen location, such as the current cursor position.


When the user copies multiple shapes, you can preserve their relative positions when they are pasted. This
technique is demonstrated by the example at VMSDK: Circuit Diagrams sample.
To achieve this effect, override ClipboardCommandSet.ProcessOnMenuPasteCommand() to use the location-specific
version of ElementOperations.Merge() . To do this, add the following code in the DslPackage project:
partial class MyDslClipboardCommandSet // EDIT NAME
{
/// <summary>
/// This method assumes we only want to paste things onto the diagram
/// - not onto anything contained in the diagram.
/// The base method pastes in a free space on the diagram.
/// But if the menu was used to invoke paste, we want to paste in the cursor position.
/// </summary>
protected override void ProcessOnMenuPasteCommand()
{

NestedShapesSampleDocView docView = this.CurrentModelingDocView as NestedShapesSampleDocView;

// Retrieve data from clipboard:


System.Windows.Forms.IDataObject data = System.Windows.Forms.Clipboard.GetDataObject();

Diagram diagram = docView.CurrentDiagram;


if (diagram == null) return;

if (!docView.IsContextMenuShowing)
{
// User hit CTRL+V - just use base method.

// Deselect anything that's selected, otherwise


// pasted item will be incompatible:
if (!this.IsDiagramSelected())
{
docView.SelectObjects(1, new object[] { diagram }, 0);
}

// Paste into a convenient spare space on diagram:


base.ProcessOnMenuPasteCommand();
}
else
{
// User right-clicked - paste at mouse position.

// Utility class:
DesignSurfaceElementOperations op = diagram.ElementOperations;

ShapeElement pasteTarget = diagram;

// Check whether what's in the paste buffer is acceptable on the target.


if (pasteTarget != null && op.CanMerge(pasteTarget, data))
{

// Although op.Merge would be a no-op if CanMerge failed, we check CanMerge first


// so that we don't create an empty transaction (after which Undo would be no-op).
using (Transaction t = diagram.Store.TransactionManager.BeginTransaction("paste"))
{
PointD place = docView.ContextMenuMousePosition;
op.Merge(pasteTarget, data, PointD.ToPointF(place));
t.Commit();
}
}
}
}
}

Let the user drag and drop elements.


See How to: Add a Drag-and-Drop Handler.

Customizing Link Copy Behavior


When the user copies an element, the standard behavior is that any embedded elements are also copied. You can
modify the standard copying behavior. In the DSL Definition, select a role at one side of a relationship and in the
Properties window set the Propagates Copy value.

There are three values:


Do not propagate copy
Propagate copy to link only - when the group is pasted, the new copy of this link will refer to the existing
element at the other end of the link.
Propagate copy to link and opposite role player - the copied group includes a copy of the element at the
other end of the link.

The changes that you make will affect both the elements and the image that is copied.

Programming Copy and Paste Behavior


Many aspects of a DSL's behavior with regard to copy, paste, creation, and deletion of objects are governed by an
instance of ElementOperations that is coupled to the diagram. You can modify your DSL's behavior by deriving
your own class from ElementOperations and overriding the ElementOperations property of your diagram class.

TIP
For more information about customizing the model by using program code, see Navigating and Updating a Model in
Program Code.
To define your own ElementOperations
1. In a new file in your DSL project, create a class that is derived from DesignSurfaceElementOperations.
2. Add a partial class definition for your diagram class. The name of this class can be found in
Dsl\GeneratedCode\Diagrams.cs.
In the diagram class, override ElementOperations to return an instance of your ElementOperations
subclass. You should return the same instance at every call.
Add this code in a custom code file in the DslPackage project:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;

public partial class MyDslDiagram


{
public override DesignSurfaceElementOperations ElementOperations
{
get
{
if (this.elementOperations == null)
{
this.elementOperations = new MyElementOperations(this.Store as IServiceProvider, this);
}
return this.elementOperations;
}
}
private MyElementOperations elementOperations = null;
}

public class MyElementOperations : DesignSurfaceElementOperations


{
public MyElementOperations(IServiceProvider serviceProvider, MyDslDiagram diagram)
: base(serviceProvider, diagram)
{ }
// Overridden methods follow
}

Receiving items dragged from other models


ElementOperations can also be used to define copy, move, deletion and drag-and-drop behavior. As a
demonstration of the use of ElementOperations, the example given here defines custom drag-and-drop behavior.
However, for that purpose you might consider the alternative approach described in How to: Add a Drag-and-
Drop Handler, which is more extensible.
Define two methods in your ElementOperations class:
CanMerge(ModelElement targetElement, System.Windows.Forms.IDataObject data)which determines whether
the source element can be dragged onto the target shape, connector or diagram.
MergeElementGroupPrototype(ModelElement targetElement, ElementGroupPrototype sourcePrototype) which
combines the source element into the target.
CanMerge ()
CanMerge() is called to determine feedback that should be given to the user as the mouse moves across the
diagram. The parameters to the method are the element over which the mouse is hovering, and data about the
source from which the drag operation has been performed. The user can drag from anywhere on the screen.
Therefore, the source object can be of many different types and can be serialized in different formats. If the
source is a DSL or UML model, the data parameter is the serialization of an ElementGroupPrototype. Drag, copy
and toolbox operations use ElementGroupPrototypes to represent fragments of models.
An Element Group Prototype can contain any number of elements and links. Element types can be identified by
their GUIDs. The GUID is of the shape that was dragged, not the underlying model element. In following example,
CanMerge() returns true if a class shape from a UML diagram is dragged onto this diagram.
public override bool CanMerge(ModelElement targetShape, System.Windows.Forms.IDataObject data)
{
// Extract the element prototype from the data.
ElementGroupPrototype prototype = ElementOperations.GetElementGroupPrototype(this.ServiceProvider, data);
if (targetShape is MyTargetShape && prototype != null &&
prototype.RootProtoElements.Any(rootElement =>
rootElement.DomainClassId.ToString()
== "3866d10c-cc4e-438b-b46f-bb24380e1678")) // Guid of UML Class shapes
// or SourceClass.DomainClassId
return true;
return base.CanMerge(targetShape, data);
}

MergeElementGroupPrototype()
This method is called when the user drops an element onto a diagram, a shape, or a connector. It should merge
the dragged content into the target element. In this example, the code determines whether it recognizes the
combination of target and prototype types; if so, the method converts the dragged elements into a prototype of
the elements that should be added to the model. The base method is called to perform the merge, either of the
converted or unconverted elements.

public override void MergeElementGroupPrototype(ModelElement targetShape, ElementGroupPrototype


sourcePrototype)
{
ElementGroupPrototype prototypeToMerge = sourcePrototype;
MyTargetShape pel = targetShape as MyTargetShape;
if (pel != null)
{
prototypeToMerge = ConvertDraggedTypeToLocal(pel, sourcePrototype);
}
if (prototypeToMerge != null)
base.MergeElementGroupPrototype(targetShape, prototypeToMerge);
}

This example deals with UML class elements dragged from a UML class diagram. The DSL is not designed to
store UML classes directly, but instead, we create a DSL element for each dragged UML class. This would be
useful, for example, if the DSL is an instance diagram. The user could drag classes onto the diagram to create
instances of those classes.
private ElementGroupPrototype ConvertDraggedTypeToLocal (MyTargetShape snapshot, ElementGroupPrototype
prototype)
{
// Find the UML project:
EnvDTE.DTE dte = snapshot.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
foreach (EnvDTE.Project project in dte.Solution.Projects)
{
IModelingProject modelingProject = project as IModelingProject;
if (modelingProject == null) continue; // not a modeling project
IModelStore store = modelingProject.Store;
if (store == null) continue;
// Look for the shape that was dragged:
foreach (IDiagram umlDiagram in store.Diagrams())
{
// Get modeling diagram that implements UML diagram:
Diagram diagram = umlDiagram.GetObject<Diagram>();
Guid elementId = prototype.SourceRootElementIds.FirstOrDefault();
ShapeElement shape = diagram.Partition.ElementDirectory.FindElement(elementId) as ShapeElement;
if (shape == null) continue;
IClass classElement = shape.ModelElement as IClass;
if (classElement == null) continue;

// Create a prototype of elements in my DSL, based on the UML element:


Instance instance = new Instance(snapshot.Store);
instance.Type = classElement.Name;
// Pack them into a prototype:
ElementGroup group = new ElementGroup(instance);
return group.CreatePrototype();
}
}
return null;
}

Standard Copy Behavior


The code in this section shows methods that can you can override to alter copying behavior. To help you see how
to achieve your own customizations, this section shows code that overrides the methods involved in copying, but
does not change the standard behavior.
When the user presses CTRL+C or uses the Copy menu command, the method ProcessOnMenuCopyCommand
is called. You can see how this is set up in DslPackage\Generated Code\CommandSet.cs. For more
information about how commands are set up, see How to: Add a Command to the Shortcut Menu.
You can override ProcessOnMenuCopyCommand by adding a partial class definition of MyDsl
ClipboardCommandSet in the DslPackage project.
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

partial class MyDslClipboardCommandSet


{
/// <summary>
/// Override ProcessOnMenuCopyCommand() to copy elements to the
/// clipboard in different formats, or to perform additional tasks
/// before or after copying - for example deselect the copied elements.
/// </summary>
protected override void ProcessOnMenuCopyCommand()
{
IList<ModelElement> selectedModelElements = this.SelectedElements;
if (selectedModelElements.Count == 0) return;

// System container for clipboard data.


// The IDataObject can contain data in several formats.
IDataObject dataObject = new DataObject();

Bitmap bitmap = null; // For export to other programs.


try
{
#region Create EGP for copying to a DSL.
this.CopyModelElementsIntoElementGroupPrototype
(dataObject, selectedModelElements);
#endregion

#region Create bitmap for copying to another application.


// Find all the shapes associated with this selection:
List<ShapeElement> shapes = new List<ShapeElement>(
this.ResolveExportedShapesForClipboardImages
(dataObject, selectedModelElements));

bitmap = this.CreateBitmapForClipboard(shapes);
if (bitmap != null)
{
dataObject.SetData(DataFormats.Bitmap, bitmap);
}
#endregion

// Add the data to the clipboard:


Clipboard.SetDataObject(dataObject, true, 5, 100);
}
finally
{
// Dispose bitmap after SetDataObject:
if (bitmap != null) bitmap.Dispose();
}
}
/// <summary>
/// Override this to customize the element group that is copied to the clipboard.
/// </summary>
protected override void CopyModelElementsIntoElementGroupPrototype(IDataObject dataObject,
IList<ModelElement> selectedModelElements)
{
return this.ElementOperations.Copy(dataObject, selectedModelElements);
}
}

Each diagram has a singleton instance of ElementOperations. You can supply your own derivative. This file, which
can be placed in the DSL project, would behave the same as the code that it overrides:

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace Company.MyDsl
{
partial class MyDslDiagram
{
/// <summary>
/// Singleton ElementOperations attached to this diagram.
/// </summary>
public override DesignSurfaceElementOperations ElementOperations
{
get
{
if (this.elementOperations == null)
{
this.elementOperations = new MyElementOperations(this.Store as IServiceProvider, this);
}
return this.elementOperations;
}
}
private MyElementOperations elementOperations = null;
}

// Our own version of ElementOperations so that we can override:


public class MyElementOperations : DesignSurfaceElementOperations
{
public MyElementOperations(IServiceProvider serviceProvider, ElementOps1Diagram diagram)
: base(serviceProvider, diagram)
{ }

/// <summary>
/// Copy elements to the clipboard data.
/// Provides a hook for adding custom data.
/// </summary>
public override void Copy(System.Windows.Forms.IDataObject data,
ICollection<ModelElement> elements,
ClosureType closureType,
System.Drawing.PointF sourcePosition)
{
if (CanAddElementGroupFormat(elements, closureType))
{
AddElementGroupFormat(data, elements, closureType);
}

// Override these to store additional data:


if (CanAddCustomFormat(elements, closureType))
{
AddCustomFormat(data, elements, closureType, sourcePosition);
}
}

protected override void AddElementGroupFormat(System.Windows.Forms.IDataObject data,


ICollection<ModelElement> elements, ClosureType closureType)
{
// Add the selected elements and those implied by the propagate copy rules:
ElementGroup elementGroup = this.CreateElementGroup(elements, closureType);

// Mark all the elements that are not embedded under other elements:
this.MarkRootElements(elementGroup, elements, closureType);

// Store in the clipboard data:


ElementGroupPrototype elementGroupPrototype = this.CreateElementGroupPrototype(elementGroup, elements,
closureType);
data.SetData(ElementGroupPrototype.DefaultDataFormatName, elementGroupPrototype);
}
/// <summary>
/// Override this to store additional elements in the element group:
/// </summary>
protected override ElementGroupPrototype CreateElementGroupPrototype(ElementGroup elementGroup,
ICollection<ModelElement> elements, ClosureType closureType)
{
ElementGroupPrototype prototype = new ElementGroupPrototype(this.Partition, elementGroup.RootElements,
elementGroup);
return prototype;
}

/// <summary>
/// Create an element group from the given starting elements, using the
/// copy propagation rules specified in the DSL Definition.
/// By default, this includes all the embedded descendants of the starting elements,
/// and also includes reference links where both ends are already included.
/// </summary>
/// <param name="startElements">model elements to copy</param>
/// <param name="closureType"></param>
/// <returns></returns>
protected override ElementGroup CreateElementGroup(ICollection<ModelElement> startElements, ClosureType
closureType)
{
// ElementClosureWalker finds all the connected elements,
// according to the propagate copy rules specified in the DSL Definition:
ElementClosureWalker walker = new ElementClosureWalker(this.Partition,
closureType, // Normally ClosureType.CopyClosure
startElements,
true, // Do not load other models.
null, // Optional list of domain roles not to traverse.
true); // Include relationship links where both ends are already included.

walker.Traverse(startElements);
IList<ModelElement> closureList = walker.ClosureList;
Dictionary<object, object> closureContext = walker.Context;

// create a group for this closure


ElementGroup group = new ElementGroup(this.Partition);
group.AddRange(closureList, false);

// create the element group prototype for the group


foreach (object key in closureContext.Keys)
{
group.SourceContext.ContextInfo[key] = closureContext[key];
}

return group;
}
}
}

See Also
Customizing Element Creation and Movement
How to: Add a Drag-and-Drop Handler
Customizing Deletion Behavior
Sample: VMSDK Circuit Diagrams sample

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
Customizing Deletion Behavior
10/18/2017 10 min to read Edit Online

Deleting an element usually causes related elements to be deleted also. All relationships connected to it, and any
child elements are deleted. This behavior is named delete propagation. You can customize delete propagation, for
example to arrange that additional related elements are deleted. By writing program code, you can make delete
propagation depend on the state of the model. You can also cause other changes to occur in response to a
deletion.
This topic includes the following sections:
Default Deletion Behavior
Setting the Propagate Delete option of a role
Overriding the Delete Closure - Use this technique where deletion might lead to deletion of neighboring
elements.
Using OnDeleting and OnDeleted - Use these methods where the response could include other actions such
as updating a value either inside or outside the store.
Deletion Rules - Use rules to propagate updates of any kind within the store, where one change might lead
to others.
Deletion Events - Use store events to propagate updates outside the store, for example to other Visual
Studio documents.
UnMerge - use the UnMerge operation to undo the merge operation that attached a child element to its
parent.

Default Deletion Behavior


By default, the following rules govern delete propagation:
If an element is deleted, all embedded elements are also deleted. The embedded elements are those that are
the targets of embedding relationships for which this element is the source. For example, if there is an
embedding relationship from Album to Song, then when a particular Album is deleted, all its Songs will
also be deleted.
By contrast, deleting a song does not delete the album.
By default, deletion does not propagate along reference relationships. If there is a reference relationship
ArtistPlaysOnAlbum from Album to Artist, deleting an album does not delete any related artist, and
deleting an artist does not delete any album.
However, deletion does propagate along some built-in relationships. For example, when a model element is
deleted, its shape on the diagram is also deleted. The element and shape are related by the
PresentationViewsSubject reference relationship.

Every relationship that is connected to the element, either at the source or target role, is deleted. The role
property of the element at the opposite role no longer contains the deleted element.

Setting the Propagate Delete option of a role


You can cause deletion to propagate along a reference relationship, or from an embedded child to its parent.
To set delete propagation
1. On the DSL Definition diagram, select the role to which you want propagation to delete. The role is
represented by the line on the left or right of a domain relationship box.
For example, if you want to specify that whenever an Album is deleted, the related Artists are also deleted,
then select the role connected to the domain class Artist.
2. In the Properties window, set the Propagates Delete property.
3. Press F5 and verify that:
When an instance of this relationship is deleted, the element at the selected role will also be deleted.
When an element at the opposite role is deleted, instances of this relationship will be deleted, and the
related elements at this role will be deleted.
You can also see the Propagates Delete option in the DSL Details window. Select a domain class and, in
the DSL Details window, open the Delete Behavior page by clicking the button at the side of the window.
The Propagate option is shown for the opposite role of each relationship. The Delete Style column
indicates whether the Propagate option is at its default setting, but it does not have any separate effect.

Delete Propagation by using program code


The options in the DSL Definition file only let you choose whether deletion propagates to an immediate neighbor.
To implement a more complex scheme of delete propagation, you can write program code.

NOTE
To add program code to your DSL definition, create a separate code file in the Dsl project and write partial definitions to
augment the classes in the Generated Code folder. For more information, see Writing Code to Customise a Domain-Specific
Language.

Defining a Delete Closure


The deletion operation uses the class YourModelDeleteClosure to determine which elements to delete, given an
initial selection. It calls ShouldVisitRelationship() and ShouldVisitRolePlayer() repeatedly, walking the graph of
relationships. You can override these methods. ShouldVisitRolePlayer is provided with the identity of a link and the
element at one of the link's roles. It should return one of the following values:
VisitorFilterResult.Yes- The element should be deleted and the walker should proceed to try the element's
other links.
VisitorFilterResult.DoNotCare - The element should not be deleted unless another query replies that it
should be deleted.
VisitorFilterResult.Never - The element must not be deleted, even if another query answers Yes, and the
walker should not try the element's other links.
// When a musician is deleted, delete their albums with a low rating.
// Override methods in <YourDsl>DeleteClosure in DomainModel.cs
partial class MusicLibDeleteClosure
{
public override VisitorFilterResult ShouldVisitRolePlayer
(ElementWalker walker, ModelElement sourceElement, ElementLink elementLink,
DomainRoleInfo targetDomainRole, ModelElement targetRolePlayer)
{
ArtistAppearsInAlbum link = elementLink as ArtistAppearsInAlbum;
if (link != null
&& targetDomainRole.RolePlayer.Id == Album.DomainClassId)
{
// Count other unvisited links to the Album of this link.
if (ArtistAppearsInAlbum.GetLinksToArtists(link.Album)
.Where(linkAlbumArtist =>
linkAlbumArtist != link &&
!walker.Visited(linkAlbumArtist))
.Count() == 0)
{
// Should delete this role player:
return VisitorFilterResult.Yes;
}
else
// Don't delete unless another relationship deletes it:
return VisitorFilterResult.DoNotCare;
}
else
{
// Test for and respond to other relationships and roles here.

// Not the relationship or role we're interested in.


return base.ShouldVisitRolePlayer(walker, sourceElement,
elementLink, targetDomainRole, targetRolePlayer);
}
}
}

The closure technique ensures that the set of elements and links to be deleted is determined before deletion
begins. The walker also combines the results of your closure with those from other parts of the model.
However, the technique assumes that deletion affects only its neighbors in the graph of relationships: you cannot
use this method to delete an element in another part of the model. You cannot use it if you want to add elements
or make other changes in response to a deletion.

Using OnDeleting and OnDeleted


You can override OnDeleting() or OnDeleted() either in a domain class, or in a domain relationship.
1. OnDeleting is called when an element is about to be deleted, but before its relationships have been
disconnected. It is still navigable to and from other elements, and is still in store.ElementDirectory .
If several elements are deleted at the same time, OnDeleting is called for all of them before performing the
deletions.
IsDeleting is true.
2. OnDeleted is called when the element has been deleted. It remains in the CLR heap so that an Undo can be
performed if required, but it is unlinked from other elements and removed from store.ElementDirectory .
For relationships, the roles still reference the old role players. IsDeleted is true.
3. OnDeleting and OnDeleted are called when the user invokes Undo after a creating an element, and when an
earlier deletion is repeated in Redo. Use this.Store.InUndoRedoOrRollback to avoid updating store elements
in these cases. For more information, see How to: Use Transactions to Update the Model.
For example, the following code deletes an Album when its last child Song is deleted:

// Delete the parent Album when the last Song is deleted.


// Override methods in the embedding relationship between Album and Song:
partial class AlbumHasSongs
{
protected override void OnDeleted()
{
base.OnDeleted();
// Don't perform in-store actions in undo:
if (this.Store.InUndoRedoOrRollback) return;
// Relationship source and target still work:
// Don't bother if source is already on its way out:
if (!this.Album.IsDeleting && !this.Album.IsDeleted)
{
if (this.Album.Songs.Count == 0)
{
this.Album.Delete();
} } } }

It is often more useful to trigger from the deletion of the relationship than the role element, because this works
both when the element is deleted, and when the relationship itself is deleted. However, for a reference relationship,
you might want to propagate deletion when a related element is deleted, but not when the relationship itself is
deleted. This example deletes an Album when its last contributing Artist is deleted, but it does not respond if the
relationships are deleted:

using System.Linq; ...


// Assumes a many-many reference relationship
// between Artist and Album.
partial class Artist
{
protected override void OnDeleting()
{
base.OnDeleting();
if (this.Store.InUndoRedoOrRollback) return;
List<Album> toDelete = new List<Album>();
foreach (Album album in this.Albums)
{
if (album.Artists.Where(artist => !artist.IsDeleting)
.Count() == 0)
{
toDelete.Add(album);
}
}
foreach (Album album in toDelete)
{
album.Delete();
} } }

When you perform Delete on an element, OnDeleting and OnDeleted will be called. These methods are always
performed inline - that is, immediately before and after the actual deletion. If your code deletes two or more
elements, OnDeleting and OnDeleted will be called in alternation on all of them in turn.

Deletion Rules and Events


As an alternative to OnDelete handlers, you can define deletion rules and deletion events.
1. Deleting and Delete rules are triggered only in a transaction, and not in an Undo or Redo. You can set
them to be queued to execute at the end of the transaction in which the deletion is performed. Deleting
rules are always executed before any Deleted rules that are in the queue.
Use rules to propagate changes that affect only elements in the store, including relationships, diagram
elements and their properties. Typically, a Deleting rule is used to propagate deletion, and a Delete rule is
used to create replacement elements and relationships.
For more information, see Rules Propagate Changes Within the Model.
2. Deleted store event is invoked at the end of a transaction, and is called after an undo or redo. It can
therefore be used to propagate deletions to objects outside the store such as files, database entries or other
objects in Visual Studio.
For more information, see Event Handlers Propagate Changes Outside the Model.

WARNING
When an element has been deleted, you can access its domain property values, but you cannot navigate relationship
links. However, if you set a deleted event on a relationship, you can also access the two elements that were its role
players. Therefore, if you want to respond to the deletion of a model element but want to access an element to which
it was linked, set a delete event on the relationship instead of the model element's domain class.

Example Deletion Rules

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


internal class AlbumDeletingRule : DeletingRule
{
public override void ElementDeleting(ElementDeletingEventArgs e)
{
base.ElementDeleting(e);
// ...perform tasks to propagate imminent deletion
}
}
[RuleOn(typeof(Album), FireTime = TimeToFire.TopLevelCommit)]
internal class AlbumDeletedRule : DeleteRule
{
public override void ElementDeleted(ElementDeletedEventArgs e)
{
base.ElementDeleted(e);
// ...perform tasks such as creating new elements
}
}

// The rule must be registered:


public partial class MusicLibDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(AlbumDeletingRule));
types.Add(typeof(AlbumDeletedRule));
// If you add more rules, list them here.
return types.ToArray();
}
}

Example Deleted Event


partial class NestedShapesSampleDocData
{
protected override void OnDocumentLoaded(EventArgs e)
{
base.OnDocumentLoaded(e);
DomainRelationshipInfo commentRelationship =
this.Store.DomainDataDirectory
.FindDomainRelationship(typeof(CommentsReferenceComponents));

this.Store.EventManagerDirectory.ElementDeleted.Add(commentRelationship,
new EventHandler<ElementDeletedEventArgs>(CommentLinkDeleted));
}

private void CommentLinkDeleted (object sender, ElementDeletedEventArgs e)


{
CommentsReferenceComponents link = e.ModelElement as CommentsReferenceComponents;
Comment comment = link.Comment;
Component component = link.Subject;
if (comment.IsDeleted)
{
// The link was deleted because the comment was deleted.
System.Windows.Forms.MessageBox.Show("Removed comment on " + component.Name);
}
else
{
// It was just the link that was deleted - the comment itself remains.
System.Windows.Forms.MessageBox.Show("Removed comment link to "
+ component.Name);
}
}
}

UnMerge
The operation that attaches a child element to its parent is called merge. It occurs when a new element or group of
elements is created from the toolbox, or moved from another part of the model, or copied from the clipboard. As
well as creating an embedding relationship between the parent and its new child, the merge operation can also set
up additional relationships, create auxiliary elements, and set property values in the elements. The merge operation
is encapsulated in an Element Merge Directive (EMD).
An EMD also encapsulates the complementary unmerge or MergeDisconnect operation. If you have a cluster of
elements that has been constructed by using a merge, it is recommended to use the associated unmerge to
remove an element from it, if you want to leave the remaining elements in a consistent state. The unmerge
operation will typically use the techniques described in the previous sections.
For more information, see Customizing Element Creation and Movement.

See Also
Customizing Copy Behavior
Customizing Element Creation and Movement
Writing Code to Customise a Domain-Specific Language
Customizing the Properties Window
10/18/2017 11 min to read Edit Online

You can customize the appearance and behavior of the properties window in your domain-specific language (DSL)
in Visual Studio. In your DSL Definition, you define domain properties on each domain class. By default, when you
select an instance of the class, either on a diagram or in Model Explorer, every domain property is listed in the
properties window. This lets you see and edit the values of domain properties, even if you have not mapped them
to shape fields on the diagram.

Names, Descriptions, and Categories


Name and Display Name. In your definition of a domain property, the Display Name of the property is the name
that appears at runtime in the properties window. By contrast, the Name is used when you write program code to
update the property. The Name must be a correct CLR alphanumeric name, but the Display Name can contain
spaces.
When you set the Name of a property in the DSL Definition, its Display Name is automatically set to a copy of the
Name. If you write a Pascal cased name such as "FuelGauge", the Display Name will automatically contain a space:
"Fuel Gauge". However, you can set the Display Name explicitly to another value.
Description. The Description of a domain property appears in two places:
In the bottom of the properties window when the user selects the property. You can use it to explain to the
user what the property represents.
In the generated program code. If you use the documentation facilities to extract API documentation, it will
appear as the description of this property in the API.
Category. A category is a heading in the Properties window.

Exposing Style Features


Some of the dynamic features of graphical elements can be represented or exposed as domain properties. A feature
that has been exposed in this manner can be updated by the user and can more easily be updated by program
code.
Right-click a shape class in DSL Definition, point to Add Exposed, and then choose a feature.
On shapes you can expose the FillColor, OutlineColor, TextColor, OutlineDashStyle, OutlineThickness and
FillGradientMode properties. On connectors you can expose the Color , TextColor, DashStyle, and Thickness
properties. On diagrams you can expose the FillColor and TextColor properties.

Forwarding: Displaying Properties of Related Elements


When the user of your DSL selects an element in a model, that element's properties are displayed in the properties
window. However, you can also display the properties of specified related elements. This is useful if you have
defined a group of elements that works together. For example, you might define a main element and an optional
plug-in element. If the main element is mapped to a shape and the other is not, it is useful to see all their properties
as if they were on one element.
This effect is named property forwarding, and it happens automatically in several cases. In other cases, you can
achieve property forwarding by defining a domain type descriptor.
Default Property Forwarding Cases
When the user selects a shape or connector, or an element in the Explorer, the following properties are displayed in
the Properties window:
The domain properties that are defined on the domain class of the model element, including those that are
defined in base classes. An exception is domain properties for which you have set Is Browsable to False .
The names of elements that are linked through relationships that have a multiplicity of 0..1. This provides a
convenient method of seeing optionally linked elements, even if you have not defined a connector mapping
for the relationship.
Domain properties of the embedding relationship that targets the element. Because embedding
relationships are usually not displayed explicitly, this lets the user see their properties.
Domain properties that are defined on the selected shape or connector.
Adding Property Forwarding
To forward a property, you define a domain type descriptor. If you have a domain relationship between two
domain classes, you can use a domain type descriptor to set a domain property in the first class to the value of a
domain property in the second domain class. For example, if you have a relationship between a Book domain class
and an Author domain class, you can use a domain type descriptor to make the Name property of a Book's
Author appear in the Properties window when the user selects the Book.

NOTE
Property forwarding affects only the Properties window when the user is editing a model. It does not define a domain
property on the receiving class. If you want to access the forwarded domain property in other parts of the DSL Definition or
in program code, you must access the forwarding element.

The following procedure assumes that you have created a DSL. The first few steps summarize the prerequisites.
To fo r w a r d a p r o p e r t y fr o m a n o t h e r e l e m e n t

1. Create a Domain-Specific Language Tools solution that contains at least two classes, which in this example
are called Book and Author. There should be a relationship of either kind between Book and Author.
The multiplicity of the source role (the role at the Book side) should be 0..1 or 1..1, so that each Book has
one Author.
2. In DSL Explorer, right-click the Book domain class, and then click Add New DomainTypeDescriptor.
A node named Paths of Custom Property Descriptors appears under the Custom Type Descriptor node.
3. Right-click the Custom Type Descriptor node, and then click Add New PropertyPath.
A new property path appears under the Paths Of Custom Property Descriptors node.
4. Select the new property path, and in the Properties window, set Path to Property to the path of the
appropriate model element.
You can edit the path in a tree view by clicking the down arrow to the right of this property. For more
information about domain paths, see Domain Path Syntax. When you have edited it, the path should
resemble BookReferencesAuthor.Author/!Author.
5. Set Property to the Name domain property of Author.
6. Set Display Name to Author Name.
7. Transform All Templates, build and run the DSL.
8. In a model diagram, create a book, an author, and link them using the reference relationship. Select the book
element, and in the Properties window you should see Author Name in addition to the properties of the
book. Change the name of the linked author, or link the book to a different author, and observe that the
Author Name of the book changes.

Custom Property Editors


The property window provides an appropriate default editing experience for the type of each domain property. For
example, for an enumerated type, the user sees a drop-down list, and for a numeric property, the user can enter
digits. This is only true for the built-in types. If you specify an external type, the user will be able to see the
property's values, but not edit it.
However, you can specify the following editors and types:
1. Another editor that is used with a standard type. For example, you could specify a file path editor for a string
property.
2. An external type for the domain property, and an editor for it.
3. A .NET editor such as the file path editor, or you can create your own custom property editor.
A conversion between an external type and an type such as String, which has a default editor.
In a DSL, an external type is any type that is not one of the simple types (such as Boolean or Int32) or String.
To define a domain property that has an external type
1. In Solution Explorer, add a reference to the assembly (DLL) that contains the external type, in the Dsl
project.
The assembly can be a .NET assembly, or an assembly supplied by you.
2. Add the type to the Domain Types list, unless you have already done so.
a. Open DslDefinition.dsl, and in DSL Explorer, right-click the root node, and then click Add New
External Type.
A new entry appears under the Domain Types node.

WARNING
The menu item is on the DSL root node, not the Domain Types node.

b. Set the name and the namespace of the new type in the Properties window.
3. Add a domain property to a domain class in the usual manner.
In the Properties window, select the external type from the drop-down list in the Type field.
At this stage, users can view the values of the property, but they cannot edit it. The displayed values are
obtained from the ToString() function. You could write program code that sets the value of the property,
for example in a command or rule.
Setting a Property Editor
Add a CLR attribute to the domain property, in the following form:

[System.ComponentModel.Editor (
typeof(AnEditor),
typeof(System.Drawing.Design.UITypeEditor))]
You can set the attribute on a property by using the Custom Attribute entry in the Properties window.
The type of AnEditor must be derived from the type specified in the second parameter. The second parameter
should be either UITypeEditor or ComponentEditor. For more information, see EditorAttribute.
You can specify either your own editor, or an editor supplied in the .NET Framework, such as FileNameEditor or
ImageEditor. For example, use the following procedure to have a property in which the user can enter a file name.
To d e fi n e a fi l e n a m e d o m a i n p r o p e r t y

1. Add a domain property to a domain class in your DSL Definition.


2. Select the new property. In the Custom Attribute field in the Properties window, enter the following
attribute. To enter this attribute, click the ellipsis [...] and then enter the attribute name and the parameters
separately:

[System.ComponentModel.Editor (
typeof(System.Windows.Forms.Design.FileNameEditor)
, typeof(System.Drawing.Design.UITypeEditor))]

3. Leave the Type of the domain property at its default setting of String.
4. To test the editor, verify that users can open the file name editor to edit your domain property.
a. Press CTRL+F5 or F5. In the debugging solution, open a test file. Create an element of the domain
class and select it.
b. In the Properties window, select the domain property. The value field shows an ellipsis [...].
c. Click the ellipsis. A file dialog box appears. Select a file and close the dialog box. The file path is now
the value of the domain property.
Defining your own property editor
You can define your own editor. You would do this to allow the user either to edit a type that you have defined, or
to edit a standard type in a special way. For example, you could allow the user to input a string that represents a
formula.
You define an editor by writing a class that is derived from UITypeEditor. Your class must override:
EditValue, to interact with the user and update the property value.
GetEditStyle, to specify whether your editor will open a dialog or provide a drop-down menu.
You can also provide a graphical representation of the property's value that will be displayed in the property
grid. To do this, override GetPaintValueSupported , and PaintValue . For more information, see UITypeEditor.

NOTE
Add the code in a separate code file in the Dsl project.

For example:
internal class TextFileNameEditor : System.Windows.Forms.Design.FileNameEditor
{
protected override void InitializeDialog(System.Windows.Forms.OpenFileDialog openFileDialog)
{
base.InitializeDialog(openFileDialog);
openFileDialog.Filter = "Text files(*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.Title = "Select a text file";
}
}

To use this editor, set the Custom Attribute of a domain property to:

[System.ComponentModel.Editor (
typeof(MyNamespace.TextFileNameEditor)
, typeof(System.Drawing.Design.UITypeEditor))]

For more information, see UITypeEditor.

Providing a drop-down list of values


You can provide a list of values for a user to choose from.

NOTE
This technique provides a list of values that can change at runtime. If you want to provide a list that does not change,
consider instead using an enumerated type as the type of your domain property.

To define a list of standard values, you add to your domain property a CLR attribute that has the following form:

[System.ComponentModel.TypeConverter
(typeof(MyTypeConverter))]

Define a class that derives from TypeConverter. Add the code in a separate file in the Dsl project. For example:

/// <summary>
/// Type converter that provides a list of values
/// to be displayed in the property grid.
/// </summary>
/// <remarks>This type converter returns a list
/// of the names of all "ExampleElements" in the
/// current store.</remarks>
public class MyTypeConverter : System.ComponentModel.TypeConverter
{
/// <summary>
/// Return true to indicate that we return a list of values to choose from
/// </summary>
/// <param name="context"></param>
public override bool GetStandardValuesSupported
(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}

/// <summary>
/// Returns true to indicate that the user has
/// to select a value from the list
/// </summary>
/// <param name="context"></param>
/// <returns>If we returned false, the user would
/// be able to either select a value from
/// the list or type in a value that is not in the list.</returns>
public override bool GetStandardValuesExclusive
(System.ComponentModel.ITypeDescriptorContext context)
{
return true;
}

/// <summary>
/// Return a list of the values to display in the grid
/// </summary>
/// <param name="context"></param>
/// <returns>A list of values the user can choose from</returns>
public override StandardValuesCollection GetStandardValues
(System.ComponentModel.ITypeDescriptorContext context)
{
// Try to get a store from the current context
// "context.Instance" returns the element(s) that
// are currently selected i.e. whose values are being
// shown in the property grid.
// Note that the user could have selected multiple objects,
// in which case context.Instance will be an array.
Store store = GetStore(context.Instance);

List<string> values = new List<string>();

if (store != null)
{
values.AddRange(store.ElementDirectory
.FindElements<ExampleElement>()
.Select<ExampleElement, string>(e =>
{
return e.Name;
}));
}
return new StandardValuesCollection(values);
}

/// <summary>
/// Attempts to get to a store from the currently selected object(s)
/// in the property grid.
/// </summary>
private Store GetStore(object gridSelection)
{
// We assume that "instance" will either be a single model element, or
// an array of model elements (if multiple items are selected).

ModelElement currentElement = null;

object[] objects = gridSelection as object[];


if (objects != null && objects.Length > 0)
{
currentElement = objects[0] as ModelElement;
}
else
{
currentElement = gridSelection as ModelElement;
}

return (currentElement == null) ? null : currentElement.Store;


}

See Also
Navigating and Updating a Model in Program Code
Customizing the Model Explorer
10/18/2017 3 min to read Edit Online

You can change the appearance and behavior of the explorer for your domain-specific language designer as
follows:
Change the window title.
Change the tab icon.
Change the icons for nodes.
Hide nodes.

Changing the Window Title


To change the window title of the generated explorer, select Explorer Behavior in the DSL Explorer, and then in
the Properties window, set the Title property to the title you want.

Changing the Tab Icon


To change the tab icon for the explorer, use a 16x16-pixel icon in a .bmp file. Put the icon file in the
\DslPackage\Resources\ folder, and then change the file name to ModelExplorerToolWindowBitmaps.bmp. For
example, you could change the Visual Studio setup.ico icon file to .bmp format and rename it to
DSLLanguageName\DslPackage\Resources\ModelExplorerToolWindowBitmaps.bmp. The generated
designer will display this icon on the tab of your explorer when it is docked together with Solution Explorer.

Setting Custom Icons on Explorer Nodes


You can customize nodes in your explorer by using explorer node settings. The following procedure shows how to
add an icon to a node.
To add an icon to an explorer node
1. Create a Domain-Specific Language Tools solution by using the Task Flow solution template.
2. Put a .bmp file that contains a 16x16-pixel icon in the Dsl\Resources folder in the solution.
3. In the DSL Explorer, right-click Explorer Behavior and then click Add New Explorer Node Settings.
An ExplorerNodeSettings node appears under the Custom Node Settings node.
4. Select ExplorerNodeSettings, and then in the Properties window, set Class to Actor.
5. Set Icon To Display to the path of the icon file.
6. Transform all templates, and then build and run the solution.
7. In the generated designer, open the Sample diagram.
The Explorer should show three Actor nodes that have your icon.
NOTE
If you have set a node icon for any element that is displayed in the generated explorer, all explorer nodes will display the icon.
If no icon has been set, the nodes will display the default icon.

Changing the Name Displayed on an Explorer Node


You can change how the names of model elements are displayed in your explorer. The following procedure shows
how to display the name of the Task that is referenced by a Comment in the comment node.
To display a property
1. Open the solution that you created in the earlier procedure.
2. Make sure that the Comment references only a single domain class by setting the multiplicity of the role
with property name Subjects to 0..1. The property name should become Subject, and the relationship name
should become CommentReferencesSubject.
3. In the DSL Explorer, right-click Explorer Behavior and then click Add New Explorer Node Settings.
An ExplorerNodeSettings node appears under the Custom Node Settings node.
4. Select ExplorerNodeSettings, and then in the Properties window, set Class to Comment.
5. Right-click the Comment node, and then click Add New Property Path.
A new node appears that is named Property Displayed.
6. Select Property Displayed, and then in the Properties window, click the value field of Path To Property.
Select Comment, then CommentReferencesSubject, then FlowElement. The resulting path should
resemble CommentReferencesSubject.Subject/!Subject.
7. In the value field of Property, select Name.
8. Transform all templates, and then build and run your solution.
9. In the generated designer, open the Sample diagram.
10. Draw a Comment Connector between the comment element and the Task1 element on the diagram.
The Explorer node should display the comment as Task1.

Hiding Nodes
You can hide a node in your explorer by adding its path to the Hidden Nodes node of the DSL Explorer. The
following procedure shows how to hide Comment nodes.
To hide an explorer node
1. Open the solution that you created in the earlier procedure.
2. In the DSL Explorer, right-click Explorer Behavior and then click Add New Domain Path.
A Domain Path node appears under Hidden Nodes.
3. Select Domain Path, and then in the Properties window, click the value field of Path Definition. Select
FlowGraph, then FlowGraphHasComments. The resulting path should resemble
FlowGraphHasComments.Comments
4. Transform all templates, and then build and run your solution.
5. In the generated designer, open the Sample diagram.
The explorer should show only an Actors node, and should not show the Comments node.

See Also
Domain-Specific Language Tools Glossary
Domain Path Syntax
10/18/2017 1 min to read Edit Online

DSL Definitions use an XPath-like syntax to locate specific elements in a model.


Ordinarily you do not have to work with this syntax directly. Where it appears in DSL Details or Properties window,
you can click the downward arrow and use the path editor. However, the path appears in this form in the field after
you have used the editor.
A domain path takes the following form:
RelationshipName.PropertyName/!Role

The syntax traverses the tree of the model. For example, the domain relationship CommentReferencesSubjects in
the illustration above has a Subjects role. The path segment /!Subjectt specifies that the path finishes on elements
accessed through the Subjects role.
Each segment starts with the name of a domain relationship. If the traversal is from an element to a relationship,
the path segment appears as Relationship.PropertyName. If the hop is from a link to an element, the path segment
appears as Relationship/!RoleName.
Slashes separate the syntax of a path. Each path segment is either a hop from an element to a link (an instance of a
relationship) or from a link to an element. Path segments frequently appear in pairs. One path segment represents
a hop from an element to a link, and the next segment represents a hop from the link to the element at the other
end. (Any link can also be the source or target of a relationship itself).
The name that you use for the element-to-link hop is the value of the role's Property Name . The name that you use
for the link-to-element hop is the target role name.

See Also
Understanding Models, Classes and Relationships
Properties of Model Elements
10/18/2017 1 min to read Edit Online

This section lists the properties that you see in the Properties window when you select an element in the DSL
Definition diagram or in the DSL Explorer.
In some cases, additional properties can be found in the DSL Details window.

Related Sections
Customizing and Extending a Domain-Specific Language
Overriding and Extending the Generated Classes
How to Define a Domain-Specific Language
Properties of a DSL Definition
10/18/2017 1 min to read Edit Online

DslDefinition properties define domain-specific language definition properties such as version numbering. The
DslDefinition properties appear in the Properties window when you click an open area of the diagram in the
Domain-Specific Language Designer.
For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.
DslDefinition has the properties in the following table:

PROPERTY DESCRIPTION DEFAULT

Access Modifier Determines if the access modifier for the public


domain class is public or internal.

Custom Attributes Custom defined attributes for the <none>


domain class.

Note Use the browse button to add an


attribute.

Company Name The name of the current company name Current company name
in the system registry.

Name The name of this domain class. Current name

Namespace The namespace affiliated with this Current namespace


domain class.

Package Guid The guid for the Visual Studio package <none>
generated for this DSL.

Package Namespace The namespace for the Visual Studio <none>


package generated for this DSL.

Product Name The name of the product that will be <none>


registered for the Visual Studio package
generated for this DSL.

Notes Notes associated with this domain class. <none>

Description Description for this domain class. <none>

Display Name The name that will be displayed in the <none>


generated designer for this domain
class.

Help Keyword The help keyword associated with this <none>


domain class.
PROPERTY DESCRIPTION DEFAULT

Build The incremental build number for this 0


domain-specific language definition.

Major Version The incremental major build number for 1


this domain-specific language definition.

Minor Version The incremental minor build number for 0


this domain-specific language definition.

Revision The incremental revision build number 0


for this domain-specific language
definition.

See Also
Domain-Specific Language Tools Glossary
Properties of Domain Classes
10/18/2017 1 min to read Edit Online

Domain classes have the properties in the following table. For information about domain classes, see
Understanding Models, Classes and Relationships. For more information about how to use these properties, see
Customizing and Extending a Domain-Specific Language.

PROPERTY DESCRIPTION DEFAULT

Access Modifier The level of access of the domain class ( public


public or internal ).

Custom Attributes Used to add attributes to the source <none>


code class that is generated from this
domain class.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the domain class ( none , abstract or
sealed ).

Base Class If this domain class is derived, the name <none>


of the base class.

Name The name of this domain class. Current name

Namespace The namespace of this domain class. Current namespace

Notes Informal notes that are associated with <none>


this domain class.

Description The description that is used to <none>


document the UI of the generated
designer.

Display Name The name that will be displayed in the <none>


generated designer for this domain
class.

Help Keyword The optional keyword that is used to <none>


index F1 help for this domain class.
See Also
Domain-Specific Language Tools Glossary
Properties of Domain Properties
10/18/2017 3 min to read Edit Online

A domain property is a feature of a model element that can hold a value. For example, the Person domain class
could have properties Name and BirthDate . In the DSL Definition, domain properties are listed in the domain class
box on the diagram and under the domain class in DSL Explorer. For more information, see How to Define a
Domain-Specific Language.

NOTE
The word "property" has two uses. A domain property is a feature that you define on a domain class. By contrast, many
elements of a DSL have properties, which are listed in the Properties window in the DSL Definition. For example, every
domain property has a set of properties, which are described in this topic.

At run time, when a user creates instances of the domain class, the values of the domain properties can be seen in
the Properties window, and can be displayed on the shapes.
Most domain properties are implemented as ordinary CLR properties. However, from a programming point of view,
domain properties have richer functionality than ordinary program properties:
You can define rules and events that monitor the state of a property. For more information, see Responding
to and Propagating Changes.
Transactions help prevent inconsistent states. For more information, see Navigating and Updating a Model in
Program Code.
When you select a Domain Property in a diagram or in DSL Explorer, you can see the following items in the
Properties window. For more information about how to use these items, see Customizing and Extending a
Domain-Specific Language.

PROPERTY DESCRIPTION DEFAULT VALUE

Description The description that is used to <none>


document the user interface (UI) of the
generated designer.

Display Name The name that will be displayed in the <none>


generated designer for this domain
property. It can contain spaces and
punctuation, for example "Song Title".
PROPERTY DESCRIPTION DEFAULT VALUE

Element Name Provider This is applicable only if you have set <none>
Is Element Name to true . You can
write code that provides a name for a
new element of a domain class,
overriding the default behavior.

In a code file in the DSL project, create a


class that is derived from
ElementNameProvider.

Then in DSL Explorer, right-click the


root of the DSL, and click Add External
Type. Enter the name of your class.

Select this domain property again, and


select the name of the class in the drop-
down list.

Getter Access Modifier The level of access of the domain class ( public
public or internal ). This controls
the scope in which program code can
access the property.

Help Keyword The optional keyword that is used to <none>


index F1 help for this domain property.

Is Browsable If True , the domain property is True


displayed to the user in the properties
window when models of this DSL are
open.

If False , the domain property is


hidden in the UI.

If you want to make the domain


property visible but read-only, set Is UI
Read Only.

Is Element Name If True , this domain property will be False


displayed as the name of its model
element in DSL Explorer.

New model elements will receive a


unique default value for this property. If
you want to control how these values
are generated, set Element Name
Provider.
PROPERTY DESCRIPTION DEFAULT VALUE

Is UI Read Only If True , the value of the domain False


property cannot be changed by using
the UI. It can still be set by programs,
and will be visible in the Properties
window.

If you want to hide the domain


property from the user, set Is
Browsable. If you want to control
access by programs, set Setter Access
Modifier.

Kind The kind of domain property ( Normal , Normal


Calculated , or CustomStorage ). For
more information, see Calculated and
Custom Storage Properties.

Name The name of this domain property. It <none>


must be a valid identifier, for example
SongTitle.

Notes Informal notes that are associated with <none>


this domain property.

Setter Access Modifier The access modifier for the setter. This public
controls the scope in which program
code can set the property.

Type The type of property. To add to the list String


of available types, right-click the root of
the DSL in DSL explorer, and click Add
External Type.

See Also
Domain-Specific Language Tools Glossary
Properties of Domain Relationships
10/18/2017 1 min to read Edit Online

The properties in the following table are associated with a domain relationship. For information about domain
relationships, see Understanding Models, Classes and Relationships. For more information about how to use these
properties, see Customizing and Extending a Domain-Specific Language.

PROPERTY DESCRIPTION DEFAULT

Access Modifier The level of access of the domain public


relationship ( public or internal ).

Custom Attributes Used to add attributes to the source <none>


code class that is generated from the
domain relationship.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) is generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , indicates that a custom False


constructor is provided in the source
code. For more information, see
Overriding and Extending the
Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the <none>


source code class that is generated
from the domain relationship ( none ,
abstract or sealed ).

Allows Duplicates If True , duplicate links of the domain False


relationship may be created between
the same two elements.

Base Relationships If the domain relationship is derived, <none>


the base relationship of the domain
relationship.

Is Embedding If True , the domain relationship is an <both>


embedding relationship. If False , the
relationship is a reference relationship.

Name The name of the domain relationship. Current name

Namespace The namespace that is affiliated with the Current namespace


domain relationship.

Notes Informal notes that are associated with <none>


the domain relationship.
PROPERTY DESCRIPTION DEFAULT

Description The description that is used to <none>


document code and is used in the UI of
the generated designer.

Display Name The name that is displayed in the <none>


generated designer for the domain
relationship.

Help Keyword The optional keyword that is used to <none>


index F1 help for the domain
relationship.

See Also
Domain-Specific Language Tools Glossary
Properties of Domain Roles
10/18/2017 3 min to read Edit Online

The properties in the following table are associated with a domain role. For information about domain roles, see
Understanding Models, Classes and Relationships. For more information about how to use these properties, see
Customizing and Extending a Domain-Specific Language.

PROPERTY DESCRIPTION DEFAULT

Collection Type If this role has multiplicity of 0..* or 1..*, (none) - LinkedElementCollection<T>
this property customizes the generic is used
type that is used to store the collection
of links.

Custom Attributes Attributes that you specify here will be <none>


added as attributes to the generated
code class.

Is Property Browsable If True , and if the multiplicity of the True


relationship is 0..1 or 1..1, the role
property can be browsed by the user in
the Properties window. The property
displays the name of the element at the
other end of the relationship link.

Is Property Generator If True , a role property is generated True


for this role, which you can use to
traverse the relationship in program
code. If you set this false, you can
traverse the relationship in a less
efficient manner by using static
methods of the domain relationship.

Property Getter Access Modifier The access modifier for the getter for public
the generated property ( public ,
internal , private , protected , or
protected internal ).

Property Setter Access Modifier The access modifier for the setter for public
the generated property ( public ,
internal , private , protected , or
protected internal ).

Multiplicity The number of model elements which Depends on the relationship type and
can play the opposite role ( 0..1 , whether this is the source or target role
1..1 , 0..* , or 1..* ). If the in the relationship.
multiplicity is 0..* or 1..* , then the
generated property represents a
collection; otherwise, the generated
property represents a single model
element.
PROPERTY DESCRIPTION DEFAULT

Name The name of the domain role. This The name of the domain class of the
property can not contain whitespace. role player for this role.

Propagates Copy DoNotPropagateCopy - The copied role PropagateCopyToLinkAndOppositeRolePlayer


player will have no copy of this link. for the source roles of embeddings.

PropagateCopyToLinkOnly - The DoNotPropagateCopy for other roles.


copied link points to the existing
opposite role player. For more information, see Customizing
Copy Behavior
PropagateCopyToLinkAndOppositeRolePlayer
- The copied link points to a copy of the
opposite role player.

Propagates Delete True to delete the element that plays True for the target of an embedding
this role when the associated link is role.
deleted.
False for other roles.

For more information, see Customizing


Deletion Behavior.

Property Name The name of the property generated in The name of the opposite role if this
the code of the role player. This name role has a zero-to-one or a one-to-one
cannot contain whitespace. multiplicity; otherwise, the pluralized
name of the opposite role.

Role Player The domain class of the element that The domain class of the role player for
can play this role in the relationship. this role.
This property is read-only.

Notes Informal notes that are associated with <none>


the domain role.

Category The category under which the <none>


generated property appears in the
Properties window in the generated
designer. If this property is empty, then
the generated property appears under
the Misc category

Description The description that is used to Description for the full name of the
document code and is used in the UI of role
the generated designer.

The description appears in the


Intellisense tooltip for the generated
property on the role player class.

Display Name The name that is displayed in the The adjusted value of the Name
generated designer for the domain role. property.

Help Keyword The optional keyword that is used to <none>


index F1 help for the domain role.
PROPERTY DESCRIPTION DEFAULT

Property Display Name The name that is displayed in the The adjusted value of the Property
generated designer for the generated Name property.
role property.

NOTE
The default value of a display name is based on the associated property value by inserting spaces before each upper-case
character that is preceded by a lower-case character and that is not followed by another upper-case character.

See Also
Properties of Domain Relationships
Properties of Diagrams
10/18/2017 1 min to read Edit Online

You can set properties that specify how diagrams will appear in the generated designer. For example, you can
specify a default color for text in the diagram.
For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.
The following table lists the properties of diagrams.

PROPERTY DESCRIPTION DEFAULT

Fill Color The fill color for the diagram. White

Text Color The color of the text that is displayed on Black


the diagram.

Access Modifier The access modifier of the class (public Public


or internal).

Custom Attributes Used to add attributes to the generated <none>


code class.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes..

Inheritance Modifier Describes the kind of inheritance of the None


source code class that is generated from
the diagram ( none , abstract or
sealed ).

Base Diagram The base class of this diagram. (none)

Name The name of this diagram. Current name

Namespace The namespace that is affiliated with this Current namespace


diagram.

Class Represented The root domain class that this diagram Current root class if applicable
represents.

Notes Informal notes that are associated with <none>


this element.
PROPERTY DESCRIPTION DEFAULT

Exposes Fill Color As Property If True , the user can set the fill color of False
the diagram of the generated designer.
To set this, right click the diagram shape
and click Add Explosed.

Exposes Text Color As Property If True , the user can set the text color False
of the diagram in the generated
designer. To set this, right click the
diagram shape and click Add Explosed.

Description The description that is used to <none>


document the generated designer.

Display Name The name that will be displayed in the <none>


generated designer for this diagram.

Help Keyword The keyword that is used to index F1 <none>


help for this diagram.

See Also
Domain-Specific Language Tools Glossary
Properties of Swimlanes
10/18/2017 2 min to read Edit Online

You can add swimlanes to a diagram. Swimlanes divide a diagram into vertical or horizontal areas. You can define
other shapes to be displayed inside swimlanes. For more information, see How to Define a Domain-Specific
Language. For more information about how to use these properties, see Customizing and Extending a Domain-
Specific Language.
Swimlanes have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Body Fill Color The fill color for the body of the White
swimlane.

Header Fill Color The fill color for the header of the DarkGray
swimlane.

Separator Color The color of the separator line. LightGray

Separator Line Style The style of the separator line ( Solid , Dash
Dash , Dot , DashDot , DashDotDot ,
or Custom ).

Separator Thickness The thickness of the separator line in 0.03125


inches.

Text Color The color that is used for text Black


decorators that are associated with this
swimlane.

Access Modifier The level of access of the class ( public Public


or internal ).

Custom Attributes Used to add attributes to the code class <none>


that is generated from this swimlane.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the swimlane ( none , abstract or
sealed ).
PROPERTY DESCRIPTION DEFAULT

Base Swimlane The base class of this swimlane. (none)

Name The name of this swimlane. Current name

Namespace The namespace that is affiliated with Current namespace


this swimlane.

Tooltip Type How the tooltip is defined ( fixed , <none>


variable , or none ). If fixed , then
the value of the Fixed Tooltip Text
property is used; if variable , then the
tooltip is defined in custom code.

Notes Informal notes that are associated with <none>


this swimlane.

Alignment Horizontal or vertical alignment. Vertical

Initial Height The initial height of this swimlane, in 0


inches. Applicable only to horizontal
swimlanes.

Initial Width The initial width of this swimlane, in 0


inches. Applicable only to vertical
swimlanes.

Exposes Text Color If True , the user can set the color of a False
swimlane in the generated designer. To
set this, right-click the swimlane shape
and click Add Exposed.

Description Used to document the generated <none>


designer.

Display Name The name that will be displayed in the <none>


generated designer to refer to this
swimlane class.

Fixed Tooltip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this swimlane.

See Also
Domain-Specific Language Tools Glossary
Properties of Geometry Shapes
10/18/2017 2 min to read Edit Online

You can use geometry shapes to specify how instances of domain classes are displayed in a domain-specific
language. For more information, see How to Define a Domain-Specific Language. For more information about how
to use these properties, see Customizing and Extending a Domain-Specific Language.
Geometry shapes have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Fill Color The fill color of this shape. White

Fill Gradient Mode The fill gradient mode of this shape Horizontal
(Horizontal, Vertical, Forward Diagonal,
Backward Diagonal, or None).

Geometry The geometry of this shape (Rectangle, Rectangle


Rounded Rectangle, Ellipse, or Circle).

Has Default Connection Points If True , the shape will use top, False
bottom, left, and right connection
points in the generated designer.

Outline Color The outline color of this shape. Black

Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).

Outline Thickness The outline thickness of this shape. 0.03125

Text Color The color that is used for text Black


decorators that are associated with this
shape.

Access Modifier The access modifier of the class (public Public


or internal).

Custom Attributes Used to add attributes to the source <none>


code class that is generated for this
shape.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.
PROPERTY DESCRIPTION DEFAULT

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the shape ( none , abstract or
sealed ).

Base Geometry Shape The base class of this shape. (none)

Name The name of this shape. Current name

Namespace The namespace that is affiliated with Current namespace


this shape.

Tooltip Type How the tooltip is defined (fixed, None


variable, or none). If fixed, then the
value of the Fixed Tooltip Text
property is used as the tooltip; if
variable, then the tooltip is defined in
custom code.

Notes Informal notes that are associated with <none>


this element.

Initial Height Initial height of this shape, in inches. 1

Initial Width Initial width of this shape, in inches. 1.5

Exposed Fill Color As Property If True , the user can set the stated False
property of a shape. To set this, right-
Exposed Fill Gradient Mode click the shape definition and click Add
Exposed.
Exposed Outline Color As Property

Exposed Outline Dash Style As Property

Exposed Outline Thickness As Property

Exposes Text Color

Description The description that is used to <none>


document the generated designer.

Display Name The name that will be displayed in the <none>


generated designer for this shape.

Fixed Tooltip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this shape.
See Also
Domain-Specific Language Tools Glossary
Properties of Image Shapes
10/18/2017 2 min to read Edit Online

You can use image shapes to specify how domain classes appear in a generated designer. Define an image shape
by setting the Image property of the class to a predefined image file. The following formats are supported:
.gif
.jpg
.jpeg
.bmp
.wmf
.emf
.png
By default, designer resource files, such as image files, are located in the Resourcesfolder in the Dsl project.
For more information, see How to Define a Domain-Specific Language. For more information about how to
use these properties, see Customizing and Extending a Domain-Specific Language.
Image shapes have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Fill Color The fill color of this shape. White

Fill Gradient Mode The fill gradient mode of this shape. Horizontal

Has Default Connection Points If True , the shape will use top, False
bottom, left, and right connection
points in the generated designer.

Outline Color The outline color of this shape. Black

Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).

Outline Thickness The outline thickness of this shape. 0.03125

Text Color The color that is used for text Black


decorators that are associated with this
shape.

Access Modifier The access modifier of the geometry Public


shape (public or internal).
PROPERTY DESCRIPTION DEFAULT

Custom Attributes Used to add attributes to the source <none>


code class that is generated from this
shape.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the image shape ( none , abstract or
sealed ).

Base Image Shape The base class of this shape. (none)

Name The name of this shape. Current name

Namespace The namespace that is affiliated with Current namespace


this shape.

Tooltip Type The place where the tooltip is defined none


(fixed, variable, or none). If fixed, then
the value of the Fixed Tooltip Text
property is used as the tooltip; if
variable, then the tooltip is defined in
custom code.

Notes Informal notes that are associated with <none>


this shape.

Initial Height The initial height of this shape, in inches. 1

Initial Width The initial width of this shape, in inches. 1.5

Exposed Fill Color As Property If True , the user can set the stated False
property of a shape. To set this, right-
Exposed Fill Gradient Mode click the shape definition and click Add
Exposed.
Exposed Outline Color As Property

Exposed Outline Dash Style As Property

Exposed Outline Thickness As Property

Exposes Text Color

Description Used to document the generated <none>


designer.
PROPERTY DESCRIPTION DEFAULT

Display Name The name that will be displayed in the <none>


generated designer for this shape.

Fixed Tooltip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this element.

Image The path to the image file that is used <none>


for this shape.

See Also
Domain-Specific Language Tools Glossary
Properties of Compartment Shapes
10/18/2017 2 min to read Edit Online

Compartment shapes are one of the shapes you can use to display a domain class in a domain-specific language.
You can expand and collapse the compartments.
For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.
Compartment shapes have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Default Expand Collapse State If Expanded , the compartments are Expanded


shown on creation. If Collapsed , they
are not.

Fill Color The fill color of this shape. White

Fill Gradient Mode The fill gradient mode of this shape. Horizontal

Geometry The geometry of this shape (Rectangle Rectangle


or Rounded Rectangle).

Has Default Connection Points If True , the shape will use top, False
bottom, left, and right connection
points in the generated designer.

Is Single Compartment Header Visible If False , and the shape has a single True
compartment, the header of the
compartment is not visible.

Outline Color The outline color of this shape. Black

Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, Custom).

Outline Thickness The outline thickness of this shape. 0.03125

Text Color The color used for text decorators that Black
are associated with this shape.

Access Modifier The level of access of the compartment Public


shape ( public or internal ).

Custom Attributes Used to add attributes to the source <none>


code class that is generated from this
compartment shape
PROPERTY DESCRIPTION DEFAULT

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.

Inheritance Modifier Describes the kind of inheritance of the None


source code class that is generated from
the compartment shape ( none ,
abstract or sealed ).

Base Compartment Shape The base class of this shape. (none)

Name The name of this shape. Current name

Namespace The namespace that is affiliated with Current namespace


this shape.

Tooltip Type How the tooltip is defined (fixed, none


variable, or none). If fixed, then the
value of the Fixed Tooltip Text
property is used as the tooltip; if
variable, then the tooltip is defined in
custom code.

Notes Informal notes that are associated with <none>


this shape.

Initial Height The initial height of this shape, in inches. 1


For compartment shapes, this is the
height of the header section only and it
cannot be resized.

Initial Width The initial width of this shape, in inches. 1.5

Exposed Fill Color As Property If True , the user can set the stated False
property of a shape. To set this, right-
Exposed Fill Gradient Mode click the shape definition and click Add
Exposed.
Exposed Outline Color As Property

Exposed Outline Dash Style As Property

Exposed Outline Thickness As Property

Exposes Text Color

Description Used to document the generated <none>


designer.
PROPERTY DESCRIPTION DEFAULT

Display Name The name that will be displayed in the <none>


generated designer for this shape.

Fixed Tooltip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this shape.

See Also
Domain-Specific Language Tools Glossary
Properties of Port Shapes
10/18/2017 2 min to read Edit Online

You can use port shapes to represent domain classes in the generated designer.
For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.
Port shapes have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Fill Color The fill color of this shape. White

Fill Gradient Mode The fill gradient mode of this shape. Horizontal

Geometry The geometry of this shape (Rectangle, Rectangle


Rounded Rectangle, Ellipse, or Circle).

Has Default Connection Points If True , the shape will use top, False
bottom, left, and right connection
points in the generated designer.

Outline Color The outline color of this shape. Black

Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).

Outline Thickness The outline thickness of this shape. 0.03125

Text Color The color that is used for text Black


decorators that are associated with this
shape.

Access Modifier The level of access of the class ( public Public


or internal ).

Custom Attributes Used to add attributes to the source <none>


code class that is generated from this
shape.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.
PROPERTY DESCRIPTION DEFAULT

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the port ( none , abstract or
sealed ).

Base Port The base class of this shape. (none)

Name The name of this shape. Current name

Namespace The namespace that is affiliated with Current namespace


this shape.

Tool Tip Type How the tooltip is defined (fixed, none


variable, or none). If fixed, then the
value of the Fixed Tooltip Text
property is used as the tooltip; if
variable, then the tooltip is defined in
custom code.

Notes Informal notes that are associated with <none>


this shape.

Initial Height The initial height of this shape, in inches. 1

Initial Width The initial width of this shape, in inches. 1.5

Exposed Fill Color As Property If True , the user can set the stated False
property of a shape. To set this, right-
Exposed Fill Gradient Mode click the shape definition and click Add
Exposed.
Exposed Outline Color As Property

Exposed Outline Dash Style As Property

Exposed Outline Thickness As Property

Exposes Text Color

Description Used to document the generated <none>


designer.

Display Name The name that will be displayed in the <none>


generated designer for this shape.

Fixed Tool Tip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this shape.

See Also
Domain-Specific Language Tools Glossary
Properties of Decorators
10/18/2017 2 min to read Edit Online

Decorators are icons, text, or expand/collapse chevrons that can appear on shapes or connectors on the diagram.
The following tables show the properties for the three kinds of decorator. Some of the properties appear only on
shape decorators or only on connector decorators.
For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.

Expand/Collapse Decorator
PROPERTY DESCRIPTION DEFAULT

DisplayName The name of the decorator that will be Expand Collapse Decorator
displayed in the generated designer.

Name The name of the decorator. ExpandCollapseDecorator

Notes Informal notes that are associated with <none>


this decorator.

HorizontalOffset The horizontal offset, relative to the 0


default position of the decorator, in
inches. (On shapes only.)

VerticalOffset The vertical offset, relative to the default 0


position of the decorator, in inches. (On
shapes only.)

OffsetFromLine The offset of the decorator from the 0


line, relative to its default position, in
inches. (On connectors only.)

OffsetFromShape The offset of the decorator from the 0


shape, relative to its default position, in
inches. (On connectors only.)

Position The default position of the decorator. SourceTop

Icon Decorator
PROPERTY DESCRIPTION DEFAULT

DefaultIcon The path of the icon or image file to be <none>


displayed.

DisplayName The name of the decorator to be Icon Decorator


displayed in the generated designer.
PROPERTY DESCRIPTION DEFAULT

Name The name of the decorator. IconDecorator

Notes Informal notes that are associated with <none>


the decorator.

HorizontalOffset The horizontal offset, relative to the 0


default position of the decorator, in
inches. (On shapes only.)

VerticalOffset The vertical offset, relative to the default 0


position of the decorator, in inches. (On
shapes only.)

OffsetFromLine The offset of the decorator from the 0


line, relative to its default position, in
inches. (On connectors only.)

OffsetFromShape The offset of the decorator from the 0


shape, relative to its default position, in
inches. (On connectors only.)

Position The default position of the decorator. SourceTop

TextDecorator
PROPERTY DESCRIPTION DEFAULT

DefaultText The default text to be displayed. Label

DisplayName The name of the decorator to be Label


displayed in the generated designer.

FontSize The font size for the text that is 8


displayed in the decorator.

FontStyle The font style for the text that is Regular


displayed in the decorator.

Name The name of the decorator. Label

Notes Informal notes that are associated with <none>


the decorator.

HorizontalOffset The horizontal offset, relative to the 0


default position of the decorator, in
inches. (On shapes only.)

VerticalOffset The vertical offset, relative to the default 0


position of the decorator, in inches. (On
shapes only.)
PROPERTY DESCRIPTION DEFAULT

OffsetFromLine The offset of the decorator from the 0


line, relative to its default position, in
inches. (On connectors only.)

OffsetFromShape The offset of the decorator from the 0


shape, relative to its default position, in
inches. (On connectors only.)

Position The default position of the decorator. TargetBottom

See Also
Domain-Specific Language Tools Glossary
Properties of Connectors
10/18/2017 2 min to read Edit Online

Connectors represent domain relationships in a generated designer.


For more information, see How to Define a Domain-Specific Language. For more information about how to use
these properties, see Customizing and Extending a Domain-Specific Language.
Connectors have the properties that are listed in the following table.

PROPERTY DESCRIPTION DEFAULT

Color The color of this connector. Black

Dash Style The dash style for the line for this Solid
connector (Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).

Source End Style The source end style for this connector None
(HollowArrow, EmptyArrow, FilledArrow,
EmptyDiamond, FilledDiamond, or
None).

Target End Style The target end style for this connector None
(HollowArrow, EmptyArrow, FilledArrow,
EmptyDiamond, FilledDiamond, or
None).

Text Color The color that is used for text Black


decorators that are associated with this
connector.

Thickness The thickness of the line for this 0.03125


connector, measured in inches.

Access Modifier The level of access of the class ( public Public


or internal ).

Custom Attributes Used to add attributes to the source <none>


code class that is generated from this
connector.

Generates Double Derived If True , both a base class and a partial False
class (to support customization through
overrides) will be generated. For more
information, see Overriding and
Extending the Generated Classes.

Has Custom Constructor If True , a custom constructor will be False


provided in the source code. For more
information, see Overriding and
Extending the Generated Classes.
PROPERTY DESCRIPTION DEFAULT

Inheritance Modifier Describes the kind of inheritance of the none


source code class that is generated from
the connector ( none , abstract or
sealed ).

Base Connector The base class of this connector. (none)

Name The name of this connector. Current name

Namespace The namespace that is affiliated with this Current namespace


connector.

Tooltip Type How the tooltip is defined (fixed, <none>


variable, or none). If fixed, then the
value of the Fixed Tooltip Text
property is used as the tooltip; if
variable, then the tooltip is defined in
custom code.

Notes Informal notes that are associated with <none>


this connector.

Routing Style The style that is used for routing the Rectilinear
connector. A Rectilinear connector
makes right-angled turns as required; a
Straight connector does not.

Exposed Color As Property If True , the user can set the stated False
property of a shape. To set this, right-
Exposed Dash Style As Property click the shape definition and click Add
Exposed.
Exposed Thickness As Property

Exposes Text Color

Description Used to document the generated <none>


designer.

Display Name The name that will be displayed in the <none>


generated designer for this connector.

Fixed Tooltip Text The text that is used for a fixed tooltip. <none>

Help Keyword The keyword that is used to index F1 <none>


help for this element.

See Also
Domain-Specific Language Tools Glossary
Sharing Classes between DSLs by using a DSL Library
10/18/2017 1 min to read Edit Online

In the Visual Studio Visualization and Modeling SDK, you can create an incomplete DSL Definition that you can
import into another DSL. This lets you factor common parts of similar models.

Creating and using DSL Libraries


To create a DSL Library
1. Create a new DSL project, and choose the DSL Library solution template.
A single DSL project will be created with an empty model.
2. You can add domain classes, relationships, shapes and so on.
The elements in the library do not have to form a single embedding tree.
To define a relationship that importers can use, create two domain classes and create the relationship
between them.
Consider setting the Inheritance Modifier of the domain classes to Abstract .
3. You can add elements that you define in DSL Explorer, such as Connection Builders.
4. You can add customizations that require additional code, such as validation constraints.
5. Click Transform All Templates.
6. Build the project.
7. When you distribute the DSL for other people to use, you must provide both the compiled assembly (DLL)
and the file DslDefinition.dsl . You can find the compiled assembly in a folder under Dsl\bin\*
To import a DSL Library
1. In another DSL Definition, in DSL Explorer, right-click the root class of the DSL, and then click Add New
DslLibrary Import.
2. In the Properties window, set the File Path of the library. You can use either a relative or an absolute path.
The imported library appears in DSL Explorer, in read-only mode.
3. You can use the imported classes as base classes. Create a domain class in the importing DSL, and in the
Properties window, set Base Class to an imported class.
4. Click Transform All Templates.
5. Add to the DSL project a reference to the assembly (DLL) that was built by the DSL Library project.
6. Build the solution.
A DSL Library can import other libraries. When you import a library, its imports also automatically appear in
DSL Explorer.

See Also
How to Define a Domain-Specific Language
In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Writing Code to Customise a Domain-Specific
Language
10/18/2017 2 min to read Edit Online

This section shows you how to use custom code to access, modify, or create a model in a domain-specific
language.
There are several contexts in which you can write code that works with a DSL:
Custom commands. You can create a command that users can invoke by right-clicking on the diagram,
and which can modify the model. For more information, see How to: Add a Command to the Shortcut
Menu.
Validation. You can write code that verifies that the model is in a correct state. For more information,
see Validation in a Domain-Specific Language.
Overriding the default behavior. You can modify many aspects of the code that is generated from
DslDefinition.dsl. For more information, see Overriding and Extending the Generated Classes.
Text Transformation. You can write text templates that contain code that accesses a model and
generates a text file, for example to generate program code. For more information, see Generating Code
from a Domain-Specific Language.
Other Visual Studio extensions. You can write separate VSIX extensions that read and modify models.
For more information, see How to: Open a Model from File in Program Code
Instances of the classes that you define in DslDefinition.dsl are kept in a data structure called the In-
Memory Store (IMS) or Store. The classes you define in a DSL always take a Store as an argument to the
constructor. For example, if your DSL defines a class called Example:
Example element = new Example (theStore);

keeping objects in the Store (instead of just as ordinary objects) provides several benefits.
Transactions. You can group a series of related changes into a transaction:
using (Transaction t = store.TransactionManager.BeginTransaction("updates"))

// make several changes to Store elements here

t.Commit();

If an exception occurs during the changes, so that the final Commit() is not performed, the Store will be
reset to its previous state. This helps you to make sure that errors do not leave the model in an
inconsistent state. For more information, see Navigating and Updating a Model in Program Code.
Binary relationships. If you define a relationship between two classes, instances at both ends have a
property that navigates to the other end. The two ends are always synchronized. For example, if you
define a parenthood relationship with roles named Parents and Children, you could write:
John.Children.Add(Mary)
Both of the following expressions are now true:
John.Children.Contains(Mary)

Mary.Parents.Contains(John)

You could also achieve the same effect by writing:


Mary.Parents.Add(John)

For more information, see Navigating and Updating a Model in Program Code.
Rules and Events. You can define rules that fire whenever specified changes are made. Rules are used,
for example, to keep shapes on the diagram up to date with the model elements they present. For more
information, see Responding to and Propagating Changes.
Serialization. The Store provides a standard way to serialize the objects it contains to a file. You can
customize the rules for serializing and deserializing. For more information, see Customizing File Storage
and XML Serialization.

See Also
Customizing and Extending a Domain-Specific Language
Overriding and Extending the Generated Classes
10/18/2017 4 min to read Edit Online

Your DSL Definition is a platform on which you can build a powerful set of tools that are based on a domain-
specific language. Many extensions and adaptations can be made by overriding and extending the classes that
are generated from the DSL Definition. These classes include not just the domain classes that you have
explicitly defined in the DSL Definition diagram, but also other classes that define the toolbox, explorer,
serialization, and so on.

Extensibility Mechanisms
Several mechanisms are provided to allow you to extend the generated code.
Overriding methods in a partial class
Partial class definitions allow a class to be defined in more than one place. This allows you to separate the
generated code from the code that you write yourself. In your manually-written code, you can override classes
inherited by the generated code.
For example, if in your DSL Definition you define a domain class named Book , you could write custom code
that adds override methods:
public partial class Book

protected override void OnDeleting()

MessageBox.Show("Deleting book " + this.Title);

base.OnDeleting();

} }

NOTE
To override methods in a generated class, always write your code in a file that is separated from the generated files.
Typically, the file is contained in a folder that is named CustomCode. If you make changes to the generated code, they
will be lost when you regenerate the code from the DSL Definition.

To discover what methods you can override, type override in the class, followed by a space. The IntelliSense
tooltip will tell you what methods can be overridden.
Double -Derived Classes
Most of the methods in generated classes are inherited from a fixed set of classes in the Modeling
namespaces. However, some methods are defined in the generated code. Ordinarily, this means that you
cannot override them; you cannot override in one partial class the methods that are defined in another partial
definition of the same class.
Nevertheless, you can override these methods by setting the Generates Double Derived flag for the domain
class. This causes two classes to be generated, one being an abstract base class of the other. All the method
and property definitions are in the base class, and only the constructor is in the derived class.
For example, in the sample Library.dsl, the CirculationBook domain class has the Generates``Double Derived
property set to true . The generated code for that domain class contains two classes:
CirculationBookBase , which is an abstract and which contains all the methods and properties.
CirculationBook , which is derived from CirculationBookBase . It is empty, except for its constructors.
To override any method, you create a partial definition of the derived class such as CirculationBook .
You can override both the generated methods and the methods inherited from the modeling
framework.
You can use this method with all types of element, including model elements, relationships, shapes,
diagrams, and connectors. You can also override methods of other generated classes. Some generated
classes such as the ToolboxHelper are always double-derived.
Custom Constructors
You cannot override a constructor. Even in double-derived classes, the constructor must be in the derived
class.
If you want to provide your own constructor, you can do this by setting Has Custom Constructor for the
domain class in the DSL Definition. When you click Transform All Templates, the generated code will not
include a constructor for that class. It will include a call to the missing constructor. This causes an error report
when you build the solution. Double-click the error report to see a comment in the generated code that
explains what you should provide.
Write a partial class definition in a file that is separate from the generated files, and provide the constructor.
Flagged Extension Points
A flagged extension point is a place in the DSL Definition where you can set a property or a check box to
indicate that you will provide a custom method. Custom constructors are one example. Other examples
include setting the Kind of a domain property to Calculated or Custom Storage or setting the Is Custom flag
in a connection builder.
In each case, when you set the flag and regenerate the code, a build error will result. Double-click the error to
see a comment that explains what you have to provide.
Rules
The transaction manager allows you to define rules that run before the end of a transaction in which a
designated event has occurred, such as a change in a property. Rules are typically used to maintain
synchronism between different elements in the store. For example, rules are used to make sure that the
diagram displays the current state of the model.
Rules are defined on a per-class basis, so that you do not have to have code that registers the rule for each
object. For more information, see Rules Propagate Changes Within the Model.
Store Events
The modeling store provides an event mechanism that you can use to listen for specific types of change in the
store, including addition and deletion of elements, changes to property values, and so on. The event handlers
are called after the close of the transaction in which the changes were made. Typically, these events are used
to update resources outside the store.
.NET Events
You can subscribe to some events on shapes. For example, you can listen for mouse-clicks on a shape. You
have to write code that subscribes to the event for each object. This code can be written in an override of
InitializeInstanceResources().
Some events are generated on ShapeFields, which are used to draw decorators on a shape. For an example,
see How to: Intercept a Click on a Shape or Decorator.
These events usually do not occur inside a transaction. You should create a transaction if you want to make
changes in the store.
Navigating and Updating a Model in Program
Code
10/18/2017 14 min to read Edit Online

You can write code to create and delete model elements, set their properties, and create and delete links
between elements. All changes must be made within a transaction. If the elements are viewed on a diagram,
the diagram will be "fixed up" automatically at the end of the transaction.

In this Topic
An Example DSL Definition
Navigating the Model
Accessing Class Information
Perform Changes inside a Transaction
Creating Model Elements
Creating Relationship Links
Deleting Elements
Deleting Relationship Links
Reordering the Links of a Relationship
Locks
Copy and Paste
Navigating and Updating Diagrams
Navigating between Shapes and Elements
Properties of Shapes and Connectors
DocView and DocData

An Example DSL Definition


This is the main part of DslDefinition.dsl for the examples in this topic:
This model is an instance of this DSL:

References and Namespaces


To run the code in this topic, you should reference:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Your code will use this namespace:


using Microsoft.VisualStudio.Modeling;

In addition, if you are writing the code in a different project from the one in which your DSL is defined, you
should import the assembly that is built by the Dsl project.

Navigating the Model


Properties
Domain properties that you define in the DSL definition become properties that you can access in program
code:
Person henry = ...;

if (henry.BirthDate < 1500) ...

if (henry.Name.EndsWith("VIII")) ...

If you want to set a property, you must do so inside a transaction:


henry.Name = "Henry VIII";

If in the DSL definition, a property's Kind is Calculated, you cannot set it. For more information, see
Calculated and Custom Storage Properties.
Relationships
Domain relationships that you define in the DSL definition become pairs of properties, one on the class at
each end of the relationship. The names of the properties appear in the DslDefinition diagram as labels on the
roles at each side of the relationship. Depending on the multiplicity of the role, the type of the property is
either the class at the other end of the relationship, or a collection of that class.
foreach (Person child in henry.Children) { ... }

FamilyTreeModel ftree = henry.FamilyTreeModel;

The properties at opposite ends of a relationship are always reciprocal. When a link is created or deleted, the
role properties on both elements are updated. The following expression (which uses the extensions of
System.Linq ) is always true for the ParentsHaveChildren relationship in the example:
(Person p) => p.Children.All(child => child.Parents.Contains(p))

&& p.Parents.All(parent => parent.Children.Contains(p));

ElementLinks. A relationship is also represented by a model element called a link, which is an instance of the
domain relationship type. A link always has one source element and one target element. The source element
and the target element can be the same.
You can access a link and its properties:
ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);

// This is now true:

link == null || link.Parent == henry && link.Child == edward

By default, no more than one instance of a relationship is allowed to link any pair of model elements. But if in
the DSL definition, the Allow Duplicates flag is true for the relationship, then there might be more than one
link, and you must use GetLinks :
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }

There are also other methods for accessing links. For example:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }

Hidden roles. If in the DSL definition, Is Property Generated is false for a particular role, then no property
is generated that corresponds to that role. However, you can still access the links and traverse the links using
the methods of the relationship:
foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }

The most frequently used example is the PresentationViewsSubject relationship, which links a model element
to the shape that displays it on a diagram:
PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

The Element Directory


You can access all the elements in the store using the element directory:
store.ElementDirectory.AllElements

There are also methods for finding elements, such as the following:
store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Accessing Class Information

You can get information about the classes, relationships, and other aspects of the DSL definition. For example:
DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")
DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

The ancestor classes of model elements are as follows:


ModelElement - all elements and relationships are ModelElements
ElementLink - all relationships are ElementLinks

Perform Changes inside a Transaction


Whenever your program code changes anything in the Store, it must do so inside a transaction. This applies
to all model elements, relationships, shapes, diagrams, and their properties. For more information, see
Transaction.
The most convenient method of managing a transaction is with a using statement enclosed in a try...catch
statement:

Store store; ...


try
{
using (Transaction transaction =
store.TransactionManager.BeginTransaction("update model"))
// Outermost transaction must always have a name.
{
// Make several changes in Store:
Person p = new Person(store);
p.FamilyTreeModel = familyTree;
p.Name = "Edward VI";
// end of changes to Store

transaction.Commit(); // Don't forget this!


} // transaction disposed here
}
catch (Exception ex)
{
// If an exception occurs, the Store will be
// rolled back to its previous state.
}

You can make any number of changes inside one transaction. You can open new transactions inside an active
transaction.
To make your changes permanent, you should Commit the transaction before it is disposed. If an exception
occurs that is not caught inside the transaction, the Store will be reset to its state before the changes.

Creating Model Elements


This example adds an element to an existing model:
FamilyTreeModel familyTree = ...; // The root of the model.
using (Transaction t =
familyTree.Store.TransactionManager
.BeginTransaction("update model"))
{
// Create a new model element
// in the same partition as the model root:
Person edward = new Person(familyTree.Partition);
// Set its embedding relationship:
edward.FamilyTreeModel = familyTree;
// same as: familyTree.People.Add(edward);
// Set its properties:
edward.Name = "Edward VII";
t.Commit(); // Don't forget this!
}

This example illustrates these essential points about creating an element:


Create the new element in a specific partition of the Store. For model elements and relationships, but
not shapes, this is usually the default partition.
Make it the target of an embedding relationship. In the DslDefinition of this example, each Person must
be the target of embedding relationship FamilyTreeHasPeople. To achieve this, we can either set the
FamilyTreeModel role property of the Person object, or add the Person to the People role property of
the FamilyTreeModel object.
Set the properties of a new element, particularly the property for which IsName is true in the
DslDefinition. This flag marks the property that serves to identify the element uniquely within its
owner. In this case, the Name property has that flag.
The DSL definition of this DSL must have been loaded into the Store. If you are writing an extension
such as a menu command, this will typically be already true. In other cases, you can explicitly load the
model into the Store, or use ModelBus to load it. For more information, see How to: Open a Model
from File in Program Code.
When you create an element in this way, a shape is automatically created (if the DSL has a diagram). It
appears in an automatically assigned location, with default shape, color, and other features. If you want
to control where and how the associated shape appears, see Creating an Element and its Shape.

Creating Relationship Links


There are two relationships defined in the example DSL definition. Each relationship defines a role property on
the class at each end of the relationship.
There are three ways in which you can create an instance of a relationship. Each of these three methods has
the same effect:
Set the property of the source role player. For example:
familyTree.People.Add(edward);

edward.Parents.Add(henry);

Set the property of the target role player. For example:


edward.familyTreeModel = familyTree;

The multiplicity of this role is 1..1 , so we assign the value.


henry.Children.Add(edward);
The multiplicity of this role is 0..* , so we add to the collection.
Construct an instance of the relationship explicitly. For example:
FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);

ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);

The last method is useful if you want to set properties on the relationship itself.
When you create an element in this way, a connector on the diagram is automatically created, but it has
a default shape, color, and other features. To control how the associated connector is created, see
Creating an Element and its Shape.

Deleting Elements
Delete an element by calling Delete() :
henry.Delete();

This operation will also delete:


Relationship links to and from the element. For example, edward.Parents will no longer contain henry .
Elements at roles for which the PropagatesDelete flag is true. For example, the shape that displays the
element will be deleted.
By default, every embedding relationship has PropagatesDelete true at the target role. Deleting henry
does not delete the familyTree , but familyTree.Delete() would delete all the Persons . For more
information, see Customizing Deletion Behavior.
By default, PropagatesDelete is not true for the roles of reference relationships.
You can cause the deletion rules to omit specific propagations when you delete an object. This is useful
if you are substituting one element for another. You supply the GUID of one or more roles for which
deletion should not be propagated. The GUID can be obtained from the relationship class:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(This particular example would have no effect, because PropagatesDelete is false for the roles of the
ParentsHaveChildren relationship.)

In some cases, deletion is prevented by the existence of a lock, either on the element or on an element
that would be deleted by propagation. You can use element.CanDelete() to check whether the element
can be deleted.

Deleting Relationship Links


You can delete a relationship link by removing an element from a role property:
henry.Children.Remove(edward); // or:

edward.Parents.Remove(henry); // or:

You can also delete the link explicitly:


edwardHenryLink.Delete();

These three methods all have the same effect. You only need to use one of them.
If the role has 0..1 or 1..1 multiplicity, you can set it to null , or to another value:
edward.FamilyTreeModel = null; // or:
edward.FamilyTreeModel = anotherFamilyTree;

Re-ordering the Links of a Relationship


The links of a particular relationship that are sourced or targeted at a particular model element have a specific
sequence. They appear in the order in which they were added. For example, this statement will always yield
the children in the same order:
foreach (Person child in henry.Children) ...

You can change the order of the links:


ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks
Your changes might be prevented by a lock. Locks can be set on individual elements, on partitions, and on the
store. If any of these levels has a lock that prevents the kind of change that you want to make, an exception
might be thrown when you attempt it. You can discover whether locks are set by using element.GetLocks(),
which is an extension method that is defined in the namespace Microsoft.VisualStudio.Modeling.Immutability.
For more information, see Defining a Locking Policy to Create Read-Only Segments.

Copy and Paste


You can copy elements or groups of elements to an IDataObject:

Person person = personShape.ModelElement as Person;


Person adopter = adopterShape.ModelElement as Person;
IDataObject data = new DataObject();
personShape.Diagram.ElementOperations
.Copy(data, person.Children.ToList<ModelElement>());

The elements are stored as a serialized Element Group.


You can merge elements from an IDataObject into a model:

using (Transaction t = targetDiagram.Store.


TransactionManager.BeginTransaction("paste"))
{
adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}

Merge () can accept either a PresentationElement or a ModelElement . If you give it a PresentationElement ,


you can also specify a position on the target diagram as a third parameter.

Navigating and updating diagrams


In a DSL, the domain model element, which represents a concept such as Person or Song, is separate from the
shape element, which represents what you see on the diagram. The domain model element stores the
important properties and relationships of the concepts. The shape element stores the size, position and color
of the object's view on the diagram, and the layout of its component parts.
Presentation Elements

In your DSL Definition, each element that you specify creates a class that is derived from one of the following
standard classes.

KIND OF ELEMENT BASE CLASS

Domain class ModelElement

Domain relationship ElementLink

Shape NodeShape

Connector BinaryLinkShape

Diagram Diagram

An element on a diagram usually represents a model element. Typically (but not always), a NodeShape
represents a domain class instance, and a BinaryLinkShape represents a domain relationship instance. The
PresentationViewsSubject relationship links a node or link shape to the model element that it represents.
Every node or link shape belongs to one diagram. A binary link shape connects two node shapes.
Shapes can have child shapes in two sets. A shape in the NestedChildShapes set is confined to the bounding
box of its parent. A shape in the RelativeChildShapes list can appear outside or partly outside the bounds of
the parent - for example a label or a port. A diagram has no RelativeChildShapes and no Parent .
Navigating between shapes and elements
Domain model elements and shape elements are related by the PresentationViewsSubject relationship.
// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
PresentationViewsSubject.GetPresentation(henry)
.FirstOrDefault() as PersonShape;

The same relationship links relationships to connectors on the diagram:

Descendants link = Descendants.GetLink(henry, edward);


DescendantConnector dc =
PresentationViewsSubject.GetPresentation(link)
.FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape

This relationship also links the root of the model to the diagram:

FamilyTreeDiagram diagram =
PresentationViewsSubject.GetPresentation(familyTree)
.FirstOrDefault() as FamilyTreeDiagram;

To get the model element represented by a shape, use:


henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Navigating around the Diagram


In general it is not advisable to navigate between shapes and connectors on the diagram. It is better to
navigate the relationships in the model, moving between the shapes and connectors only when it is necessary
to work on the appearance of the diagram. These methods link connectors to the shapes at each end:
personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Many shapes are composites; they are made up of a parent shape and one or more layers of children. Shapes
that are positioned relative to another shape are said to be its children. When the parent shape moves, the
children move with it.
Relative children can appear outside the bounding box of the parent shape. Nested children appear strictly
inside the bounds of the parent.
To obtain the top set of shapes on a diagram, use:
Diagram.NestedChildShapes

The ancestor classes of shapes and connectors are:


ModelElement
-- PresentationElement
-- ShapeElement
----- NodeShape
------- Diagram
------- YourShape
----- LinkShape
------- BinaryLinkShape
--------- YourConnector
Properties of Shapes and Connectors
In most cases, it is not necessary to make explicit changes to shapes. When you have changed the model
elements, the "fix up" rules update the shapes and connectors. For more information, see Responding to and
Propagating Changes.
However, it is useful to make some explicit changes to shapes in properties that are independent of the model
elements. For example, you could change these properties:
Size - determines the height and width of the shape.
Location - position relative to the parent shape or diagram
StyleSet - the set of pens and brushes used for drawing the shape or connector
Hide - makes the shape invisible
Show - makes the shape visible after a Hide()

Creating an Element and its Shape


When you create an element and link it into the tree of embedding relationships, a shape is automatically
created and associated with it. This is done by the "fixup" rules that execute at the end of the transaction.
However, the shape will appear in an automatically-assigned location, and its shape, color and other features
will have default values. To control how the shape is created, you can use the merge function. You must first
add the elements you want to add into an ElementGroup, and then merge the group into the diagram.
This method:
Sets the name, if you have assigned a property as the element name.
Observes any Element Merge Directives that you specified in the DSL Definition.
This example creates a shape at the mouse position, when the user double-clicks the diagram. In the
DSL Definition for this sample, the FillColor property of ExampleShape has been exposed.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
partial class MyDiagram
{
public override void OnDoubleClick(DiagramPointEventArgs e)
{
base.OnDoubleClick(e);

using (Transaction t = this.Store.TransactionManager


.BeginTransaction("double click"))
{
ExampleElement element = new ExampleElement(this.Store);
ElementGroup group = new ElementGroup(element);

{ // To use a shape of a default size and color, omit this block.


ExampleShape shape = new ExampleShape(this.Partition);
shape.ModelElement = element;
shape.AbsoluteBounds = new RectangleD(0, 0, 1.5, 1.0);
shape.FillColor = System.Drawing.Color.Azure;
group.Add(shape);
}

this.ElementOperations.MergeElementGroupPrototype(
this,
group.CreatePrototype(),
PointD.ToPointF(e.MousePosition));
t.Commit();
}
}
}

If you provide more than one shape, set their relative positions using the AbsoluteBounds .
You can also set the color and other exposed properties of connectors using this method.
Use Transactions
Shapes, connectors and diagrams are subtypes of ModelElement and live in the Store. You must therefore
make changes to them only inside a transaction. For more information, see How to: Use Transactions to
Update the Model.

Document View and Document Data

Store Partitions
When a model is loaded, the accompanying diagram is loaded at the same time. Typically, the model is loaded
into Store.DefaultPartition, and the diagram content is loaded into another Partition. Usually, the content of
each partition is loaded and saved to a separate file.

See Also
ModelElement
Validation in a Domain-Specific Language
Generating Code from a Domain-Specific Language
How to: Use Transactions to Update the Model
Integrating Models by using Visual Studio Modelbus
Responding to and Propagating Changes
How to: Use Transactions to Update the Model
10/18/2017 3 min to read Edit Online

Transactions make sure that changes that were made to the store are treated as a group. Changes that are
grouped can be committed or rolled back as a single unit.
Whenever your program code modifies, adds, or deletes any element in the Store in Visual Studio Visualization
and Modeling SDK, it must do so inside a transaction. There must be an active instance of Transaction associated
with the Store when the change happens. This applies to all model elements, relationships, shapes, diagrams, and
their properties.
The transaction mechanism helps you avoid inconsistent states. If an error occurs during a transaction, all the
changes are rolled back. If the user performs an Undo command, each recent transaction is treated as a single step.
The user cannot undo parts of a recent change, unless you explicitly put them in separate transactions.

Opening a transaction
The most convenient method of managing a transaction is with a using statement enclosed in a try...catch
statement:

Store store; ...


try
{
using (Transaction transaction =
store.TransactionManager.BeginTransaction("update model"))
// Outermost transaction must always have a name.
{
// Make several changes in Store:
Person p = new Person(store);
p.FamilyTreeModel = familyTree;
p.Name = "Edward VI";
// end of changes to Store

transaction.Commit(); // Don't forget this!


} // transaction disposed here
}
catch (Exception ex)
{
// If an exception occurs, the Store will be
// rolled back to its previous state.
}

If an exception that prevents the final Commit() occurs during the changes, the Store will be reset to its previous
state. This helps you make sure that errors do not leave the model in an inconsistent state.
You can make any number of changes inside one transaction. You can open new transactions inside an active
transaction. The nested transactions must commit or roll back before the containing transaction ends. For more
information, see the example for the TransactionDepth property.
To make your changes permanent, you should Commit the transaction before it is disposed. If an exception occurs
that is not caught inside the transaction, the Store will be reset to its state before the changes.

Rolling back a transaction


To ensure that the Store remains in or reverts to its state before the transaction, you can use either of these tactics:
1. Raise an exception that is not caught inside the scope of the transaction.
2. Explicitly roll back the transaction:

this.Store.TransactionManager.CurrentTransaction.Rollback();

Transactions Do Not Affect Non-Store Objects


Transactions only govern the state of the Store. They cannot undo partial changes that have been made to external
items such as files, databases, or objects that you have declared with ordinary types outside the DSL definition.
If an exception might leave such a change inconsistent with the Store, you should deal with that possibility in the
exception handler. One way to make sure that external resources remain synchronized with the Store objects is to
couple each external object to an in-store element by using event handlers. For more information, see Event
Handlers Propagate Changes Outside the Model.

Rules Fire at the End of a Transaction


At the end of a transaction, before the transaction is disposed, the rules attached to elements in the store are fired.
Each rule is a method that is applied to a model element that has changed. For example, there are "fix up" rules
that update the state of a Shape when its model element has changed, and which create a Shape when a model
element is created. There is no specified firing order. A change made by a rule can fire another rule.
You can define your own rules. For more information about rules, see Responding to and Propagating Changes.
Rules do not fire after an undo, a redo, or a rollback command.

Transaction Context
Each transaction has a dictionary in which you can store any information you want:
store.TransactionManager

.CurrentTransaction.TopLevelTransaction

.Context.Add(aKey, aValue);

This is especially useful for transferring information between rules.

Transaction state
In some cases you need to avoid propagating a change if the change is caused by undoing or redoing a
transaction. This can happen, for example, if you write a property value handler that can update another value in
the Store. Because the undo operation resets all the values in the Store to their previous states, it is not necessary
to compute updated values. Use this code:

if (!this.Store.InUndoRedoOrRollback) {...}

Rules can fire when the store is initially being loaded from a file. To avoid responding to these changes, use:

if (!this.Store.InSerializationTransaction) {...}
Integrating Models by using Visual Studio Modelbus
11/7/2017 16 min to read Edit Online

Visual Studio ModelBus provides a method for creating links between models and from other tools into models.
For example, you could link domain-specific language (DSL) models and UML models. You can create an
integrated set of DSLs.
ModelBus lets you create a unique reference to a model or to a specific element inside a model. This reference can
be stored outside the model, for example, in an element in another model. When, on a later occasion, a tool wants
to obtain access to the element, the Model Bus infrastructure will load the appropriate model and return the
element. If you want, you can display the model to the user. If the file cannot be accessed in its previous location,
ModelBus will ask the user to find it. If the user finds the file, ModelBus will fix all the references to that file.

NOTE
In the current Visual Studio implementation of ModelBus, the linked models must be items in the same Visual Studio
solution.

For additional information and sample code, see:


How to: Add a Drag-and-Drop Handler
Modeling SDK for Visual Studio

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Providing Access to a DSL


Before you can create ModelBus references to a model or its elements, you must define a ModelBusAdapter for the
DSL. The easiest way to do this is to use the Visual Studio Model Bus Extension, which adds commands to the DSL
Designer.
To expose a DSL Definition to Model Bus
1. Download and install the Visual Studio Model Bus extension, unless you have already installed it. For more
information, see Visualization and Modeling SDK.
2. Open the DSL definition file. Right-click the design surface and then click Enable Modelbus.
3. In the dialog box, choose I want to expose this DSL to the ModelBus. You can choose both options if you
want this DSL both to expose its models and to consume references to other DSLs.
4. Click OK. A new project "ModelBusAdapter" is added to the DSL solution.
5. If you want to access the DSL from a text template, you must modify AdapterManager.tt in the new project.
Omit this step if you want to access the DSL from other code such as commands and event handlers. For
more information, see Using Visual Studio ModelBus in a Text Template.
a. Change the base class of AdapterManagerBase to VsTextTemplatingModelingAdapterManager.
b. Near the end of the file, insert this additional attribute in front of class AdapterManager:
[Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]
c. In the References of ModelBusAdapter project, add
Microsoft.VisualStudio.TextTemplating.Modeling.11.0.
If you want to access the DSL both from text templates and from other code, you need two adapters,
one modified and one unmodified.
6. Click Transform All Templates.
7. Rebuild the solution.
It is now possible for ModelBus to open instances of this DSL.
The folder ModelBusAdapters\bin\* contains the assemblies built by the Dsl project and the
ModelBusAdapters project. To reference this DSL from another DSL, you should import these assemblies.
Making sure that elements can be referenced
Visual Studio ModelBus adapters use the guid of an element to identify it, by default. These identifiers must
therefore be persisted in the model file.
To e n su r e t h a t e l e m e n t I D s a r e p e r si st e d

1. Open DslDefinition.dsl.
2. In DSL Explorer, expand Xml Serialization Behavior, then Class Data.
3. For each class to which you want to create Model Bus references:
Click the class node, and in the Properties window, make sure that Serialize Id is set to true .
Alternatively, if you want to use element names to identify elements instead of guids, you can override parts
of the generated adapters. Override the following methods in the adapter class:
Override GetElementId to return the identifier you want to use. This method is called when creating
references.
Override ResolveElementReference to locate the correct element from a Model Bus reference.

Accessing a DSL from another DSL


You can store model bus references in a domain property in a DSL, and you can write custom code that uses them.
You can also let the user create a model bus reference by picking a model file and an element within it.
To enable a DSL to use references to another DSL, you should first make it a consumer of model bus references.
To enable a DSL to consume references to an exposed DSL
1. In the DSL Definition diagram, right-click the main part of the diagram and then click Enable Modelbus.
2. In the dialog box, select I want to enable this model to consume model bus references.
3. In the Dsl project of the consuming DSL, add the following assemblies to the project references. You will
find these assemblies (.dll files) in the ModelBusAdapter\bin\* directory of the exposed DSL.
The exposed DSL assembly, for example Fabrikam.FamilyTree.Dsl.dll
The exposed model bus adapter assembly, for example
Fabrikam.FamilyTree.ModelBusAdapter.dll
4. Add the following .NET assemblies to the project references of the consuming DSL project.
a. Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0.dll
b. Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.11.0.dll
To store a Model Bus Reference in a domain property
1. In the DSL Definition of the consuming DSL, add a domain property to a domain class and set its name.
2. In the Properties window, with the domain property selected, set Type to ModelBusReference .
At this stage, program code can set the property value, but it is read-only in the Properties window.
You can allow users to set the property with a specialized ModelBus reference editor. There are two
versions of this editor or picker: one allows users to choose a model file, and the other allows users to
choose a model file and an element within the model.
To allow the user to set a Model Bus Reference in a domain property
1. Right-click the domain property and then click Edit ModelBusReference specific properties. A dialog
box opens. This is the Model Bus Picker.
2. Select the appropriate Kind of ModelBusReference: to a model or to an element inside a model.
3. In file dialog filter string, enter a string such as Family Tree files |*.ftree . Subsitute the file extension of
your exposed DSL.
4. If you chose to reference an element in a model, you can add a list of types that the user can select, for
example Company.FamilyTree.Person.
5. Click OK, and then click Transform All Templates in the solution explorer toolbar.

WARNING
If you have not selected a valid model or entity, the OK button will have no effect, even though it might appear
enabled.

6. If you specified a list of target types such as Company.FamilyTree.Person, then you must add an assembly
reference to your DSL project, referencing the DLL of the target DSL, for example
Company.FamilyTree.Dsl.dll
To test a Model Bus Reference
1. Build both the exposed and consuming DSLs.
2. Run one of the DSLs in experimental mode by pressing F5 or CTRL+F5.
3. In the Debugging project in the experimental instance of Visual Studio, add files that are instances of each
DSL.

NOTE
Visual Studio ModelBus can only resolve references to models that are items in the same Visual Studio solution. For
example, you cannot create a reference to a model file in another part of your file system.

4. Create some elements and links in the instance of the exposed DSL, and save it.
5. Open an instance of the consuming DSL, and select a model element that has a model bus reference
property.
6. In Properties window, double-click the model bus reference property. The picker dialog opens.
7. Click Browse and select the instance of the exposed DSL.
The picker will also let you choose an item in the model, if you specified the element-specific kind of model
bus reference.
Creating References in Program Code
When you want to store a reference to a model or an element inside a model, you create a ModelBusReference .
There are two kinds of ModelBusReference : model references and element references.
To create a model reference, you need the AdapterManager of the DSL of which the model is an instance, and the
filename or Visual Studio project item of the model.
To create an element reference, you need an adapter for the model file, and the element you want to refer to.

NOTE
With the Visual Studio ModelBus, you can create references only to items in the same Visual Studio solution.

Import the exposed DSL assemblies


In the consuming project, add project references to the DSL and ModelBusAdapter assemblies of the exposed DSL.
For example, suppose that you want to store ModelBus References in elements of a MusicLibrary DSL. The
ModelBus References will refer to elements of the FamilyTree DSL. In the Dsl project of the MusicLibrary solution,
in the References node, add references to the following assemblies:
Fabrikam.FamilyTree.Dsl.dll - the exposed DSL.
Fabrikam.FamilyTree.ModelBusAdapters.dll - the ModelBus adapter of the exposed DSL.
Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0
Microsoft.VisualStudio.Modeling.Sdk.Integration.Shell.11.0
These assemblies can be found in the ModelBusAdapters project of the exposed DSL, under bin\* .
In the code file where you will create references, you will typically have to import these namespaces:

// The namespace of the DSL you want to reference:


using Fabrikam.FamilyTree; // Exposed DSL
using Fabrikam.FamilyTree.ModelBusAdapters;
using Microsoft.VisualStudio.Modeling.Integration;
using System.Linq;
...

To create a reference to a model


To create a model reference, you access the AdapterManager for the exposed DSL, and use it to create a reference
to the model. You can specify either a file path, or a EnvDTE.ProjectItem .
From the AdapterManager, you can obtain an Adapter, which provides access to individual elements in the model.

NOTE
You must dispose an Adapter when you have finished with it. The most convenient way to achieve this is with a using
statement. The following example illustrates this.
// The file path of a model instance of the FamilyTree DSL:
string targetModelFile = "TudorFamilyTree.ftree";
// Get the ModelBus service:
IModelBus modelBus =
this.Store.GetService(typeof(SModelBus)) as IModelBus;
// Get an adapterManager for the target DSL:
FamilyTreeAdapterManager manager =
(modelbus.GetAdapterManager(FamilyTreeAdapter.AdapterId)
as FamilyTreeAdapterManager;
// or: (modelBus.FindAdapterManagers(targetModelFile).First())
// or could provide an EnvDTE.ProjectItem

// Create a reference to the target model:


// NOTE: the target model must be a file in this project.
ModelBusReference modelReference =
manager.CreateReference(targetModelFile);
// or can use an EnvDTE.ProjectItem instead of the filename

// Get the root element of this model:


using (FamilyTreeAdapter adapter =
modelBus.CreateAdapter(modelReference) as FamilyTreeAdapter)
{
FamilyTree modelRoot = adapter.ModelRoot;
// Access elements under the root in the usual way:
foreach (Person p in modelRoot.Persons) {...}
// You can create adapters for individual elements:
ModelBusReference elementReference =
adapter.GetElementReference(person);
...
} // Dispose adapter

If you want to be able to use modelReference later, you can store it in a domain property that has the External Type
ModelBusReference :

using Transaction t = this.Store.TransactionManager


.BeginTransaction("keep reference"))
{
artist.FamilyTreeReference = modelReference;
t.Commit();
}

To allow users to edit this domain property, use ModelReferenceEditor as the parameter in the Editor attribute. For
more information, see Allow the User to Edit a Reference.
To create a reference to an element
The adapter that you created for the model can be used to create and resolve references.

// person is an element in the FamilyTree model:


ModelBusReference personReference =
adapter.GetElementReference(person);

If you want to be able to use elementReference later, you can store it in a domain property that has the External
Type ModelBusReference . To allow users to edit it, use ModelElementReferenceEditor as the parameter in the Editor
attribute. For more information, see Allow the User to Edit a Reference.
Resolving references
If you have a ModelBusReference (MBR) you can obtain the model or the model element to which it refers. If the
element is presented on a diagram or other view, you can open the view and select the element.
You can create an adapter from an MBR. From the adapter, you can obtain the root of the model. You can also
resolve MBRs that refer to specific elements within the model.

using Microsoft.VisualStudio.Modeling.Integration; ...


ModelBusReference elementReference = ...;

// Get the ModelBus service:


IModelBus modelBus =
this.Store.GetService(typeof(SModelBus)) as IModelBus;
// Use a model reference or an element reference
// to obtain an adapter for the target model:
using (FamilyTreeAdapter adapter =
modelBus.CreateAdapter(elementReference) as FamilyTreeAdapter)
// or CreateAdapter(modelReference)
{
// Get the root of the model:
FamilyTree tree = adapter.ModelRoot;

// Get a model element:


MyDomainClass mel =
adapter.ResolveElementReference<MyDomainClass>(elementReference);
if (mel != null) {...}

// Get the diagram or other view, if there is one:


ModelBusView view = adapter.GetDefaultView();
if (view != null)
{
view.Open();
// Display the diagram:
view.Show();
// Attempt to select the shape that presents the element:
view.SetSelection(elementReference);
}
} // Dispose the adapter.

To r e so l v e M o d e l B u s R e fe r e n c e s i n a Te x t Te m p l a t e

1. The DSL you want to access must have a ModelBus Adapter that has been configured for access by text
templates. For more information, see Providing Access to a DSL.
2. Typically, you will be accessing a target DSL using a Model Bus Reference (MBR) stored in a source DSL.
Your template therefore includes the directive of the source DSL, plus code to resolve the MBR. For more
information about text templates, see Generating Code from a Domain-Specific Language.
<#@ template debug="true" hostspecific="true"
inherits="Microsoft.VisualStudio.TextTemplating.Modeling.ModelBusEnabledTextTransformation" #>
<#@ SourceDsl processor="SourceDslDirectiveProcessor" requires="fileName='Sample.source'" #>
<#@ output extension=".txt" #>
<#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0" #>
<#@ assembly name = "System.Core" #>
<#@ assembly name = "Company.CompartmentDragDrop.Dsl.dll" #>
<#@ assembly name = "Company.CompartmentDragDrop.ModelBusAdapter.dll" #>
<#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Company.CompartmentDragDrop" #>
<#@ import namespace="Company.CompartmentDragDrop.ModelBusAdapters" #>
<# // Get source root from directive processor:
ExampleModel source = this.ExampleModel;
// This DSL has a MBR in its root:
using (ModelBusAdapter adapter = this.ModelBus.CreateAdapter(source.ModelReference) as ModelBusAdapter)
{
ModelBusAdapterManager manager =
this.ModelBus.FindAdapterManagers(this.Host.ResolvePath("Sample.compDD1")).FirstOrDefault();
ModelBusReference modelReference =
manager.CreateReference(this.Host.ResolvePath("Sample.compDD1"));

// Get the root element of this model:


using (CompartmentDragDropAdapter adapter =
this.ModelBus.CreateAdapter(modelReference) as CompartmentDragDropAdapter)
{
ModelRoot root = adapter.ModelRoot;
#>
[[<#= root.Name #>]]
<#
}
#>

For more information and a walkthrough, see Using Visual Studio ModelBus in a Text Template

Serializing a ModelBusReference
If you want to store a ModelBusReference (MBR) in the form of a string, you can serialize it:

string serialized = modelBus.SerializeReference(elementReference);


// Store it anywhere, then get it back again:
ModelBusReference elementReferenceRestored =
modelBus.DeserializeReference(serialized, null);

A MBR that is serialized in this manner is independent of context. If you are using the simple file-based Model Bus
Adapter, the MBR contains an absolute file path. This is sufficient if the instance model files will never move.
However, the model files will typically be items in a Visual Studio project. Your users will expect to be able to move
the whole project to different parts of the file system. They will also expect to be able to keep the project under
source control and open it on different computers. Path names should therefore be serialized relative to the
location of the project that contains the files.
Serializing Relative to a Specified File Path
A ModelBusReference contains a ReferenceContext , which is a dictionary in which you can store information such
as the file path relative to which it should be serialized.
To serialize relative to a path:
elementReference.ReferenceContext.Add(
ModelBusReferencePropertySerializer.FilePathSaveContextKey,
currentProjectFilePath);
string serialized = modelBus.SerializeReference(elementReference);

To retrieve the reference from the string:

ReferenceContext context = new ReferenceContext();


context.Add(ModelBusReferencePropertySerializer.FilePathLoadContextKey,
currentProjectFilePath);
ModelBusReference elementReferenceRestored =
modelBus.DeserializeReference(serialized, context);

ModelBusReferences created by other Adapters


The following information is useful if you want to create your own adapter.
A ModelBusReference (MBR) consists of two parts: the MBR header, which is deserialized by the model bus, and an
adapter-specific that is handled by the specific adapter manager. This lets you provide your own adapter
serialization format. For example, you could reference a database rather than a file, or you could store additional
information in the adapter reference. Your own adapter can place additional information in the ReferenceContext .
When you deserialize an MBR, you must provide a ReferenceContext, which is then stored in the MBR object.
When you serialize an MBR, the stored ReferenceContext is used by the adapter to help generate the string. The
deserialized string does not contain all the information in the ReferenceContext. For example, in the simple file-
based adapter, the ReferenceContext contains a root file path, which is not stored in the serialized MBR string.
The MBR is deserialized in two stages:
ModelBusReferencePropertySerializer is the standard serializer that deals with the MBR header. It uses the
standard DSL SerializationContext property bag, which is stored in the ReferenceContext using the key
ModelBusReferencePropertySerializer.ModelBusLoadContextKey . In particular, the SerializationContext should
contain an instance of ModelBus .
Your ModelBus Adapter deals with the adapter-specific part of the MBR. It can use additional information
stored in the ReferenceContext of the MBR. The simple file-based adapter keeps root file paths using the
keys FilePathLoadContextKey and FilePathSaveContextKey .
An adapter reference in a model file is deserialized only when it is used.

To Create a Model
Creating, opening and editing a model
The following fragment is taken from the State Machine sample on the VMSDK Web site. It illustrates the use of
ModelBusReferences to create and open a model, and to obtain the diagram associated with the model.
In this sample, the name of the target DSL is StateMachine. Several names are derived from it, such as the name of
the model class and the name of the ModelBusAdapter.
using Fabrikam.StateMachine.ModelBusAdapters;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Integration;
using Microsoft.VisualStudio.Modeling.Integration.Shell;
using Microsoft.VisualStudio.Modeling.Shell;
...
// Create a new model.
ModelBusReference modelReference =
StateMachineAdapterManager .CreateStateMachineModel(modelName, fileName);
//Keep reference of new model in this model.
using (Transaction t = ...)
{
myModelElement.ReferenceProperty = modelReference;
t.Commit();
}
// Get the ModelBus service from Visual Studio.
IModelBus modelBus = Microsoft.VisualStudio.Shell.Package.
GetGlobalService(typeof(SModelBus)) as IModelBus;
// Get a modelbus adapter on the new model.
ModelBusAdapter modelBusAdapter;
modelBus.TryCreateAdapter(modelReference,
this.ServiceProvider, out modelBusAdapter);
using (StateMachineAdapter adapter =
modelBusAdapter as StateMachineAdapter)
{
if (adapter != null)
{
// Obtain a Diagram from the adapter.
Diagram targetDiagram =
((StandardVsModelingDiagramView)
adapter.GetDefaultView()
).Diagram;

using (Transaction t =
targetDiagram.Store.TransactionManager
.BeginTransaction("Update diagram"))
{
DoUpdates(targetDiagram);
t.Commit();
}

// Display the new diagram.


adapter.GetDefaultView().Show();
}
}

Validating references
The BrokenReferenceDetector tests all the domain properties in a Store that can hold ModelBusReferences. It calls
the action you that provide where any action is found. This is particularly useful for validation methods. The
following validation method tests the store on an attempt to save the model, and reports broken references in the
errors window:
[ValidationMethod(ValidationCategories.Save)]
public void ValidateModelBusReferences(ValidationContext context)
{
BrokenReferenceDetector.DetectBrokenReferences(this.Store,
delegate(ModelElement element, // parent of property
DomainPropertyInfo property, // identifies property
ModelBusReference reference) // invalid reference
{
context.LogError(string.Format(INVALID_REF_FORMAT,
property.Name,
referenceState.Name,
new ModelBusReferenceTypeConverter().
ConvertToInvariantString(reference)),
"Reference",
element);
});
}}
private const string INVALID_REF_FORMAT =
"The '{0}' domain property of ths ReferenceState instance "
+ "named '{1}' contains reference value '{2}' which is invalid";

Actions performed by the ModelBus Extension


The following information is not essential, but might be useful if you make extensive use of ModelBus.
The ModelBus Extension makes the following changes in your DSL solution.
When you right-click the DSL Definition diagram, click Enable Modelbus, and then select Enable this DSL to
Consume the ModelBus:
In the DSL project, a reference is added to Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0.dll
In the DSL Definition, an External Type reference is added:
Microsoft.VisualStudio.Modeling.Integration.ModelBusReference .
You can see the reference in DSL Explorer, under Domain Types. To add external type references
manually, right-click the root node.
A new template file is added, Dsl\GeneratedCode\ModelBusReferencesSerialization.tt.
When you set the type of a domain property to ModelBusReference, and then right-click the property and
click Enable ModelBusReference specific properties:
Several CLR attributes are added to the domain property. You can see them in the Custom Attributes field in
the Properties window. In Dsl\GeneratedCode\DomainClasses.cs, you can see the attributes on the
property declaration:

[System.ComponentModel.TypeConverter(typeof(
Microsoft.VisualStudio.Modeling.Integration.ModelBusReferenceTypeConverter))]
[System.ComponentModel.Editor(typeof(
Microsoft.VisualStudio.Modeling.Integration.Picker
.ModelReferenceEditor // or ModelElementReferenceEditor
), typeof(System.Drawing.Design.UITypeEditor))]
[Microsoft.VisualStudio.Modeling.Integration.Picker
.SupplyFileBasedBrowserConfiguration
("Choose a model file", "Target model|*.target")]

When you right click the DSL Definition Diagram, click Enable ModelBus, and select Expose this DSL to
the ModelBus:
A new project ModelBusAdapter is added to the solution.
A reference to ModelBusAdapter is added to the DslPackage project. ModelBusAdapter has a reference to the
Dsl project.

In DslPackage\source.extention.tt, |ModelBusAdapter| is added as a MEF Component.

See Also
How to: Open a Model from File in Program Code
How to: Add a Drag-and-Drop Handler
Using Visual Studio ModelBus in a Text Template
Extend your DSL by using MEF
10/18/2017 8 min to read Edit Online

You can extend your domain-specific language (DSL) by using Managed Extensibility Framework (MEF). You or
other developers will be able to write extensions for the DSL without changing the DSL definition and program
code. Such extensions include menu commands, drag-and-drop handlers, and validation. Users will be able to
install your DSL, and then optionally install extensions for it.
In addition, when you enable MEF in your DSL, it can be easier for you to write some of the features of your DSL,
even if they are all built together with the DSL.
For more information about MEF, see Managed Extensibility Framework (MEF).
To enable your DSL to be extended by MEF
1. Create a new folder named MefExtension inside the DslPackage project. Add the following files to it:
File name: CommandExtensionVSCT.tt

IMPORTANT
Set the GUID in this file to be the same as the GUID CommandSetId that is defined in
DslPackage\GeneratedCode\Constants.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#
// CmdSet Guid must be defined before master template is included
// This Guid must be kept synchronized with the CommandSetId Guid in Constants.tt
Guid guidCmdSet = new Guid ("00000000-0000-0000-0000-000000000000");
string menuidCommandsExtensionBaseId="0x4000";
#>
<#@ include file="DslPackage\CommandExtensionVSCT.tt" #>

File name: CommandExtensionRegistrar.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="DslPackage\CommandExtensionRegistrar.tt" #>

File name: ValidationExtensionEnablement.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="DslPackage\ValidationExtensionEnablement.tt" #>

File name: ValidationExtensionRegistrar.tt

If you add this file, you must enable validation in your DSL by using at least one of the switches in
EditorValidation in DSL Explorer.

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="DslPackage\ValidationExtensionRegistrar.tt" #>
File name: PackageExtensionEnablement.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="DslPackage\PackageExtensionEnablement.tt" #>

2. Create a new folder named MefExtension inside the Dsl project. Add the following files to it:
File name: DesignerExtensionMetaDataAttribute.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="Dsl\DesignerExtensionMetadataAttribute.tt" #>

File name: GestureExtensionEnablement.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="Dsl\GestureExtensionEnablement.tt" #>

File name: GestureExtensionController.tt

<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>


<#@ include file="Dsl\GestureExtensionController.tt" #>

3. Add the following line to the existing file that is named DslPackage\Commands.vsct:

<Include href="MefExtension\CommandExtensionVSCT.vsct"/>

Insert the line after the existing <Include> directive.


4. Open DslDefinition.dsl.

5. In DSL Explorer, select Editor\Validation.


6. In the Properties window, make sure that at least one of the properties named Uses... is true .
7. In the Solution Explorer toolbar, click Transform All Templates.
Subsidiary files appear underneath each of the files that you added.
8. Build and run the solution to verify that it is still working.
Your DSL is now MEF-enabled. You can write menu commands, gestures handlers, and validation
constraints as MEF extensions. You can write these extensions in your DSL solution together with other
custom code. In addition, you or other developers can write separate Visual Studio Extensions that extend
your DSL.

Creating an extension for a MEF-enabled DSL


If you have access to a MEF-enabled DSL created by yourself or someone else, you can write extensions for it. The
extensions can be used to add menu commands, gestures handlers, or validation constraints. To author these
extensions, you use a Visual Studio Extension (VSIX) solution. The solution has two parts: a class library project that
builds the code assembly, and a VSIX project that packages the assembly.
To create a DSL extension VSIX
1. Create a new class library project. To do this, in the New Project dialog box, select Visual Basic or Visual
C# and then select Class Library.
2. In the new class library project, add a reference to the assembly of the DSL.
This assembly usually has a name that ends with ".Dsl.dll".
If you have access to the DSL project, you can find the assembly file under the directory Dsl\bin\\*
If you have access to the DSL VSIX file, you can find the assembly by changing the file name
extension of the VSIX file to ".zip". Decompress the .zip file.
3. Add references to the following .NET assemblies:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0.dll
Microsoft.VisualStudio.Modeling.Sdk.Shell.11.0.dll
System.ComponentModel.Composition.dll
System.Windows.Forms.dll
4. Create a VSIX project in the same solution. To do this, in the New Project dialog box, expand Visual Basic
or Visual C#, click Extensibility, and then select VSIX project.
5. In Solution Explorer, right-click the VSIX project and then click Set as StartUp Project.
6. In the new project, open source.extension.vsixmanifest.
7. Click Add Content. In the dialog box, set Content Type to MEF Component, and Source Project to your
class library project.
8. Add a VSIX reference to the DSL.
a. In source.extension.vsixmanifest, click Add Reference
b. In the dialog box, click Add Payload and then locate the VSIX file of the DSL. The VSIX file is built in
the DSL solution, in DslPackage\bin\\*.
This lets users install the DSL and your extension at the same time. If the user has already installed
the DSL, only your extension will be installed.
9. Review and update the other fields of source.extension.vsixmanifest. Click Select Editions and verify
that the correct Visual Studio editions are set.
10. Add code to the class library project. Use the examples in the next section as a guide.
You can add any number of command, gesture, and validation classes.
11. To test the extension, press F5. In the experimental instance of Visual Studio, create or open an example file
of the DSL.

Writing MEF extensions for DSLs


You can write extensions in the assembly code project of a separate DSL extension solution. You can also use MEF
in your DslPackage project, as a convenient way to write commands, gestures, and validation code as part of the
DSL.
Menu Commands
To write a menu command, define a class that implements ICommandExtension and prefix the class with the
attribute that is defined in your DSL, named YourDsl CommandExtension . You can write more than one menu
command class.
QueryStatus() is called whenever the user right-clicks the diagram. It should inspect the current selection and set
command.Enabled to indicate when the command is applicable.

using System.ComponentModel.Composition;
using System.Linq;
using Company.MyDsl; // My DSL
using Company.MyDsl.ExtensionEnablement; // My DSL
using Microsoft.VisualStudio.Modeling; // Transactions
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement; // IVsSelectionContext
using Microsoft.VisualStudio.Modeling.ExtensionEnablement; // ICommandExtension

namespace MyMefExtension
{
// Defined in Dsl\MefExtension\DesignerExtensionMetaDataAttribute.cs:
[MyDslCommandExtension]
public class MyCommandClass : ICommandExtension
{
/// <summary>
/// Provides access to current document and selection.
/// </summary>
[Import]
IVsSelectionContext SelectionContext { get; set; }

/// <summary>
/// Called when the user selects this command.
/// </summary>
/// <param name="command"></param>
public void Execute(IMenuCommand command)
{
// Transaction is required if you want to update elements.
using (Transaction t = SelectionContext.CurrentStore
.TransactionManager.BeginTransaction("fix names"))
{
foreach (ExampleShape shape in SelectionContext.CurrentSelection)
{
ExampleElement element = shape.ModelElement as ExampleElement;
element.Name = element.Name + " !";
}
t.Commit();
}
}

/// <summary>
/// Called when the user right-clicks the diagram.
/// Determines whether the command should appear.
/// This method should set command.Enabled and command.Visible.
/// </summary>
/// <param name="command"></param>
public void QueryStatus(IMenuCommand command)
{
command.Enabled =
command.Visible = (SelectionContext.CurrentSelection.OfType<ExampleShape>().Count() > 0);
}

/// <summary>
/// Called when the user right-clicks the diagram.
/// Determines the text of the command in the menu.
/// </summary>
public string Text
{
get { return "My menu command"; }
}
}
}

Gesture Handlers
A gesture handler can deal with objects dragged onto the diagram from anywhere, inside or outside Visual Studio.
The following example lets the user drag files from Windows Explorer onto the diagram. It creates elements that
contain the file names.
You can write handlers to deal with drags from other DSL models and UML models. For more information, see
How to: Add a Drag-and-Drop Handler.

using System.ComponentModel.Composition;
using System.Linq;
using Company.MyDsl;
using Company.MyDsl.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling; // Transactions
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;

namespace MefExtension
{
[MyDslGestureExtension]
class MyGestureExtension : IGestureExtension
{
public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
{
System.Windows.Forms.MessageBox.Show("double click!");
}

/// <summary>
/// Called when the user drags anything over the diagram.
/// Return true if the dragged object can be dropped on the current target.
/// </summary>
/// <param name="targetMergeElement">The shape or diagram that the mouse is currently over</param>
/// <param name="diagramDragEventArgs">Data about the dragged element.</param>
/// <returns></returns>
public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs)
{
// This handler only allows items to be dropped onto the diagram:
return targetMergeElement is MefDsl2Diagram &&
// And only accepts files dragged from Windows Explorer:
diagramDragEventArgs.Data.GetFormats().Contains("FileNameW");
}

/// <summary>
/// Called when the user drops an item onto the diagram.
/// </summary>
/// <param name="targetDropElement"></param>
/// <param name="diagramDragEventArgs"></param>
public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
{
MefDsl2Diagram diagram = targetDropElement as MefDsl2Diagram;
if (diagram == null) return;

// This handler only accepts files dragged from Windows Explorer:


string[] draggedFileNames = diagramDragEventArgs.Data.GetData("FileNameW") as string[];
if (draggedFileNames == null || draggedFileNames.Length == 0) return;

using (Transaction t = diagram.Store.TransactionManager.BeginTransaction("file names"))


{
// Create an element to represent each file:
foreach (string fileName in draggedFileNames)
{
ExampleElement element = new ExampleElement(diagram.ModelElement.Partition);
element.Name = fileName;

// This method of adding the new element allows the position


// of the shape to be specified:
ElementGroup group = new ElementGroup(element);
ElementGroup group = new ElementGroup(element);
diagram.ElementOperations.MergeElementGroupPrototype(
diagram, group.CreatePrototype(), PointD.ToPointF(diagramDragEventArgs.MousePosition));
}
t.Commit();
}
}
}
}

Validation constraints
Validation methods are marked by the ValidationExtension attribute that is generated by the DSL, and also by
ValidationMethodAttribute. The method can appear in any class that is not marked by an attribute.
For more information, see Validation in a Domain-Specific Language.

using Company.MyDsl;
using Company.MyDsl.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.Validation;

namespace MefExtension
{
class MyValidationExtension // Can be any class.
{
// SAMPLE VALIDATION METHOD.
// All validation methods have the following attributes.

// Specific to the extended DSL:


[MyDslValidationExtension]

// Determines when validation is applied:


[ValidationMethod(
ValidationCategories.Save
| ValidationCategories.Open
| ValidationCategories.Menu)]

/// <summary>
/// When validation is executed, this method is invoked
/// for every element in the model that is an instance
/// of the second parameter type.
/// </summary>
/// <param name="context">For reporting errors</param>
/// <param name="elementToValidate"></param>
private void ValidateClassNames
(ValidationContext context,
// Type determines to what elements this will be applied:
ExampleElement elementToValidate)
{
// Write code here to test values and links.
if (elementToValidate.Name.IndexOf(' ') >= 0)
{
// Log any unacceptable values:
context.LogError(
// Description:
"Name must not contain spaces"
// Error code unique to this type of error:
, "MyDsl001"
// Element to highlight when user double-clicks error:
, elementToValidate);
} } } }

See Also
Shipping Visual Studio Extensions
Managed Extensibility Framework (MEF)
How to: Add a Drag-and-Drop Handler
Validation in a Domain-Specific Language
How to: Open a Model from File in Program Code
10/18/2017 2 min to read Edit Online

You can open DSL models in any application.


From a Visual Studio extension, you can use ModelBus for this purpose. ModelBus provides standard mechanism
for referencing a model or elements in a model, and for finding the model if it has moved. For more information,
see Integrating Models by using Visual Studio Modelbus.

Target Framework
Set the Target framework of your application project to .NET Framework 4.
To set the Target framework
1. Open the Visual Studio project for the application in which you want to read a DSL model.
2. In Solution Explorer, right-click the project and then click Properties.
3. In the project properties window, on the Application tab, set the Target framework field to .NET
Framework 4.

NOTE
You might need to do this even if you selected .NET Framework 4 in the project creation dialog box. The target framework
should not be .NET Framework 4 Client Profile.

References
You have to add these references to your Visual Studio application project:
Microsoft.VisualStudio.Modeling.Sdk.11.0

If you do not see this under the .NET tab in the Add References dialog box, click the Browse tab and
navigate to
%Program Files%\Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\ .
Your DSL assembly, which you will find under the bin folder your DSL project. Its name is typically of the
form: YourCompany.YourProject .Dsl.dll .

Important Classes in the DSL


Before you can write the code that reads your DSL, you should know the names of some of the classes generated
by your DSL. In your DSL solution, open the Dsl project and look in the GeneratedCode folder. Alternatively,
double-click the DSL assembly in your project References, and open the DSL namespace in Object Browser.
These are the classes that you should identify:
YourDslRootClass - This is the name of the root class in your DslDefinition.dsl .
YourDslName SerializationHelper - This class is defined in SerializationHelper.cs in your DSL project.
YourDslName DomainModel - This class is defined in DomainModel.cs in your DSL project.
Reading from a File
The following example is designed to read a DSL in which the important classes are as follows:
FamilyTreeModel
FamilyTreeSerializationHelper
FamilyTreeDomainModel
The other domain class in this DSL is Person.

using System;
using Microsoft.VisualStudio.Modeling;
using Company.FamilyTree; // Your DSL namespace

namespace StandaloneReadDslConsole
{ class Program
{ static void Main(string[] args)
{
// The path of a DSL model file:
string dslModel = @"C:\FamilyTrees\Tudor.ftree";
// Set up the Store to read your type of model:
Store store = new Store(
typeof(Company.FamilyTree.FamilyTreeDomainModel));
// The Model type generated by the DSL:
FamilyTreeModel familyTree;
// All Store changes must be in a Transaction:
using (Transaction t =
store.TransactionManager.BeginTransaction("Load model"))
{
familyTree =
FamilyTreeSerializationHelper.Instance.
LoadModel(store, dslModel, null, null, null);
t.Commit(); // Don't forget this!
}
// Now we can read the model:
foreach (Person p in familyTree.People)
{
Console.WriteLine(p.Name);
foreach (Person child in p.Children)
{
Console.WriteLine(" " + child.Name);
}
} } } }

Saving to a File
The following addition to the previous code makes a change to the model and then saves it to a file.
using (Transaction t =
store.TransactionManager.BeginTransaction("update model"))
{
// Create a new model element:
Person p = new Person(store);
// Set its embedding relationship:
p.FamilyTreeModel = familyTree;
// - same as: familyTree.People.Add(p);
// Set its properties:
p.Name = "Edward VI";
t.Commit(); // Don't forget this!
}
// Save the model:
try
{
SerializationResult result = new SerializationResult();
FamilyTreeSerializationHelper.Instance
.SaveModel(result, familyTree, @"C:\FamilyTrees\Tudor-upd.ftree");
// Report any error:
if (result.Failed)
{
foreach (SerializationMessage message in result)
{
Console.WriteLine(message);
}
}
}
catch (System.IO.IOException ex)
{ ... }
How to: Set CLR Attributes on an Element
10/18/2017 1 min to read Edit Online

Custom attributes are special attributes that can be added to domain elements, shapes, connectors, and diagrams.
You can add any attribute that inherits from the System.Attribute class.
To add a custom attribute
1. In the DSL Explorer, select the element to which you want to add a custom attribute.
2. In the Properties window, next to the Custom Attributes property, click the Browse (...) icon.
The Edit Attributes dialog box opens.
3. In the Name column, click <add attribute> and type the name of your attribute. Press ENTER.
4. The line under the attribute name shows parentheses. On this line type a parameter type for the attribute (for
example, string ), and then press ENTER.
5. In the Name Property column, type an appropriate name, for example, MyString .
6. Click OK.
The Custom Attributes property now displays the attribute in the following format:
[ AttributeName ( ParameterName = Type )]

See Also
Domain-Specific Language Tools Glossary
Defining a Locking Policy to Create Read-Only
Segments
10/18/2017 6 min to read Edit Online

The Immutability API of the Visual Studio Visualization and Modeling SDK allows a program to lock part or all of a
domain-specific language (DSL) model so that it can be read but not changed. This read-only option could be used,
for example, so that a user can ask colleagues to annotate and review a DSL model but can disallow them from
changing the original.
In addition, as author of a DSL, you can define a locking policy. A locking policy defines which locks are permitted,
not permitted, or mandatory. For example, when you publish a DSL, you can encourage third-party developers to
extend it with new commands. But you could also use a locking policy to prevent them from altering the read-only
status of specified parts of the model.

NOTE
A locking policy can be circumvented by using reflection. It provides a clear boundary for third-party developers, but does
not provide strong security.

More information and samples are available at the Visual Studio Visualization and Modeling SDK Web site.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Setting and Getting Locks


You can set locks on the store, on a partition, or on an individual element. For example, this statement will prevent
a model element from being deleted, and will also prevent its properties from being changed:

using Microsoft.VisualStudio.Modeling.Immutability; ...


element.SetLocks(Locks.Delete | Locks.Property);

Other lock values can be used to prevent changes in relationships, element creation, movement between partitions,
and re-ordering links in a role.
The locks apply both to user actions and to program code. If program code attempts to make a change, an
InvalidOperationException will be thrown. Locks are ignored in an Undo or Redo operation.

You can discover whether an element has a any lock in a given set by using IsLocked(Locks) and you can obtain
the current set of locks on an element by using GetLocks() .
You can set a lock without using a transaction. The lock database is not part of the store. If you set a lock in
response to a change of a value in the store, for example in OnValueChanged, you should allow changes that are
part of an Undo operation.
These methods are extension methods that are defined in the Microsoft.VisualStudio.Modeling.Immutability
namespace.
Locks on partitions and stores
Locks can also be applied to partitions and the store. A lock that is set on a partition applies to all the elements in
the partition. Therefore, for example, the following statement will prevent all the elements in a partition from being
deleted, irrespective of the states of their own locks. Nevertheless, other locks such as Locks.Property could still be
set on individual elements:

partition.SetLocks(Locks.Delete);

A lock that is set on the Store applies to all its elements, irrespective of the settings of that lock on the partitions
and the elements.
Using Locks
You could use locks to implement schemes such as the following examples:
Disallow changes to all elements and relationships except those that represent comments. This allows users
to annotate a model without changing it.
Disallow changes in the default partition, but allow changes in the diagram partition. The user can rearrange
the diagram, but cannot alter the underlying model.
Disallow changes to the Store except for a group of users who are registered in a separate database. For
other users, the diagram and model are read-only.
Disallow changes to the model if a Boolean property of the diagram is set to true. Provide a menu command
to change that property. This helps ensure users that they do not make changes accidentally.
Disallow addition and deletion of elements and relationships of particular classes, but allow property
changes. This provides users with a fixed form in which they can fill the properties.

Lock values
Locks can be set on a Store, Partition, or individual ModelElement. Locks is a Flags enumeration: you can combine
its values using '|'.
Locks of a ModelElement always include the Locks of its Partition.
Locks of a Partition always include the Locks of the Store.
You cannot set a lock on a partition or store and at the same time disable the lock on an individual element.

VALUE MEANING IF ISLOCKED(VALUE) IS TRUE

None No restriction.

Property Domain properties of elements cannot be changed. This does


not apply to properties that are generated by the role of a
domain class in a relationship.

Add New elements and links cannot be created in a partition or


store.

Not applicable to ModelElement .

Move Element cannot be moved between partitions if


element.IsLocked(Move) is true, or if
targetPartition.IsLocked(Move) is true.
VALUE MEANING IF ISLOCKED(VALUE) IS TRUE

Delete An element cannot be deleted if this lock is set on the element


itself, or on any of the elements to which deletion would
propagate, such as embedded elements and shapes.

You can use element.CanDelete() to discover whether an


element can be deleted.

Reorder The ordering of links at a roleplayer cannot be changed.

RolePlayer The set of links that are sourced at this element cannot be
changed. For example, new elements cannot be embedded
under this element. This does not affect links for which this
element is the target.

If this element is a link, its source and target are not affected.

All Bitwise OR of the other values.

Locking Policies
As the author of a DSL, you can define a locking policy. A locking policy moderates the operation of SetLocks(), so
that you can prevent specific locks from being set or mandate that specific locks must be set. Typically, you would
use a locking policy to discourage users or developers from accidentally contravening the intended use of a DSL, in
the same manner that you can declare a variable private .
You can also use a locking policy to set locks on all elements dependent on the element's type. This is because
SetLocks(Locks.None) is always called when an element is first created or deserialized from file.

However, you cannot use a policy to vary the locks on an element during its life. To achieve that effect, you should
use calls to SetLocks() .
To define a locking policy, you have to:
Create a class that implements ILockingPolicy.
Add this class to the services that are available through the DocData of your DSL.
To define a locking policy
ILockingPolicy has the following definition:

public interface ILockingPolicy


{
Locks RefineLocks(ModelElement element, Locks proposedLocks);
Locks RefineLocks(Partition partition, Locks proposedLocks);
Locks RefineLocks(Store store, Locks proposedLocks);
}

These methods are called when a call is made to SetLocks() on a Store, Partition, or ModelElement. In each
method, you are provided with a proposed set of locks. You can return the proposed set, or you can add and
subtract locks.
For example:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Immutability;
namespace Company.YourDsl.DslPackage // Change
{
public class MyLockingPolicy : ILockingPolicy
{
/// <summary>
/// Moderate SetLocks(this ModelElement target, Locks locks)
/// </summary>
/// <param name="element">target</param>
/// <param name="proposedLocks">locks</param>
/// <returns></returns>
public Locks RefineLocks(ModelElement element, Locks proposedLocks)
{
// In my policy, users can never delete an element,
// and other developers cannot easily change that:
return proposedLocks | Locks.Delete);
}
public Locks RefineLocks(Store store, Locks proposedLocks)
{
// Only one user can change this model:
return Environment.UserName == "aUser"
? proposedLocks : Locks.All;
}

To make sure that users can always delete elements, even if other code calls SetLocks(Lock.Delete):

return proposedLocks & (Locks.All ^ Locks.Delete);

To disallow change in all the properties of every element of MyClass:


return element is MyClass ? (proposedLocks | Locks.Property) : proposedLocks;

To make your policy available as a service


In your DslPackage project, add a new file that contains code that resembles the following example:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Immutability;
namespace Company.YourDsl.DslPackage // Change
{
// Override the DocData GetService() for this DSL.
internal partial class YourDslDocData // Change
{
/// <summary>
/// Custom locking policy cache.
/// </summary>
private ILockingPolicy myLockingPolicy = null;

/// <summary>
/// Called when a service is requested.
/// </summary>
/// <param name="serviceType">Service requested</param>
/// <returns>Service implementation</returns>
public override object GetService(System.Type serviceType)
{
if (serviceType == typeof(SLockingPolicy)
|| serviceType == typeof(ILockingPolicy))
{
if (myLockingPolicy == null)
{
myLockingPolicy = new MyLockingPolicy();
}
return myLockingPolicy;
}
// Request is for some other service.
return base.GetService(serviceType);
}
}
Adding Extensions to DSL Definitions
10/18/2017 1 min to read Edit Online

DSL Definition extension allows you to create a package of extensions to a domain-specific language (DSL). The DSL
extension, which is contained in a Visual Studio Integration Extension (VSIX), can be installed on a user's computer
in the same manner as a DSL. The additional features can be dynamically enabled and disabled at run time. DSLs do
not have to be explicitly designed for extension, and extensions can be designed later or by third parties without
altering the extended DSL.
The additional features can include the following:
Properties for model and presentation elements
Decorators for shapes and connectors
Classes, relationships, shapes and connectors
Validation constraints
Toolbox items and tabs
A user of an extended DSL can create and save a model that contains instances of the additional features, and
these can be read by other users who have installed the appropriate extension. Users who have not installed
the extension cannot use the additional features, but they can update and save a model without losing the
additional features.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

See Also
Related blog posts
Adding a Tracking Property to a Domain-Specific
Language Definition
10/18/2017 17 min to read Edit Online

This walkthrough shows how to add a tracking property to a domain model.


A tracking domain property is a property that can be updated by the user but which has a default value that is
calculated by using the values of other domain properties or elements.
For example, in the Domain-Specific Language Tools (DSL Tools), the Display Name property of a domain class has
a default value that is calculated by using the name of the domain class, but a user can change the value at design
time or reset it to the calculated value.
In this walkthrough, you create a domain-specific language (DSL) that has a Namespace tracking property that has
a default value based on the Default Namespace property of the model. For more information about tracking
properties, see Defining Tracking Properties.
The DSL Tools support tracking property descriptors. However, the DSL designer cannot be used to add a
tracking property to a language. Therefore, you must add custom code to define and implement the tracking
property.
A tracking property has two states: tracking, and updated by the user. Tracking properties have the following
features:
When in the tracking state, the value of the tracking property is calculated, and the value is updated as other
properties in the model change.
When in the updated by user state, the value of the tracking property retains the value to which the user last
set the property.
In the Properties window, the Reset command for the tracking property is only enabled when the property
is in the updated by user state. The Reset command sets the tracking property state to tracking.
In the Properties window, when the tracking property is in the tracking state, its value is displayed in a
regular font.
In the Properties window, when the tracking property is in the updated by user state, its value is displayed in
a bold font.

Prerequisites
Before you can start this walkthrough, you must first install these components:

Visual Studio http://go.microsoft.com/fwlink/?LinkID=185579

Visual Studio SDK http://go.microsoft.com/fwlink/?LinkID=185580

Domain-Specific Language Tools http://go.microsoft.com/fwlink/?LinkID=185581

Creating the DSL Project


Create the project for your domain-specific language.
To create the project
1. Create a Domain-Specific Language Designer project. Name it TrackingPropertyDSL .
2. In the Domain-Specific Language Designer Wizard, set the following options:
a. Select the MinimalLanguage template.
b. Use the default name for the domain-specific language, TrackingPropertyDSL .
c. Set the extension for model files to trackingPropertyDsl .
d. Use the default template icon for the model files.
e. Set the name of the product to Product Name .
f. Set the name of the company to Company Name .
g. Use the default value for the root namespace for projects in the solution,
CompanyName.ProductName.TrackingPropertyDSL .

h. Allow the wizard to create a strong name key file for your assemblies.
i. Review the details of the solution, and then click Finish to create the DSL definition project.

Customizing the Default DSL Definition


In this section, you customize the DSL definition to contain the following items:
A Namespace tracking property for every element of the model.
A Boolean IsNamespaceTracking property for every element of the model. This property will indicate
whether the tracking property is in the tracking state or in the updated by user state.
A Default Namespace property for the model. This property will be used to calculate the default value of the
Namespace tracking property.
A CustomElements calculated property for the model. This property will indicate the proportion of elements
that have a custom namespace.
To add the domain properties
1. In the DSL designer, right-click the ExampleModel domain class, point to Add, and then click
DomainProperty.
a. Name the new property DefaultNamespace .
b. In the Properties window for the new property, set Default Value to DefaultNamespace , and set
Type to String.
2. To the ExampleModel domain class, add a domain property named CustomElements .
In the Properties window for the new property, set Kind to Calculated.
3. To the ExampleElement domain class, add a domain property named Namespace .
In the Properties window for the new property, set Is Browsable to False, and set Kind to CustomStorage.
4. To the ExampleElement domain class, add a domain property named IsNamespaceTracking .
In the Properties window for the new property, set Is Browsable to False, set Default Value to true , and
set Type to Boolean.
To update the diagram elements and DSL details
1. In the DSL designer, right-click the ExampleShape geometry shape, point to Add, and then click Text
Decorator.
a. Name the new text decorator NamespaceDecorator .
b. In the Properties window for the text decorator, set Position to InnerBottomLeft.
2. In the DSL designer, select the line that connects the ExampleElement class to the ExampleShape shape.
a. In the DSL Details window, select the Decorator Maps tab.
b. In the Decorators list, select NamespaceDecorator, select its check box and then on the Display
property list, select Namespace.
3. In DSL Explorer, expand the Domain Classes folder, right-click the ExampleElement node, and then click
Add New Domain Type Descriptor.
a. Expand the ExampleElement node, and select the Custom Type Descriptor (Domain Type
Descriptor) node.
b. In the Properties window for the domain type descriptor, set Custom Coded to True.
4. In DSL Explorer, select the Xml Serialization Behavior node.
a. In the Properties window, set Custom Post Load to True.

Transforming Templates
Now that you have defined the domain classes and properties for your DSL, you can verify that the DSL definition
can be transformed correctly to regenerate the code for your project.
To transform the text templates
1. On the Solution Explorer toolbar, click Transform All Templates.
2. The system regenerates the code for the solution, and saves DslDefinition.dsl. For information about the XML
format of definition files, see The DslDefinition.dsl File.

Creating Files for Custom Code


When you transform all templates, the system generates the source code that defines your domain-specific
language in the Dsl and DslPackage projects. So that you can avoid interfering with the generated text, write your
custom code in files that are distinct from the generated code files.
You must provide code for maintaining the value and the state of your tracking property. To help you distinguish
your custom code from the generated code, and to avoid file naming conflicts, put your custom code files in a
separate subfolder.
To create the code files
1. In Solution Explorer, right-click the DSL project, point to Add, and then click New Folder. Name the new
folder CustomCode .
2. Right-click the new CustomCode folder, point to Add, and then click New Item.
3. Select the Code File template, set the Name to NamespaceTrackingProperty.cs , and then click OK.
The NamespaceTrackingProperty.cs file is created and opened for editing.
4. In the folder, create the following code files: ExampleModel.cs,``HelperClasses.cs , Serialization.cs , and
TypeDescriptor.cs .
5. In the DslPackage project, also create a CustomCode folder, and add to it a Package.cs code file.

Adding Helper Classes to Support Tracking Properties


To the HelperClasses.cs file, add the TrackingHelper and CriticalException classes as follows. You will reference
these classes later in this walkthrough.
To add the helper classes
1. Add the following code to the HelperClasses.cs file.

using System;
using System.Collections;
using System.Diagnostics;
using Microsoft.VisualStudio.Modeling;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
internal static class TrackingHelper
{
/// <summary>Notify each model element in a collection that a tracked
/// property has changed.</summary>
/// <param name="store">The store for the model.</param>
/// <param name="collection">The collection of model elements that
/// contain the tracking property.</param>
/// <param name="propertyId">The ID of the tracking property.</param>
/// <param name="trackingPropertyId">The ID of the property that
/// indicates whether the tracking property is tracking.</param>
internal static void UpdateTrackingCollectionProperty(
Store store,
IEnumerable collection,
Guid propertyId,
Guid trackingPropertyId)
{
DomainPropertyInfo propInfo =
store.DomainDataDirectory.GetDomainProperty(propertyId);

DomainPropertyInfo trackingPropInfo =
store.DomainDataDirectory.GetDomainProperty(trackingPropertyId);

Debug.Assert(propInfo != null);
Debug.Assert(trackingPropInfo != null);
Debug.Assert(trackingPropInfo.PropertyType.Equals(typeof(bool)),
"Tracking property not specified as a boolean");

foreach (ModelElement element in collection)


{
// If the tracking property is currently tracking, then notify
// it that the tracked property has changed.
bool isTracking = (bool)trackingPropInfo.GetValue(element);
if (isTracking)
{
propInfo.NotifyValueChange(element);
}
}
}
}

/// <summary>Helper class to flag critical exceptions from ones that are
/// safe to ignore.</summary>
internal static class CriticalException
{
/// <summary>Gets whether an exception is critical and can not be
/// ignored.</summary>
/// <param name="ex">The exception to check.</param>
/// <returns>True if the exception is critical.</returns>
internal static bool IsCriticalException(Exception ex)
{
if (ex is NullReferenceException
|| ex is StackOverflowException
|| ex is OutOfMemoryException
|| ex is System.Threading.ThreadAbortException)
return true;

if (ex.InnerException != null)
return IsCriticalException(ex.InnerException);

return false;
}
}
}

Adding Custom Code for the Custom Type Descriptor


Implement the GetCustomProperties method for the type descriptor for the ExampleModel domain class.

NOTE
The code that the DSL Tools generate for the custom type descriptor for ExampleModel calls GetCustomProperties ;
however, the DSL Tools do not generate code that implements the method.

Defining this method creates the tracking property descriptor for the Namespace tracking property. Also, providing
attributes for the tracking property enables the Properties window to display the property correctly.
To modify the type descriptor for the ExampleModel domain class
1. Add the following code to the TypeDescriptor.cs file.
using System;
using System.ComponentModel;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
// To the custom type descriptor for the ExampleElement domain class, add
// the GetCustomProperties method.
public partial class ExampleElementTypeDescriptor
{
/// <summary>Returns the property descriptors for the described
/// ExampleElement domain class.</summary>
/// <remarks>This method adds the tracking property descriptor.
/// </remarks>
private PropertyDescriptorCollection GetCustomProperties(
Attribute[] attributes)
{
// Get the default property descriptors from the base class
PropertyDescriptorCollection propertyDescriptors =
base.GetProperties(attributes);

// Get a reference to the model element that is being described.


ExampleElement source = this.ModelElement as ExampleElement;

//Add the descriptor for the tracking property.


if (source != null)
{
DomainPropertyInfo domainProperty =
source.Store.DomainDataDirectory.GetDomainProperty(
ExampleElement.NamespaceDomainPropertyId);

DomainPropertyInfo trackingProperty =
source.Store.DomainDataDirectory.GetDomainProperty(
ExampleElement.IsNamespaceTrackingDomainPropertyId);

// Define attributes for the tracking property so that the


// Properties window displays the property correctly.
Attribute[] attr = new Attribute[] {
new DisplayNameAttribute("Element Namespace"),
new DescriptionAttribute("The namespace of the element."),
new CategoryAttribute("Tracking Properties"),
};

propertyDescriptors.Add(new TrackingPropertyDescriptor(
source, domainProperty, trackingProperty, attr));
}

// Return the property descriptors for this element


return propertyDescriptors;
}
}
}

Adding Custom Code for the Package


The generated code defines a type description provider for the ExampleElement domain class; however, you must
add code to instruct the DSL to use this type description provider.
To update the DSL package to use your custom type descriptor
1. Add the following code to the Package.cs file.
using System.ComponentModel;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
// Override the default Initialize method.
internal sealed partial class TrackingPropertyDSLPackage
{
protected override void Initialize()
{
// Add the custom type descriptor for the ExampleElement type.
TypeDescriptor.AddProvider(
new ExampleElementTypeDescriptionProvider(),
typeof(ExampleElement));

base.Initialize();
}
}
}

Adding Custom Code for the Model


Implement the GetCustomElementsValue method for the ExampleModel domain class.

NOTE
The code that the DSL Tools generate for ExampleModel calls GetCustomElementsValue ; however, the DSL Tools do not
generate code that implements the method.

Defining the GetCustomElementsValuemethod provides the logic for the CustomElements calculated property of
ExampleModel . This method counts the number of ExampleElement domain classes that have a Namespace tracking
property that has a user-updated value, and returns a string that represents this count as a proportion of the total
elements in the model.
In addition, add an OnDefaultNamespaceChanged method to ExampleModel , and override the OnValueChanged method
of the DefaultNamespacePropertyHandler nested class of ExampleModel to call OnDefaultNamespaceChanged .
Because the DefaultNamespace property is used to calculate the Namespace tracking property, ExampleModel must
notify all ExampleElement domain classes that the value of DefaultNamespace has changed.
To modify the property handler for the tracked property
1. Add the following code to the ExampleModel.cs file.
using System.Linq;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
public partial class ExampleModel
{
public string GetCustomElementsValue()
{
if (this.Elements.Count == 0) return "0/0";

int number = this.Elements.Count(e => !e.IsNamespaceTracking);


return string.Format("{0}/{1}", number, this.Elements.Count);
}

#region Value changed handler for the tracked property

// When a tracked property changes, it needs to notify all of the properties


// that track it.

/// <summary>Called by the DefaultNamespace property value handler when the


/// DefaultNamespace property changes.</summary>
/// <param name="oldValue">The previous value of the property.</param>
/// <param name="newValue">The new value of the property.</param>
protected virtual void OnDefaultNamespaceChanged(
string oldValue, string newValue)
{
// Use the helper class to notify all of the elements in the model
// that the default namespace has changed.
TrackingHelper.UpdateTrackingCollectionProperty(
this.Store,
this.Elements,
ExampleElement.NamespaceDomainPropertyId,
ExampleElement.IsNamespaceTrackingDomainPropertyId);
}

// Update the change handler for the DefaultNamespace property.


internal sealed partial class DefaultNamespacePropertyHandler
{
/// <summary>Called when the DefaultNamespace property changes.</summary>
/// <param name="element">The model element that has the property that
/// changed.</param>
/// <param name="oldValue">The previous value of the property.</param>
/// <param name="newValue">The new value of the property.</param>
protected override void OnValueChanged(
ExampleModel element, string oldValue, string newValue)
{
base.OnValueChanged(element, oldValue, newValue);

if (!element.Store.InUndoRedoOrRollback)
{
element.OnDefaultNamespaceChanged(oldValue, newValue);
}
}
}

#endregion
}
}

Adding Custom Code for the Tracking Property


Add a CalculateNamespace method to the ExampleElement domain class.
Defining this method provides the logic for the CustomElements calculated property of ExampleModel . This method
counts the number of ExampleElement domain classes that have a Namespace tracking property that is in the
updated by user state, and returns a string that represents this count as a proportion of the total elements in the
model.
Also, add storage for, and methods to get and set, the Namespace custom storage property of the ExampleElement
domain class.

NOTE
The code that the DSL Tools generate for ExampleModel calls the get and set methods; however, the DSL Tools do not
generate code that implements the methods.

To add the method for the custom type descriptor


1. Add the following code to the NamespaceTrackingProperty.cs file.

using System;
using Microsoft.VisualStudio.Modeling;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
// To the domain class that has the tracking property, add the caluclation
// for when the property is tracking.
public partial class ExampleElement
{
/// <summary>Calculates the actual value of the property when it is
/// tracking.</summary>
/// <returns>The value of the tracking property when it is
/// tracking.</returns>
/// <remarks>Making this method virtual allows child classes to modify
/// the calculation. This method does not need to perform validation, as
/// its caller handles validation prior to calling this method.
/// <para>In this case, the tracking value depends on the default namespace
/// property of the parent model.</para></remarks>
protected virtual string CalculateNamespace()
{
return this.ExampleModel.DefaultNamespace;
}

#region Tracking property implementation

// Implement the Namespace domain property of the ExampleElement domain class,


// and update the IsNamespaceTracking domain property value handler.

/// <summary>Storage for the Namespace property.</summary>


private string namespaceStorage;

/// <summary>Gets the value of the Namespace property.</summary>


/// <returns>The value of the Namespace property.</returns>
private string GetNamespaceValue()
{
// Only retrieve the tracked value if the store is not in the
// middle of a serialization transaction.
bool loading = this.Store.TransactionManager.InTransaction
&& this.Store.TransactionManager.CurrentTransaction.IsSerializing;

if (!loading && this.IsNamespaceTracking)


{
try
{
return this.CalculateNamespace();
}
catch (NullReferenceException)
{
return default(string);
}
catch (Exception e)
catch (Exception e)
{
if (CriticalException.IsCriticalException(e))
{
throw;
}
else
{
return default(string);
}
}
}

return namespaceStorage;
}

/// <summary>Sets the value of the Namespace property.</summary>


/// <param name="value">The new value for the property.</param>
private void SetNamespaceValue(string value)
{
namespaceStorage = value;

// Only update the state of the tracking property if the store is


// not in the middle of a serialization transaction.
bool loading = this.Store.TransactionManager.InTransaction
&& this.Store.TransactionManager.CurrentTransaction.IsSerializing;

if (!this.Store.InUndoRedoOrRollback && !loading)


{
this.IsNamespaceTracking = false;
}
}

// Update the default behavior of the ExampleElement.IsNamespaceTracking


// domain property value handler.
internal sealed partial class IsNamespaceTrackingPropertyHandler
{
/// <summary>Called after the IsNamespaceTracking property changes.
/// </summary>
/// <param name="element">The model element that has the property
/// that changed.</param>
/// <param name="oldValue">The previous value of the property.
/// </param>
/// <param name="newValue">The new value of the property.</param>
protected override void OnValueChanged(
ExampleElement element, Boolean oldValue, Boolean newValue)
{
base.OnValueChanged(element, oldValue, newValue);
if (!element.Store.InUndoRedoOrRollback && newValue)
{
DomainPropertyInfo propInfo =
element.Store.DomainDataDirectory.GetDomainProperty(
ExampleElement.NamespaceDomainPropertyId);
propInfo.NotifyValueChange(element);
}
}

/// <summary>Performs the reset operation for the IsNamespaceTracking


/// property for a model element.</summary>
/// <param name="element">The model element that has the property
/// to reset.</param>
internal void ResetValue(ExampleElement element)
{
object calculatedValue = null;

try
{
calculatedValue = element.CalculateNamespace();
}
catch (NullReferenceException)
catch (NullReferenceException)
{
}
catch (System.Exception e)
{
if (CriticalException.IsCriticalException(e))
{
throw;
}
}

if ((calculatedValue != null
&& object.Equals(element.Namespace, calculatedValue)))
{
element.isNamespaceTrackingPropertyStorage = true;
}
}

/// <summary>Method to set IsNamespaceTracking to false so that this


/// instance of this tracking property is not storage-based.
/// </summary>
/// <param name="element">The element on which to reset the property
/// value.</param>
internal void PreResetValue(ExampleElement element)
{
// Force the IsNamespaceTracking property to false so that the value
// of the Namespace property is retrieved from storage.
element.isNamespaceTrackingPropertyStorage = false;
}
}

#endregion
}
}

Adding Custom Code to Support Serialization


Add code to support the custom post-load behavior for XML serialization.

NOTE
The code that the DSL Tools generate calls the OnPostLoadModel and OnPostLoadModelAndDiagram methods; however, the
DSL Tools do not generate code that implements these methods.

To add code to support the custom post-load behavior


1. Add the following code to the Serialization.cs file.

using System;
using System.Diagnostics;
using Microsoft.VisualStudio.Modeling;

namespace CompanyName.ProductName.TrackingPropertyDSL
{
#region Helper classes for maintaining state while the store is serializing.

public abstract partial class TrackingPropertyDSLSerializationHelperBase


{
/// <summary>Reset the tracking state properties to their natural values
/// based on comparing storage with calculation.</summary>
/// <param name="store">The store that contains this model.</param>
internal static void ResetTrackingProperties(Store store)
{
// Two passes required - one to set all elements to storage-based
// then another to set some back to being tracking.
foreach (ModelElement element in store.ElementDirectory.AllElements)
{
ExampleElement myElementInstance = element as ExampleElement;
if (myElementInstance != null)
{
myElementInstance.PreResetIsTrackingProperties();
continue;
}
}
foreach (ModelElement element in store.ElementDirectory.AllElements)
{
ExampleElement myElementInstance = element as ExampleElement;
if (myElementInstance != null)
{
myElementInstance.ResetIsTrackingProperties();
continue;
}
}
}
}

// Add the pre-reset and reset methods for the model element.
public partial class ExampleElement
{
/// <summary>Calls the pre-reset method on the associated property value
/// handler for each tracking property of this model element.</summary>
internal virtual void PreResetIsTrackingProperties()
{
ExampleElement.IsNamespaceTrackingPropertyHandler.Instance.PreResetValue(this);
}

/// <summary>Calls the reset method on the associated property value


/// handler for each tracking property of this model element.</summary>
internal virtual void ResetIsTrackingProperties()
{
ExampleElement.IsNamespaceTrackingPropertyHandler.Instance.ResetValue(this);
}
}

#endregion

#region Custom serialization code

// To the serialization helper for the TrackingPropertyDSL class, add post


// load handlers to bind the tracking property to the serialization process.
// These handlers manage the tracking states and property values when the
// model is serialized and deserialized.
public partial class TrackingPropertyDSLSerializationHelperBase
{
/// <summary>Customize model loading.</summary>
/// <param name="serializationResult">The serialization result from the
/// load operation.</param>
/// <param name="partition">The partition in which the new
/// instance was created.</param>
/// <param name="fileName">The name of the file from which the
/// instance was deserialized.</param>
/// <param name="modelRoot">The root of the file that was loaded.
/// </param>
private void OnPostLoadModel(
SerializationResult serializationResult,
Partition partition,
string fileName,
ExampleModel modelRoot)
{
}

/// <summary>Customize model and diagram loading.</summary>


/// <param name="serializationResult">Stores serialization result from
/// the load operation.</param>
/// the load operation.</param>
/// <param name="modelPartition">Partition in which the new
/// instance will be created.</param>
/// <param name="modelFileName">Name of the file from which the
/// instance will be deserialized.</param>
/// <param name="diagramPartition">Partition in which the new
/// diagram instance will be created.</param>
/// <param name="diagramFileName">Name of the file from which the
/// diagram instance will be deserialized.</param>
/// <param name="modelRoot">The root of the file that was loaded.</param>
/// <param name="diagram">The diagram matching the modelRoot.</param>
private void OnPostLoadModelAndDiagram(
SerializationResult serializationResult,
Partition modelPartition,
string modelFileName,
Partition diagramPartition,
string diagramFileName,
ExampleModel modelRoot,
TrackingPropertyDSLDiagram diagram)
{
Debug.Assert(modelPartition != null);
Debug.Assert(modelPartition.Store != null);

// Tracking properties need to be set up according to whether the


// serialization matches the calculated values.
TrackingPropertyDSLSerializationHelperBase.ResetTrackingProperties(
modelPartition.Store);
}
}

#endregion
}

Testing the Language


The next step is to build and run the DSL designer in a new instance of Visual Studio so that you can verify that the
tracking property is working correctly.
To exercise the language
1. On the Build menu, click Rebuild Solution.
2. On the Debug menu, click Start Debugging.
The experimental build of Visual Studio opens the Debugging solution, which contains an empty test file.
3. In Solution Explorer, double-click the Test.trackingPropertyDsl file to open it in the designer, and then click
the design surface.
Notice that in the Properties window for the diagram, the Default Namespace property is
DefaultNamespace, and the Custom Elements property is 0/0.
4. Drag an ExampleElement element from the Toolbox to the diagram surface.
5. In the Properties window for the element, select the Element Namespace property, and change the value
from DefaultNamespace to OtherNamespace.
Notice that the value of Element Namespace is now shown in bold.
6. In the Properties window, right-click Element Namespace, and then click Reset.
The value of the property is changed to DefaultNamespace, and the value is shown in a regular font.
Right-click Element Namespace again. The Reset command is now disabled because the property is
currently in its tracking state.
7. Drag another ExampleElement from the Toolbox to the diagram surface, and change its Element
Namespace to OtherNamespace.
8. Click the design surface.
In the Properties window for the diagram, the value of Custom Elements is now 1/2.
9. Change Default Namespace for the diagram from DefaultNamespace to NewNamespace.
The Namespace of the first element tracks the Default Namespace property, whereas the Namespace of
the second element retains its user-updated value of OtherNamespace.
10. Save the solution, and then close the experimental build.

Next Steps
If you plan to use more than one tracking property, or implement tracking properties in more than one DSL, you
can create a text template to generate the common code for supporting each tracking property. For more
information about text templates, see Code Generation and T4 Text Templates.

See Also
TrackingPropertyDescriptor
ElementTypeDescriptor
How to Define a Domain-Specific Language
How to: Create a Domain-Specific Language Solution
Debugging by Using the Store Viewer
10/18/2017 1 min to read Edit Online

With the Store Viewer, you can examine the state of a store used by Domain-Specific Language Tools. The Store
Viewer displays all of the domain model elements that are in a specific store, along with element properties and
links between elements.

Opening Store Viewer


When you are in the Visual Studio experimental build, stop your code at a breakpoint where an instance of the store
contains model information. Then, open the Store Viewer by typing the following command in the Immediate
window:

Microsoft.VisualStudio.Modeling.Diagnostics.StoreViewer.Show(mystore);

NOTE
You must replace mystore with the name of your store instance. Also, if you add the namespace to your code, you can type
the command for displaying Store Viewer without the fully qualified namespace:
using Microsoft.VisualStudio.Modeling.Diagnostics;

...

StoreViewer.Show(mystore);

The Show method has several overloads. You can specify an instance of a store or a partition as the parameter.
As an alternative, you can put the line of code that displays the Store Viewer anywhere in your code where the
parameter that you pass to the Show method is in scope. This action displays the Store Viewer when the line of
code executes as a snapshot of the contents of the store.
Using Store Viewer
When the Store Viewer opens, a modeless Windows Forms window appears, as the following illustration shows.
Store Viewer
The Store Viewer has three panes: the left pane, top-right pane, and bottom-right pane. The left pane is a tree view
of the types in the DomainDataDirectory member of a store. If you expand the Partition node and click an element,
the element's properties appear in the top-right pane. If the element is linked to other elements, the additional
elements appear in the bottom-right pane. If you double-click an element in the bottom-right pane, the element is
highlighted in the left pane.

See Also
Navigating and Updating a Model in Program Code
The DslDefinition.dsl File
10/18/2017 24 min to read Edit Online

This topic describes the structure of the DslDefinition.dsl file in the Dsl project of a Domain-Specific Language Tools
solution, which defines a domain-specific language. The DslDefinition.dsl file describes the classes and
relationships of a domain-specific language, together with the diagram, shapes, connectors, serialization format,
and Toolbox of the domain-specific language and its editing tools. In a domain-specific language solution, the
code that defines those tools is generated according to the information in the DslDefinition.dsl file.
Generally, you use the Domain-Specific Language Designer to edit the DslDefinition.dsl file. However, its raw form
is XML, and you can open a DslDefinition.dsl file in an XML editor. You might find it useful to understand what
information the file contains and how it is organized for debugging and extension purposes.
Examples in this topic are taken from the Component Diagram solution template. To see an example, create a
domain-specific language solution that is based on the Component Models solution template. After you create the
solution, the DslDefinition.dsl file appears in the Domain-Specific Language Designer. Close the file, right-click it in
Solution Explorer, point to Open With, click XML Editor, and then click OK.

Sections of the DslDefinition.dsl File


The root element is <Dsl>, and its attributes identify the name of the domain-specific language, the namespace,
and major and minor version numbers for versioning. The DslDefinitionModel schema defines the content and
structure for a valid DslDefinition.dsl file.
The child elements of the <Dsl> root element are as follows:
Classes
This section defines each domain class that generates a class in the generated code.
Relationships
This section defines each relationship in the model. The source and the target represent the two sides of a
relationship.
Types
This section defines each type and its namespace. Domain properties have two types. DomainEnumerations are
defined in the model and generate types into DomainModel.cs. ExternalTypes refer to types that are defined
elsewhere (such as String or Int32 ) and do not generate anything.
Shapes
This section defines the shapes that describe how the model appears in a designer. These geometric shapes are
mapped to the classes in the model in the Diagram section.
Connectors
This section defines the appearance of the connectors that appear in a designer. These geometric style descriptions
are mapped to specific relationships in the model in the Diagram section.
XmlSerializationBehavior
This section defines a serialization scheme and provides additional information about how each class is saved to a
file.
ExplorerBehavior
This section defines how the DSL Explorer window appears when the user is editing a model.
ConnectionBuilders
This section defines a connection builder for each connector tool (the tool for making links between any two classes
that can be connected). This section determines whether you can connect a source and a target class.
Diagram
This section defines a diagram, and you use it to specify properties such as background color and the root class.
(The root class is the domain class that is represented by the diagram as a whole.) The Diagram section also
contains ShapeMap and ConnectorMap elements, which specify the shape or connector that represents each
domain class or relationship.
Designer
This section defines a designer (editor), which brings together a Toolbox, validation settings, a diagram, and a
serialization scheme. The Designer section also defines the root class of the model, which is usually also the root
class of the diagram.
Explorer
This section identifies the DSL Explorer behavior (defined in the XmlSerializationBehavior section).

Monikers in the DslDefinition.dsl file


Throughout the DslDefinition.dsl file, you can use monikers to make cross-references to specific items. For example,
each Relationship definition contains a Source subsection and a Target subsection. Each subsection contains the
moniker of the class of object that can be linked with that relationship:

<DomainRelationship ... Name="LibraryHasMembers" Namespace="ExampleNamespace" > <Source>


<DomainRole ...>
<RolePlayer>
<DomainClassMoniker Name="Library" />
</RolePlayer>
</DomainRole>
</Source>

Usually, the namespace of the referenced item (in this example, the Library domain class) is the same as the
referencing item (in this case, the LibraryHasMembers domain relationship). In those cases, the moniker must give
only the name of the class. Otherwise, you should use the full form /Namespace/Name:

<DomainClassMoniker Name="/ExampleNameSpace/Library" />

The moniker system requires that siblings in the XML tree have distinct names. For this reason, validation errors
occur if you try to save a domain-specific language definition that has, for example, two classes of the same name.
You should always correct such duplicate-name errors before you save the DslDefinition.dsl file so that you can
reload it correctly later.
Each type has its own type of moniker: DomainClassMoniker, DomainRelationshipMoniker, and so on.

Types
The Types section specifies all the types that the DslDefinition.dsl file contains as types of properties. These types
fall into two kinds: external types, such as System.String, and enumerated types.
External Types
The Component Diagram example lists a set of standard primitive types, although only some of them are used.
Each External Type definition consists of just a name and a namespace, such as String and System:
<ExternalType Name="String" Namespace="System" />

The full names of the types are used, instead of the equivalent compiler keywords such as "string".
External types are not restricted to standard library types.
Enumerations
A typical Enumeration specification resembles this example:

<DomainEnumeration IsFlags="true" Name="PageSort" Namespace="Fabrikam.Wizard">


<Literals>
<EnumerationLiteral Name="Start" Value="1"/>
<EnumerationLiteral Name="Decision" Value="2"/>
</Literals>
</DomainEnumeration>

The IsFlags attribute controls whether the generated code is prefixed by the [Flags] Common Language
Runtime (CLR) attribute, which determines whether values of the enumeration can be combined bitwise. If this
attribute is set to true, you should specify power-of-two values for the literal values.

Classes
Most of the elements in any definition of a domain-specific language are either directly or indirectly instances of
DomainClass . Subclasses of DomainClass include DomainRelationship , Shape , Connector , and Diagram . The
Classes section of the DslDefinition.dsl file lists the domain classes.

Each class has a set of properties and might have a base class. In the Component Diagram example, NamedElement
is an abstract class that has a Name property, whose type is string:

<DomainClass Id="ee3161ca-2818-42c8-b522-88f50fc72de8" Name="NamedElement" Namespace="Fabrikam.CmptDsl5"


DisplayName="Named Element" InheritanceModifier="Abstract">
<Properties>
<DomainProperty Id="ef553cf0-33b5-4e34-a30b-cfcfd86f2261" Name="Name" DisplayName="Name" DefaultValue=""
Category="" IsElementName="true">
<Type>
<ExternalTypeMoniker Name="/System/String" />
</Type>
</DomainProperty>
</Properties>
</DomainClass>

NamedElement is the base of several of the other classes such as Component , which has its own properties in
addition to the Name property, which it inherited from NamedElement . The BaseClass child node contains a moniker
reference. Because the referenced class is in the same namespace, only its name is required in the moniker:

<DomainClass Name="Component" Namespace="Fabrikam.CmptDsl5" DisplayName="Component">


<BaseClass>
<DomainClassMoniker Name="NamedElement" />
</BaseClass>
<Properties>
<DomainProperty Name="Kind" DisplayName="Kind" >
<Type>
<ExternalTypeMoniker Name="/System/String" />
</Type>
</DomainProperty>
</Properties>
Every domain class (including relationships, shapes, connectors, and diagrams) can have these attributes and child
nodes:
Id. This attribute is a GUID. If you do not provide a value in the file, the Domain-Specific Language Designer
will create a value. (In illustrations in this document, this attribute is usually omitted to save space.)
Name and Namespace. These attributes specify the name and namespace of the class in the generated
code. Together they must be unique within the domain-specific language.
InheritanceModifier. This attribute is "abstract", "sealed", or none.
DisplayName. This attribute is the name that appears in the Properties window. The DisplayName
attribute can contain spaces and other punctuation.
GeneratesDoubleDerived. If this attribute is set to true, two classes are generated, and one is a subclass of
the other. All the generated methods are in the base, and the constructors are in the subclass. By setting this
attribute, you can override any generated method in custom code.
HasCustomConstructor. If this attribute is set to true, the constructor is omitted from the generated code
so that you can write your own version.
Attributes. This attribute contains the CLR Attributes of the generated class.
BaseClass. If you specify a base class, it must be of the same type. For example, a domain class must have
another domain class as its base, and a compartment shape must have a compartment shape. If you do not
specify a base class, the class in the generated code derives from a standard framework class. For example, a
domain class derives from ModelElement .
Properties. This attribute contains the properties that are maintained under transaction control and
persisted when the model is saved.
ElementMergeDirectives. Each element merge directive controls how a different instance of another class
is added to an instance of the parent class. You can find more detail about element merge directives later in
this topic.
A C# class is generated for each domain class that is listed in the Classes section. The C# classes are
generated in Dsl\GeneratedCode\DomainClasses.cs.
Properties
Each domain property has a name and a type. The name must be unique within the domain class and its transitive
bases.
The type must refer to one of those listed in the Types section. Generally, the moniker must include the
namespace.

<DomainProperty Name="Name" DisplayName="Name" DefaultValue="" Category="" IsElementName="true">


<Type>
<ExternalTypeMoniker Name="/System/String" />
</Type>
</DomainProperty>

Each domain property can also have these attributes:


IsBrowsable. This attribute determines whether the property appears in the Properties window when the
user clicks an object of the parent class.
IsUIReadOnly. This attribute determines whether the user can change the property in the Properties
window or through a decorator in which the property is presented.
Kind. You can set this attribute to Normal, Calculated, or CustomStorage. If you set this attribute to
Calculated, you must provide custom code that determines the value, and the property will be read-only. If
you set this attribute to CustomStorage, you must provide code that both gets and sets values.
IsElementName. If this attribute is set to true, its value is automatically set to a unique value when an
instance of the parent class is created. This attribute can be set to true for only one property in each class,
which must have a string type. In the Component Diagram example, the Name property in NamedElement has
IsElementName set to true. Whenever a user creates a Component element (which inherits from NamedElement
), the name is automatically initialized to something like "Component6."
DefaultValue. If you have specified this attribute, the value that you specified is assigned to this attribute for
new instances of this class. If IsElementName is set, the DefaultValue attribute specifies the initial part of the
new string.
Category is the header under which the property will appear in the Properties window.

Relationships
The Relationships section lists all the relationships in the domain-specific language. Every Domain Relationship is
binary and directed, linking members of a source class to members of a target class. The source and target classes
are typically domain classes, but relationships to other relationships are also permitted.
For example, the Connection relationship links members of the OutPort class to members of the InPort class. Each
link instance of the relationship connects an instance of an OutPort to an instance of an InPort. Because the
relationship is many-many, each OutPort can have many Connection links with sources on it, and each InPort
instance can have many Connection links that target it.
Source and target roles
Each relationship contains source and target roles that have the following attributes:
The RolePlayer attribute references the domain class of the linked instances: OutPort for the source, InPort
for the target.
The Multiplicity attribute has four possible values (ZeroMany, ZeroOne, One, and OneMany). This attribute
refers to the number of links of this relationship that can be associated with one role player.
The PropertyName attribute specifies the name that is used in the role playing class to access the objects at
the other end. This name is used in template or custom code to traverse the relationship. For example, the
PropertyName attribute of the source role is set to Targets . Therefore, the following code will work:

OutPort op = ...; foreach (InPort ip in op.Targets) ...

By convention, property names are plural if the multiplicity is ZeroMany or OneMany.


The multiplicity of a role refers to how many of the opposite role can be associated with each instance of this
role. For example, in the relationship ComponentHasPorts, the target role has the RolePlayer attribute set to
Port, the PropertyName attribute set to Component, and the Multiplicity attribute set to ZeroOne.
Therefore, the appropriate code to use this role is:

ComponentPort p = ...; Component c = p.Component; if (c != null) ...

The role's Name is the name that is used within the Relationship class to refer to that end of a link. By
convention, a role name is always singular, because each link has only one instance at each end. The
following code would work:
Connection connectionLink = ...; OutPort op = connectionLink.Source;

By default, the IsPropertyGenerator attribute is set to true. If it is set to false, no property is created on the
Role Player class. (In that case, op.Targets , for example, would not work). However, it is still possible to use
custom code to traverse the relationship or obtain access to the links themselves if the custom code uses the
relationship explicitly:

OutPort op = ...; foreach (InPort ip in Connection.GetTargets(op)) ...


foreach (Connection link in Connection.GetLinksToTargets(op)) ...

Relationship attributes
In addition to the attributes and child nodes that are available to all classes, each relationship has these attributes:
IsEmbedding. This Boolean attribute specifies whether the relationship is part of the embedding tree. Every
model must form a tree with its embedding relationships. Every domain class must therefore be the target of
at least one embedding relationship, unless it is the root of a model.
AllowsDuplicates. This Boolean attribute, which is false by default, applies only to relationships that have a
"many" multiplicity at both source and target. It determines whether language users may connect a single
pair of source and target elements by more than one link of the same relationship.

Designer and Toolbox Tabs


The main part of the Designer section of the DslDefinition.dsl file is the ToolboxTab elements. One designer can
have several of these elements, each of which represents a headed section in the generated designer's Toolbox.
Each ToolboxTab element can contain one or more ElementTool elements, ConnectionTool elements, or both.
Element tools can create instances of a specific domain class. When the user drags an element tool onto the
diagram, the result is determined by element merge directives as described in the section about element merge
directives later in this topic.
Each connection tool can invoke a specific connection builder. One connection builder can create more than one
type of relationship, depending on where the user clicks the mouse, as described in the section about connection
builders.
Neither type of tool directly constructs shapes or connectors. Each instantiates a domain class or a domain
relationship; the Shape and Connector mappings then determine how that domain class or domain relationship
appears.

Paths
Domain paths appear in several locations in the DslDefinition.dsl file. These paths specify a series of links from one
element in a model (that is, an instance of the domain-specific language) to another. Path syntax is simple but
verbose.
Paths appear in the DslDefinition.dsl file in <DomainPath>...</DomainPath> tags. Although paths can navigate
through multiple links, most examples in practice traverse only one link.
A path consists of a sequence of segments. Each segment is a hop either from an object to a link or from a link to
an object. Therefore, the hops typically alternate in a long path. The first hop is from an object to a link, the second
hop is to the object at the other end of the link, the third hop is to the next link, and so on. The occasional exception
to this sequence is where a relationship is itself the source or target of another relationship.
Each segment starts with a relationship name. In an object-to-link hop, the relationship precedes a dot and the
property name: " Relationship . Property ". In a link-to-object hop, the relationship precedes an exclamation mark
and the role name: " Relationship ! Role ".
The Component Diagram example contains a path in the ParentElementPath of the ShapeMap for InPort. This path
starts as follows:

ComponentHasPorts.Component

In this example, InPort is a subclass of ComponentPort and has a relationship ComponentHasPorts. The property is
called Component.
When writing C# against this model, you can jump across a link in one step by using the property that the
relationship generates on each of the classes that it relates:

InPort port; ... Component c = port.Component;

However, you must do both hops explicitly in Path Syntax. Because of this requirement, you can access the
intermediate link more easily. The following code completes the hop from the link to the Component:

ComponentHasPorts.Component / ! Component

(You can omit the relationship name where it is the same as in the previous segment.)

Element Merge Directives


When the language user drags an item from the Toolbox onto the diagram, an instance of the tool's class is
constructed. Also, links are made between that instance and the existing model elements. Some items, such as
components or comments, are created when the language user drags them from the Toolbox onto a blank part of
the diagram. Other items are created when the language user drags them onto other host elements. For example,
an OutPort or InPort is created when the language user drags it onto a component.
A potential host class, such as Component, will accept a new element only if the host class has an element merge
directive for the class of the new element. For example, the DomainClass node with Name="Component" contains:

<DomainClass Name="Component" ...> ...


<ElementMergeDirective>
<Index>
<DomainClassMoniker Name="ComponentPort" />
</Index>
<LinkCreationPaths>
<DomainPath>ComponentHasPorts.Ports</DomainPath>
</LinkCreationPaths>
</ElementMergeDirective> ...

The class moniker that is under the Index node references the class of element that can be accepted. In this case,
ComponentPort is the abstract base class of InPort and OutPort. Therefore, either of those elements can be
accepted.
ComponentModel, the root class of the language, has element merge directives for components and comments.
The language user can drag items for those classes directly onto the diagram because the blank parts of the
diagram represent the root class. However, ComponentModel has no element merge directive for ComponentPort.
Therefore, the language user cannot drag InPorts or OutPorts directly onto the diagram.
The element merge directive determines what link or links are created so that the new element can integrate or
merge into the existing model. For a ComponentPort, an instance of ComponentHasPorts is created. The
DomainPath identifies both the relationship and the property of the parent class, Ports, to which the new element
will be added.
You can create more than one link on an element merge directive by including more than one link creation path.
One of the paths must be embedded.
You can use more than one segment in a link creation path. In this case, the last segment defines what link must be
created. The earlier segments navigate from the parent class to the object from which the new link should be
created.
For example, you can add this element merge directive to the Component class:

<DomainClass Name="Component" ...> ...


<ElementMergeDirective>
<Index>
<DomainClassMoniker Name="Comment"/>
</Index>
<LinkCreationPaths>
<DomainPath> ComponentModelHasComponents . ComponentModel / !ComponentModel /
ComponentModelHasComments.Comments </DomainPath>
<DomainPath>CommentsReferenceComponents.Comments</DomainPath>
</LinkCreationPaths>
</ElementMergeDirective>

Language users can then drag a comment onto a component and have the new comment automatically created
with a link to the component.
The first link creation path navigates from the Component to the ComponentModel and then creates an instance of the
embedding relationship ComponentModelHasComments . The second link creation path creates a link of the reference
relationship CommentsReferenceComponents from the host Component to the new Comment. All link creation
paths must start with the host class and must end at a link that steps towards the newly instantiated class.

XmlClassData
Each domain class (including relationships and other subtypes) can have extra information provided in an
XmlClassData node, which appears under the XmlSerializationBehavior section of the DslDefinition.dsl file. This
information specifically concerns how instances of the class are stored in serialized form when a model is saved to
a file.
Much of the generated code that XmlSerializationBehavior influences is in Dsl\GeneratedCode\Serializer.cs .
Each XmlClassData node includes these child nodes and attributes:
A moniker node, which references the class to which the data applies.
XmlPropertyData for each property that is defined on the class.
XmlRelationshipData for each relationship that is sourced at the class. (Relationships also have their own
XmlClassData nodes.)
TypeName string attribute, which determines the name of the serialization helper class in the generated
code.
ElementName string, which determines the XML tag of serialized instances of this class. By convention,
ElementName is usually the same as the class name except the first letter is lowercase. For example, a
sample model file starts with the following:

<componentModel ...
MonikerElementName in the user's serialized model files. This attribute introduces a moniker that
references this class.
MonikerAttributeName, which identifies the name of the XML attribute within a moniker. In this fragment
of a user's serialized file, the author of the domain-specific language defined MonikerElementName as
"inPortMoniker" and MonikerAttributeName as "path":

<inPortMoniker path="//Component2/InPort1" />

ConnectionBuilders
A connection builder is defined for each connection tool. Each connection builder consists of one or more
LinkConnectDirective elements, each of which contains one or more SourceDirective elements and one or more
TargetDirective elements. After clicking a connection tool, the user can start a connection from any shape mapped
to a model element that appears in the list of SourceDirective elements. The connection can then be completed on a
shape that is mapped to an element that appears in the list of TargetDirective elements. The class of relationship
instantiated depends on the LinkConnectDirective element designated by where the connection was started.
XmlPropertyData
A DomainPropertyMoniker attribute identifies the property to which the data refers. This attribute must be a
property of the enclosing ClassData's class.
The XmlName attribute gives the corresponding attribute name as it should appear in the XML. By convention, this
string is the same as the property name except the first letter is lowercase.
By default, the Representation attribute is set to Attribute. If Representation is set to Element, a child node is
created in the XML. If Representation is set to Ignore, the property is not serialized.
The IsMonikerKey and IsMonikerQualifier attributes give a property a role in identifying instances of the parent
class. You can set IsMonikerKey to true for one property that is defined in or inherited by a class. This attribute
identifies an individual instance of the parent class. The property that you set to IsMonikerKey is usually a name or
other key identifier. For example, the Name string property is the moniker key for NamedElement and its derived
classes. When the user saves a model to file, this attribute must contain unique values for each instance, among its
siblings in the tree of embedding relationships.
In the serialized model file, the full moniker of an element is a path from the model root down the tree of
embedding relationships, quoting the moniker key at each point. For example, InPorts are embedded within
Components, which are in turn embedded in the model root. A valid moniker is therefore:

<inPortMoniker name="//Component2/InPort1" />

You can set the IsMonikerQualifier attribute for a string property and provide an additional way to construct the
full name of an element. For example, in the DslDefinition.dsl file, Namespace is a moniker qualifier.
XmlRelationshipData
Within a serialized model file, links (of both embedding and reference relationships) are represented by child nodes
of the source end of the relationship. For embedding relationships, the child node contains a subtree. For reference
relationships, the child node contains a moniker that references another part of the tree.
The XmlRelationshipData attribute in an XmlClassData attribute defines exactly how the child nodes are nested
within the source element. Every relationship that is a source on the Domain Class has one XmlRelationshipData
attribute.
The DomainRelationshipMoniker attribute identifies one of the relationships sourced on the class.
The RoleElementName attribute gives the XML tag name that encloses the child node in the serialized data.
For example, the DslDefinition.dsl file contains:

<XmlClassData ElementName="component" ...>


<DomainClassMoniker Name="Component" />
<ElementData>
<XmlRelationshipData RoleElementName="ports">
<DomainRelationshipMoniker Name="ComponentHasPorts" />
</XmlRelationshipData>

Therefore, the serialized file contains:

<component name="Component1"> <!-- parent ->


<ports> <!-- role ->
<outPort name="OutPort1"> <!-- child element ->
...
</outPort>
</ports> ...

If the UseFullForm attribute is set to true, an extra layer of nesting is introduced. This layer represents the
relationship itself. The attribute must be set to true if the relationship has properties.

<XmlClassData ElementName="outPort">
<DomainClassMoniker Name="OutPort" />
<ElementData>
<XmlRelationshipData UseFullForm="true" RoleElementName="targets">
<DomainRelationshipMoniker Name="Connection" />
</XmlRelationshipData>
</ElementData>
</XmlClassData>

The serialized file contains:

<outPort name="OutPort1"> <!-- Parent ->


<targets> <!-- role ->
<connection sourceRoleName="X"> <!-- relationship link ->
<inPortMoniker name="//Component2/InPort1" /> <!-- child ->
</connection>
</targets>
</outPort>

(The Connection Relationship has its own XML class data, which provides its element and attribute names.)
If the OmitElement attribute is set to true, the relationship role name is omitted, which abbreviates the serialized
file and is unambiguous if the two classes have no more than one relationship. For example:

<component name="Component3">
<!-- only one relationship could get here: ->
<outPort name="OutPort1">
<targets> ...

Serialization of a Domain-Specific Language Definition


The DslDefinition.dsl file is itself a serialized file and conforms to a domain-specific language definition. The
following are some examples of XML serialization definitions:
Dsl is the RootClass node and the class of the diagram. DomainClass, DomainRelationship, and other
elements are embedded under Dsl .
Classes is the RoleElementName of the relationship between Domain-Specific Language and
DomainClass.

<Dsl Name="CmptDsl5" ...>


<Classes>
<DomainClass Name="NamedElement" InheritanceModifier="Abstract" ...

The XmlSerializationBehavior attribute is embedded under the Dsl attribute, but the OmitElement attribute
has been set on the embedding relationship. Therefore, no RoleElementName attribute intervenes. By contrast, a
ClassData attribute is the RoleElementName attribute of the embedding relationship between an
XmlSerializationBehavior attribute and an XmlClassData attribute.

<Dsl Name="CmptDsl5" ...> ...


<XmlSerializationBehavior Name="ComponentsSerializationBehavior" >
<ClassData>
<XmlClassData ...>...</XmlClassData>
<XmlClassData ...>...</XmlClassData>

ConnectorHasDecorators is the embedding relationship between Connector and Decorator . UseFullForm has
been set so that the name of the relationship appears with its list of properties for each link from the Connector
object. However, OmitElement has also been set so that no RoleElementName encloses the multiple links that are
embedded inside Connector :

<Connector Name="AssociationLink" ...>


<ConnectorHasDecorators Position="TargetTop" ...>
<TextDecorator Name="TargetRoleName" />
</ConnectorHasDecorators>
<ConnectorHasDecorators Position="SourceTop" ...>
<TextDecorator Name="SourceRoleName" />
</ConnectorHasDecorators>
</Connector>

Shapes and Connectors


Shape and Connector definitions inherit attributes and child nodes from domain classes, in addition to the
following:
Color and Line``Style attributes.
ExposesFillColorAsProperty and several similar attributes. These Boolean attributes make the
corresponding property variable by the user. Generally, when a language user clicks a shape on the diagram,
the properties that appear in the Properties window are those of the domain class instance to which the
shape is mapped. If ExposesFillColorAsProperty is set to true, a property of the shape itself also appears.
ShapeHasDecorators. An instance of this attribute occurs for each text, icon, or expand/collapse decorator.
(In the DslDefinition.dsl file, ShapeHasDecorators is a relationship with UseFullForm set to true.)

Shape Maps
Shape maps determine how instances of a given domain class appear on the screen, represented by a shape. Both
shape and connector maps appear under the Diagram section of the DslDefinition.dsl file.
As in the following example, the ShapeMap elements have, at a minimum, the moniker of a domain class, the
moniker of a shape, and a ParentElementPath element:
<ShapeMap>
<DomainClassMoniker Name="InPort" />
<ParentElementPath>
<DomainPath>ComponentHasPorts.Component/!Component</DomainPath>
</ParentElementPath>
<PortMoniker Name="InPortShape" />
</ShapeMap>

The primary function of the ParentElementPath element is so that the same class of objects can appear as a
different shape in different contexts. For example, if an InPort could also be embedded in a comment, the InPort
could appear as a different shape for that purpose.
Secondly, the path determines how the shape relates to its parent. No embedding structure is defined between the
shapes in a DslDefinition.dsl file. You must infer the structure from the shape maps. The parent of a shape is the
shape that is mapped to the domain element that the parent element path identifies. In this case, the path identifies
the component to which the InPort belongs. In another shape map, the Component class is mapped to
ComponentShape. Therefore, the new InPort shape is made a child shape of its component's ComponentShape .
If you attached the InPort shape to the diagram instead, the parent element path would have to take another step,
to the component model, which is mapped to the diagram:

ComponentHasPorts . Component / ! Component / ComponentModelHasComponents . ComponentModel / !


ComponentModel

The root of the model does not have a shape map. Instead, the root is referenced directly from the diagram, which
has a Class element:

<Diagram Name="ComponentDiagram" >


<Class>
<DomainClassMoniker Name="ComponentModel" />
</Class>...

Decorator Maps
A decorator map associates a property in the mapped class to a decorator on the shape. If the property is an
enumerated or Boolean type, its value can determine whether the decorator is visible. If the decorator is a text
decorator, the value of the property can appear, and the user can edit it.
Compartment Shape Maps
Compartment shape maps are subtypes of shape maps.

Connector Maps
The minimal connector map references a connector and a relationship:

<ConnectorMap>
<ConnectorMoniker Name="CommentLink" />
<DomainRelationshipMoniker Name="CommentsReferenceComponents" />
</ConnectorMap>

Connector maps can also contain decorator maps.

See Also
Domain-Specific Language Tools Glossary
How to Define a Domain-Specific Language
Understanding Models, Classes and Relationships
Understanding the DSL Code
10/18/2017 14 min to read Edit Online

A Domain-Specific Language (DSL) solution generates an API that you can use to read and update instances of the
DSL in Visual Studio. This API is defined in the code that is generated from the DSL definition. This topic describes
the generated API.

The example solution: Component Diagrams


To create the solution that is the source of most of the examples in this topic, create a DSL from the Component
Models solution template. This is one of the standard templates that appears when you create a new DSL solution.

NOTE
The Component Diagrams DSL template is not related to the UML component diagrams that you can create by using the
Architecture menu in Visual Studio. In the New Project dialog box, expand Other Project Types\Extensibility and then
click Domain-Specific Language Designer.

Press F5 and experiment, if you are not familiar with this solution template. Notice in particular that you create
ports by dragging a port tool onto a component, and that you can connect ports.

The Structure of the DSL Solution


The Dsl project defines the API for your DSL. The DslPackage project defines how it integrates with Visual Studio.
You can also add your own projects, which can also contain code generated from the model.
The code directories
Most of the code in each of these projects is generated from Dsl\DslDefinition.dsl. The generated code is in the
Generated Code folder. To see a generated file, click [+] next to the generating .tt file.
We recommend that you inspect the generated code to help you understand the DSL. To see the generated files,
expand the *.tt files in Solution Explorer.
The *.tt files contain very little generating code. Instead, they use <#include> directives to include shared template
files. The shared files can be found in \Program Files\Microsoft Visual Studio
10.0\Common7\IDE\Extensions\Microsoft\DSL SDK\DSL Designer\11.0\TextTemplates
When you add your own program code to the DSL solution, add it in a separate file, outside the Generated Code
folder. You might want to create a Custom Code folder. (When you add a new code file to a custom folder,
remember to correct the namespace in the initial code skeleton.)
We strongly recommend that you do not edit the generated code directly, because your edits will be lost when you
rebuild the solution. Instead, to customize your DSL:
Adjust the many parameters in the DSL Definition.
Write partial classes in separate code files, to override methods that are defined in, or inherited by, the
generated classes. In some cases, you have to set the Generates Double Derived option of a class in the
DSL Definition, in order to be able to override a generated method.
Set options in the DSL Definition that cause the generated code to provide 'hooks' for your own code.
For example, if you set the Has Custom Constructor option of a domain class, and then build the solution,
you will see error messages. When you double-click one of these error messages, you will see comments in
the generated code that explain what your custom code should provide.
Write your own text templates to generate code specific to your application. You can use include files to
share parts of the templates that are common to many projects, and you can create Visual Studio project
templates to set up projects that are initialized with your own file structure.

Generated Files in Dsl


The following generated files appear in the Dsl project.
YourDsl Schema.xsd

The schema for files that contains instances of your DSL. This file is copied to the compilation (bin) directory. When
you install your DSL, you can copy this file to \Program Files\Microsoft Visual Studio 11.0\Xml\Schemas so
that model files can be validated. For more information, see Deploying Domain-Specific Language Solutions.
If you customize serialization by setting options in DSL Explorer, the schema will change accordingly. However, if
you write your own serialization code, this file might no longer represent the actual schema. For more information,
see Customizing File Storage and XML Serialization.
ConnectionBuilders.cs

A connection builder is a class that creates relationships. It is the code behind a connection tool. This file contains a
pair of classes for each connection tool. Their names are derived from the names of the domain relationship and
connection tool: RelationshipBuilder, and ConnectorToolConnectAction.
(In the component solution example, one of the connection builders is called ConnectionBuilder, This is a
coincidence, because the domain relationship is named Connection.)
The relationship is created in the Relationship Builder.Connect() method. The default version verifies that the
source and target model elements are acceptable, and then instantiates the relationship. For example:
CommentReferencesSubject(sourceAccepted, targetAccepted);

Each builder class is generated from a node in the Connection Builders section in DSL Explorer. One Connect
method can create relationships between one or more pairs of domain classes. Each pair is defined by a Link
Connect Directive, which you can find in DSL Explorer under the builder node.
For example, you could add to one connection builder Link Connect Directives for each of the three types of
relationship in the sample DSL. This would provide the user with a single connection tool. The type of relationship
instantiated would depend on the types of the source and target elements selected by the user. To add Link
Connect Directives, right-click a builder in DSL Explorer.
To write custom code that runs when a specific type of domain relationship is created, select the appropriate Link
Connect Directive under the builder node. In the Properties window, set Uses Custom Connect. Rebuild the
solution, and then supply code to correct the resulting errors.
To write custom code that runs whenever the user uses this connection tool, set the Is Custom property of the
connection builder. You can supply code that decides whether a source element is permitted, whether a specific
combination of source and target is permitted, and what updates should be made to the model when a connection
is made. For example, you could allow a connection only if it would not create a loop in the diagram. Instead of a
single relationship link, you could instantiate a more complex pattern of several inter-related elements between the
source and target.
Connectors.cs

Contains the classes for the connectors, which are the diagram elements that typically represent reference
relationships. Each class is generated from one connector in the DSL Definition. Every connector class is derived
from BinaryLinkShape
To make the color and some other style features variable at run time, right-click the class on the DSL Definition
diagram and point to Add Exposed.
To make additional style features variable at run time, see for example TextField and ShapeElement.
Diagram.cs

Contains the class that defines the diagram. It is derived from Diagram.
To make the color and some other style features variable at run time, right-click the class on the DSL Definition
diagram and point to Add Exposed.
In addition, this file contains the FixupDiagram rule, which responds when a new element is added to the model.
The rule adds a new shape and links the shape to the model element.
DirectiveProcessor.cs

This directive processor helps your users to write text templates that read an instance of your DSL. The directive
processor loads the assemblies (DLLs) for your DSL, and effectively inserts using statements for your namespace.
This allows the code in the text templates to use the classes and relationships that you have defined in your DSL.
For more information, see Generating Code from a Domain-Specific Language and Creating Custom T4 Text
Template Directive Processors.
DomainClasses.cs

Implementations of domain classes that you have defined, including abstract classes and the model root class. They
are derived from ModelElement.
Each domain class contains:
A property definition and a nested handler class for each domain property. You can override
OnValueChanging() and OnValueChanged(). For more information, see Domain Property Value Change
Handlers.
In the example DSL, the Comment class contains a property Text and a handler class TextPropertyHandler .
Accessor properties for the relationships in which this domain class participates. (There is no nested class for
role properties.)
In the example DSL, the Comment class has accessors that access its parent model through the embedding
relationship ComponentModelHasComments .
Constructors. If you want to override these, set Has Custom Constructor on the domain class.
Element Group Prototype (EGP) handler methods. These are necessary if the user can merge (add) another
element onto instances of this class. Typically the user does this by dragging from an element tool or
another shape, or by pasting.
In the example DSL, an Input Port or Output Port can be merged onto a Component. Also, Components and
Comments can be merged onto the model. The
The EGP handler methods in the Component class allow a Component to accept Ports, but not Comments.
The EGP handler in the root model class accepts Comments and Components, but not Ports.
DomainModel.cs

The class that represents the domain model. It is derived from DomainModel.

NOTE
This is not the same as the root class of the model.

Copy and Delete Closures define what other elements should be included when an element is copied or deleted.
You can control this behavior by setting the Propagates Copy and Propagates Delete properties of the roles at
each side of every relationship. If you want the values to be determined dynamically, you can write code to override
the methods of the Closure classes.
DomainModelResx.resx

This contains strings such as the descriptions of domain classes and properties, property names, toolbox labels,
standard error messages, and other strings that could be displayed to the user. It also contains tool icons and
images for image shapes.
This file is bound into the built assembly, and provides the default values of these resources. You can localize your
DSL by creating a satellite assembly that contains a localized version of the resources. That version will be used
when the DSL is installed in a culture matching the localized resources. For more information, see Deploying
Domain-Specific Language Solutions.
DomainRelationships.cs

Each link between two elements in a model is represented by an instance of a domain relationship class. All
relationship classes are derived from ElementLink, which in turn is derived from ModelElement. Because it is a
ModelElement, an instance of a relationship can have properties and can be the source or target of a relationship.
HelpKeywordHelper.cs

Provides functions that are used when the user presses F1.
MultiplicityValidation.cs

In relationship roles where you specify a multiplicity of 1..1 or 1..*, the user should be warned that at least one
instance of the relationship is required. This file provides validation constraints that implement those warnings. The
1..1 link to a embedding parent is not verified.
For these constraints to be executed, you must have set one of the Uses... options in the Editor\Validation node
in DSL Explorer. For more information, see Validation in a Domain-Specific Language.
PropertiesGrid.cs

This file contains code only if you have attached a Custom Type Descriptor to a domain property. For more
information, see Customizing the Properties Window.
SerializationHelper.cs

A validation method to ensure that no two elements are referenced by the same moniker. For more
information, see Customizing File Storage and XML Serialization.
SerializationHelper class, which provides functions that are used in common by the serialization classes.
Serializer.cs

A serializer class for each domain class, relationship, shape, connector, diagram, and model.
Many of the features of these classes can be controlled by the settings in DSL Explorer under Xml
Serialization Behavior.
Shapes.cs

A class for every shape class in the DSL Definition. Shapes are derived from NodeShape. For more
information, see Customizing File Storage and XML Serialization.
To override the generated methods with your own methods in a partial class, set Generates Double
Derived for the connector in the DSL Definition. To replace a constructor with your own code, set Has
Custom Constructor.
To make the color and some other style features variable at run time, right-click the class on the DSL
Definition diagram and point to Add Exposed.
To make additional style features variable at run time, see for example TextField and ShapeElement
ToolboxHelper.cs

Sets up the toolbox by installing element group prototypes into the element tools. Copies of these
prototypes are merged with the target elements when the user runs the tool.
You could override CreateElementPrototype() to define a toolbox item that creates a group of several
objects. For example, you could define an item to represent objects that have sub-components. After
changing the code, reset the experimental instance of Visual Studio to clear the toolbox cache.

Generated files in the DslPackage project


DslPackage couples the DSL model to the Visual Studio shell, managing the window, toolbox, and menu
commands. Most of the classes are double derived, so that you can override any of their methods.
CommandSet.cs

The context menu commands that are visible on the diagram. You can adapt or add to this set. This file contains the
code for the commands. The location of the commands on menus is determined by the Commands.vsct file. For
more information, see Writing User Commands and Actions.
Constants.cs

GUIDs.
DocData.cs

YourDsl DocData manages loading and saving a model to file, and creates the Store instance.
For example, if you want to save your DSL in a database instead of a file, you could override Load and Save
methods.
DocView.cs

YourDsl DocView manages the window in which the diagram appears. For example, you could embed the diagram
inside a windows Form:
Add a User Control file to the DslPackage project. Add a Panel in which the diagram can be displayed. Add buttons
and other controls. In the code view of the form, add the following code, adjusting the names to your DSL:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Shell;

namespace Company.EmbedInForm
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}

private DiagramDocView docView;

public UserControl1(DiagramDocView docView, Control content)


: this()
{
this.docView = docView;
panel1.Controls.Add(content);
}

private void button1_Click(object sender, EventArgs e)


{
ExampleModel modelRoot = this.docView.CurrentDiagram.ModelElement as ExampleModel;
foreach (ExampleElement element in modelRoot.Elements)
{
listBox1.Items.Add(element.Name);
}
}
}
internal partial class EmbedInFormDocView
{

private ContainerControl container;

/// <summary>
/// Return a User Control instead of the DSL window.
/// The user control will contain the DSL window.
/// </summary>

public override System.Windows.Forms.IWin32Window Window


{
get
{
if (container == null)
{
// Put the normal DSL Window inside our control
container = new UserControl1(this, (Control)base.Window);
}
return container;
}
}
}

}
EditorFactory.cs

Instantiates DocData and DocView . It fulfills a standard interface that Visual Studio uses to open an editor when
your DSL package starts. It is referenced in the ProvideEditorFactory attribute in Package.cs
GeneratedVSCT.vsct

Locates the standard menu commands on menus, such as the diagram context menu, the Edit menu, and so on.
The code for the commands is in CommandSet.cs. You can relocate or modify the standard commands, and you can
add your own commands. For more information, see Writing User Commands and Actions.
ModelExplorer.cs

Defines the Model Explorer for your DSL. This is the tree view of the model that the user sees alongside the
diagram.
For example, you could override InsertTreeView() to change the order in which elements appear in the Model
Explorer.
If you want the selection in the model explorer to keep synchronized with the diagram selection, you could use the
following code:

protected override void OnSelectionChanged(global::System.EventArgs e)


{
base.OnSelectionChanged(e);
// get the selected element
DslModeling::ModelElement selectedElement =
this.PrimarySelection as DslModeling::ModelElement;
// Select in the model explorer
SelectInModelExplorer<YOURLANGUAGEExplorerToolWindow>(selectedElement);
}
private void SelectInModelExplorer<T>(DslModeling::ModelElement modelElement)
where T : DslShell.ModelExplorerToolWindow
{
DslShell::ModelingPackage package =
this.GetService(typeof(VSShell.Package)) as DslShell::ModelingPackage;

if (package != null)
{
// find the model explorer window
T explorerWindow = package.GetToolWindow(typeof(T), true) as T;
if (explorerWindow != null)
{
// get the tree container
DslShell.ModelExplorerTreeContainer treeContainer =
explorerWindow.TreeContainer;
// find the tree node
DslShell.ExplorerTreeNode treeNode =
treeContainer.FindNodeForElement(modelElement);
// select the node
explorerWindow.TreeContainer.ObjectModelBrowser.SelectedNode = treeNode;
}
}
}

ModelExplorerToolWindow.cs

Defines the window in which the model explorer is displayed. Handles the selection of items in the explorer.
Package.cs

This file defines how the DSL integrates into Visual Studio. Attributes on the package class register the DSL as the
handler for files that have your file extension, define its toolbox, and define how to open a new window. The
Initialize() method is called one time when the first DSL is loaded into a Visual Studio instance.
Source.extension.vsixmanifest

To customize this file, edit the .tt file.

WARNING
If you edit the .tt file to include resources such as icons or images, make sure that the resource is included in the VSIX build.
In Solution Explorer, select the file and make sure that the Include in VSIX property is True .

This file controls how the DSL is packaged into a Visual Studio Integration Extension (VSIX). For more information,
see Deploying Domain-Specific Language Solutions.

See Also
How to Define a Domain-Specific Language
Understanding Models, Classes and Relationships
Customizing and Extending a Domain-Specific Language
Writing Code to Customise a Domain-Specific Language
Validation in a Domain-Specific Language
10/18/2017 10 min to read Edit Online

As the author of a domain-specific language (DSL), you can define validation constraints to verify that the model
created by the user is meaningful. For example, if your DSL allows users to draw a family tree of people and their
ancestors, you could write a constraint that ensures that children have birth dates after their parents.
You can have the validation constraints execute when the model is saved, when it is opened, and when the user
explicitly runs the Validate menu command. You can also execute validation under program control. For
example, you could execute validation in response to a change in a property value or relationship.
Validation is particularly important if you are writing text templates or other tools that process your users'
models. Validation ensures that the models fulfill the preconditions assumed by those tools.

WARNING
You can also allow validation constraints to be defined in separate extensions to your DSL, along with extension menu
commands and gesture handlers. Users can choose to install these extensions in addition to your DSL. For more
information, see Extend your DSL by using MEF.

Running Validation
When a user is editing a model, that is, an instance of your domain-specific language, the following actions can
run validation:
Right-click the diagram and select Validate All.
Right-click the top node in the Explorer of your DSL and select Validate All
Save the model.
Open the model.
In addition, you can write program code that runs validation, for example, as part of a menu command or
in response to a change.
Any validation errors will appear in the Error List window. The user can double-click an error message to
select the model elements that are the cause of the error.

Defining Validation Constraints


You define validation constraints by adding validation methods to the domain classes or relationships of your
DSL. When validation is run, either by the user or under program control, some or all of the validation methods
are executed. Each method is applied to each instance of its class, and there can be several validation methods in
each class.
Each validation method reports any errors that it finds.

NOTE
Validation methods report errors, but do not change the model. If you want to adjust or prevent certain changes, see
Alternatives to Validation.
To define a validation constraint
1. Enable validation in the Editor\Validation node:
a. Open Dsl\DslDefinition.dsl.
b. In DSL Explorer, expand the Editor node and select Validation.
c. In the Properties window, set the Uses properties to true . It is most convenient to set all these
properties.
d. Click Transform All Templates in the Solution Explorer toolbar.
2. Write partial class definitions for one or more of your domain classes or domain relationships. Write
these definitions in a new code file in the Dsl project.
3. Prefix each class with this attribute:

[ValidationState(ValidationState.Enabled)]

By default, this attribute will also enable validation for derived classes. If you want to disable validation
for a specific derived class, you can use ValidationState.Disabled .
4. Add validation methods to the classes. Each validation method can have any name, but have one
parameter of type ValidationContext.
It must be prefixed with one or more ValidationMethod attributes:

[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu


) ]

The ValidationCategories specify when the method is executed.


For example:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;

// Allow validation methods in this class:


[ValidationState(ValidationState.Enabled)]
// In this DSL, ParentsHaveChildren is a domain relationship
// from Person to Person:
public partial class ParentsHaveChildren
{
// Identify the method as a validation method:
[ValidationMethod
( // Specify which events cause the method to be invoked:
ValidationCategories.Open // On file load.
| ValidationCategories.Save // On save to file.
| ValidationCategories.Menu // On user menu command.
)]
// This method is applied to each instance of the
// type (and its subtypes) in a model:
private void ValidateParentBirth(ValidationContext context)
{
// In this DSL, the role names of this relationship
// are "Child" and "Parent":
if (this.Child.BirthYear < this.Parent.BirthYear
// Allow user to leave the year unset:
&& this.Child.BirthYear != 0)
{
context.LogError(
// Description:
"Child must be born after Parent",
// Unique code for this error:
"FAB001ParentBirthError",
// Objects to select when user double-clicks error:
this.Child,
this.Parent);
}
}

Notice the following points about this code:


You can add validation methods to domain classes or domain relationships. The code for these types is in
Dsl\Generated Code\Domain*.cs.
Each validation method is applied to every instance of its class and its subclasses. In the case of a domain
relationship, each instance is a link between two model elements.
Validation methods are not applied in any specified order, and each method is not applied to the
instances of its class in any predictable order.
It is usually bad practice for a validation method to update the store content, because this would lead to
inconsistent results. Instead, the method should report any error by calling context.LogError , LogWarning
or LogInfo .
In the LogError call, you can provide a list of model elements or relationship links that will be selected
when the user double-clicks the error message.
For information about how to read the model in program code, see Navigating and Updating a Model in
Program Code.
The example applies to the following domain model. The ParentsHaveChildren relationship has roles that
are named Child and Parent.
Validation Categories
In the ValidationMethodAttribute attribute, you specify when the validation method should be executed.

CATEGORY EXECUTION

ValidationCategories When the user invokes the Validate menu command.

ValidationCategories When the model file is opened.

ValidationCategories When the file is saved. If there are validation errors, the user
will be given the option of canceling the save operation.

ValidationCategories When the file is saved. If there are errors from methods in
this category, the user is warned that it might not be
possible to re-open the file.

Use this category for validation methods that test for


duplicated names or IDs, or other conditions that might
cause loading errors.

ValidationCategories When the ValidateCustom method is called. Validations in


this category can be invoked only from program code.

For more information, see Custom Validation Categories.

Where to Place Validation Methods


You can often achieve the same effect by placing a validation method on a different type. For example, you could
add a method to the Person class instead of the ParentsHaveChildren relationship, and have it iterate through
the links:
[ValidationState(ValidationState.Enabled)]
public partial class Person
{[ValidationMethod
( ValidationCategories.Open
| ValidationCategories.Save
| ValidationCategories.Menu
)
]
private void ValidateParentBirth(ValidationContext context)
{
// Iterate through ParentHasChildren links:
foreach (Person parent in this.Parents)
{
if (this.BirthYear <= parent.BirthYear)
{ ...

Aggregating validation constraints. To apply validation in a predictable order, define a single validation
method on an owner class, such the root element of your model. This technique also lets you aggregate multiple
error reports into a single message.
Drawbacks are that the combined method is less easy to manage, and that the constraints must all have the
same ValidationCategories . We therefore recommend that you keep each constraint in a separate method if
possible.
Passing values in the context cache. The context parameter has a dictionary into which you can place
arbitrary values. The dictionary persists for the life of the validation run. A particular validation method could, for
example, keep an error count in the context, and use it to avoid flooding the error window with repeated
messages. For example:

List<ParentsHaveChildren> erroneousLinks;
if (!context.TryGetCacheValue("erroneousLinks", out erroneousLinks))
erroneousLinks = new List<ParentsHaveChildren>();
erroneousLinks.Add(this);
context.SetCacheValue("erroneousLinks", erroneousLinks);
if (erroneousLinks.Count < 5) { context.LogError( ... ); }

Validation of Multiplicities
Validation methods for checking minimum multiplicity are automatically generated for your DSL. The code is
written to Dsl\Generated Code\MultiplicityValidation.cs. These methods take effect when you enable
validation in the Editor\Validation node in DSL Explorer.
If you set the multiplicity of a role of a domain relationship to be 1..* or 1..1, but the user does not create a link of
this relationship, a validation error message will appear.
For example, if your DSL has classes Person and Town, and a relationship PersonLivesInTown with a relationship
1..\* at the Town role, then for each Person that has no Town, an error message will appear.

Running Validation from Program Code


You can run validation by accessing or creating a ValidationController. If you want the errors to be displayed to
the user in the error window, use the ValidationController that is attached to your diagram's DocData. For
example, if you are writing a menu command, CurrentDocData.ValidationController is available in the command
set class:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
partial class MyLanguageCommandSet
{
private void OnMenuMyContextMenuCommand(object sender, EventArgs e)
{
ValidationController controller = this.CurrentDocData.ValidationController;
...

For more information, see How to: Add a Command to the Shortcut Menu.
You can also create a separate validation controller, and manage the errors yourself. For example:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
Store store = ...;
VsValidationController validator = new VsValidationController(s);
// Validate all elements in the Store:
if (!validator.Validate(store, ValidationCategories.Save))
{
// Deal with errors:
foreach (ValidationMessage message in validator.ValidationMessages) { ... }
}

Running validation when a change occurs


If you want to make sure that the user is warned immediately if the model becomes invalid, you can define a
store event that runs validation. For more information about store events, see Event Handlers Propagate
Changes Outside the Model.
In addition to the validation code, add a custom code file to your DslPackage project, with content similar to the
following example. This code uses the ValidationController that is attached to the document. This controller
displays the validation errors in the Visual Studio error list.
using System;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
namespace Company.FamilyTree
{
partial class FamilyTreeDocData // Change name to your DocData.
{
// Register the store event handler:
protected override void OnDocumentLoaded()
{
base.OnDocumentLoaded();
DomainClassInfo observedLinkInfo = this.Store.DomainDataDirectory
.FindDomainClass(typeof(ParentsHaveChildren));
DomainClassInfo observedClassInfo = this.Store.DomainDataDirectory
.FindDomainClass(typeof(Person));
EventManagerDirectory events = this.Store.EventManagerDirectory;
events.ElementAdded
.Add(observedLinkInfo, new EventHandler<ElementAddedEventArgs>(ParentLinkAddedHandler));
events.ElementDeleted.Add(observedLinkInfo, new EventHandler<ElementDeletedEventArgs>
(ParentLinkDeletedHandler));
events.ElementPropertyChanged.Add(observedClassInfo, new EventHandler<ElementPropertyChangedEventArgs>
(BirthDateChangedHandler));
}
// Handler will be called after transaction that creates a link:
private void ParentLinkAddedHandler(object sender,
ElementAddedEventArgs e)
{
this.ValidationController.Validate(e.ModelElement,
ValidationCategories.Save);
}
// Called when a link is deleted:
private void ParentLinkDeletedHandler(object sender,
ElementDeletedEventArgs e)
{
// Don't apply validation to a deleted item!
// - Validate store to refresh the error list.
this.ValidationController.Validate(this.Store,
ValidationCategories.Save);
}
// Called when any property of a Person element changes:
private void BirthDateChangedHandler(object sender,
ElementPropertyChangedEventArgs e)
{
Person person = e.ModelElement as Person;
// Not interested in changes in other properties:
if (e.DomainProperty.Id != Person.BirthYearDomainPropertyId)
return;

// Validate all parent links to and from the person:


this.ValidationController.Validate(
ParentsHaveChildren.GetLinksToParents(person)
.Concat(ParentsHaveChildren.GetLinksToChildren(person))
, ValidationCategories.Save);
}
}
}

The handlers are also called after Undo or Redo operations that affect the links or elements.

Custom Validation Categories


In addition to the standard validation categories, such as Menu and Open, you can define your own categories.
You can invoke these categories from program code. The user cannot invoke them directly.
A typical use for custom categories is to define a category that tests whether the model satisfies the
preconditions of a particular tool.
To add a validation method to a particular category, prefix it with an attribute like this:

[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}

NOTE
You can prefix a method with as many [ValidationMethod()] attributes as you want. You can add a method to both
custom and standard categories.

To invoke a custom validation:

// Invoke all validation methods in a custom category:


validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");

Alternatives to Validation
Validation constraints report errors, but do not change the model. If, instead, you want to prevent the model
becoming invalid, you can use other techniques.
However, these techniques are not recommended. It is usually better to let the user decide how to correct an
invalid model.
Adjust the change to restore the model to validity. For example, if the user sets a property above the
allowed maximum, you could reset the property to the maximum value. To do this, define a rule. For more
information, see Rules Propagate Changes Within the Model.
Roll back the transaction if an invalid change is attempted. You could also define a rule for this purpose,
but in some cases it is possible to override a property handler OnValueChanging(), or to override a method
such as OnDeleted(). To roll back a transaction, use
this.Store.TransactionManager.CurrentTransaction.Rollback(). For more information, see Domain Property
Value Change Handlers.

WARNING
Make sure that the user knows that the change has been adjusted or rolled back. For example, use
System.Windows.Forms.MessageBox.Show("message").

See Also
Navigating and Updating a Model in Program Code
Event Handlers Propagate Changes Outside the Model
Writing User Commands and Actions
10/18/2017 1 min to read Edit Online

You can customize a domain-specific language by adding commands to the diagram, and by responding to
gestures such as drag-and-drop, click, and double-click events.

User actions
How to: Modify a Standard Menu Command - You can customize the behavior of commands such as copy
and print.
How to: Add a Command to the Shortcut Menu - Create your own commands that can update your model
or external resources.
How to: Add a Drag-and-Drop Handler - Allow the user to move items from one diagram to another,
including UML diagrams. You can store references to one model in another.
How to: Intercept a Click on a Shape or Decorator - Allow your user to click through from an item to another
item that it references.
How to: Access and Constrain the Current Selection
Customizing Element Tools

See Also
Deploying Domain-Specific Language Solutions
How to: Modify a Standard Menu Command in a
Domain-Specific Language
10/18/2017 5 min to read Edit Online

You can modify the behavior of some of the standard commands that are defined automatically in your DSL. For
example, you could modify Cut so that it excludes sensitive information. To do this, you override methods in a
command set class. These classes are defined in the CommandSet.cs file, in the DslPackage project, and are
derived from CommandSet.
In summary, to modify a command:
1. Discover what commands you can modify.
2. Create a partial declaration of the appropriate command set class.
3. Override the ProcessOnStatus and ProcessOnMenu methods for the command.
This topic explains this procedure.

NOTE
If you want to create your own menu commands, see How to: Add a Command to the Shortcut Menu.

What commands can you modify?


To discover what commands you can modify
1. In the DslPackage project, open GeneratedCode\CommandSet.cs . This C# file can be found in Solution Explorer
as a subsidiary of CommandSet.tt .
2. Find classes in this file whose names end with " CommandSet ", for example Language1CommandSet and
Language1ClipboardCommandSet .

3. In each command set class, type " override " followed by a space. IntelliSense will show a list of the
methods that you can override. Each command has a pair of methods whose names begin "
ProcessOnStatus " and " ProcessOnMenu ".

4. Note which of the command set classes contains the command you want to modify.
5. Close the file without saving your edits.

NOTE
Ordinarily, you should not edit files that have been generated. Any edits will be lost the next time that the files are
generated.

Extend the appropriate command set class


Create a new file that contains a partial declaration of the command set class.
To extend the Command Set class
1. In Solution Explorer, in the DslPackage project, open the GeneratedCode folder and then look under
CommandSet.tt and open its generated file CommandSet.cs. Note the namespace and the name of the first
class that is defined there. For example, you might see:
namespace Company.Language1

{ ... internal partial class Language1CommandSet : ...

2. In DslPackage, create a folder named Custom Code. In this folder, create a new class file named
CommandSet.cs .

3. In the new file, write a partial declaration that has the same namespace and name as the generated partial
class. For example:

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
namespace Company.Language1 /* Make sure this is correct */
{ internal partial class Language1CommandSet { ...

Note If you used the class file template to create the new file, you must correct both the namespace and
the class name.

Override the command methods


Most commands have two associated methods: The method with a name like ProcessOnStatus ... determines
whether the command should be visible and enabled. It is called whenever the user right-clicks the diagram, and
should execute quickly and make no changes. ProcessOnMenu ... is called when the user clicks the command, and
should perform the function of the command. You might want to override either one or both of these methods.
To change when the command appears on a menu
Override the ProcessOnStatus... method. This method should set the Visible and Enabled properties of its
parameter MenuCommand. Typically the command looks at this.CurrentSelection to determine whether the
command applies to the selected elements, and might also look at their properties to determine whether the
command can be applied in their current state.
As a general guide, the Visible property should be determined by what elements are selected. The Enabled
property, which determines whether the command appears black or gray on the menu, should depend on the
current state of the selection.
The following example disables the Delete menu item when the user has selected more than one shape.

NOTE
This method does not affect whether the command is available through a keystroke. For example, disabling the Delete menu
item does not prevent the command from being invoked through the Delete key.
/// <summary>
/// Called when user right-clicks on the diagram or clicks the Edit menu.
/// </summary>
/// <param name="command">Set Visible and Enabled properties.</param>
protected override void ProcessOnStatusDeleteCommand (MenuCommand command)
{
// Default settings from the base method.
base.ProcessOnStatusDeleteCommand(command);
if (this.CurrentSelection.Count > 1)
{
// If user has selected more than one item, Delete is greyed out.
command.Enabled = false;
}
}

It is good practice to call the base method first, to deal with all the cases and settings with which you are not
concerned.
The ProcessOnStatus method should not create, delete, or update elements in the Store.
To change the behavior of the command
Override the ProcessOnMenu... method. The following example prevents the user from deleting more than one
element at a time, even by using the Delete key.

/// <summary>
/// Called when user presses Delete key
/// or clicks the Delete command on a menu.
/// </summary>
protected override void ProcessOnMenuDeleteCommand()
{
// Allow users to delete only one thing at a time.
if (this.CurrentSelection.Count <= 1)
{
base.ProcessOnMenuDeleteCommand();
}
}

If your code makes changes to the Store, such as creating, deleting or updating elements or links, you must do so
inside a transaction. For more information, see How to Create and Update model elements.
Writing the code of the methods
The following fragments are frequently useful within these methods:
this.CurrentSelection . The shape that the user right-clicked is always included in this list of shapes and
connectors. If the user clicks on a blank part of the diagram, the Diagram is the only member of the list.
this.IsDiagramSelected() - true if the user clicked a blank part of the diagram.
this.IsCurrentDiagramEmpty()

this.IsSingleSelection() - the user did not select multiple shapes


this.SingleSelection - the shape or diagram that the user right-clicked
shape.ModelElement as MyLanguageElement - the model element represented by a shape.
For more information about how to navigate from element to element and about how to create objects and
links, see Navigating and Updating a Model in Program Code.

See Also
MenuCommand
Writing Code to Customise a Domain-Specific Language
How to: Add a Command to the Shortcut Menu
How VSPackages Add User Interface Elements
Visual Studio Command Table (.Vsct) Files
VSCT XML Schema Reference
VMSDK - Circuit Diagrams sample. Extensive DSL Customization
Sample code: Circuit Diagrams
How to: Add a Command to the Shortcut Menu
10/18/2017 11 min to read Edit Online

You can add menu commands to your domain-specific language (DSL) so that your users can perform tasks that
are specific to your DSL. The commands appear on the context (shortcut) menu when users right-click on the
diagram. You can define a command so that it only appears in the menu in specific circumstances. For example,
you can make the command visible only when the user clicks specific types of element, or elements in specific
states.
In summary, the steps are performed in the DslPackage project, as follows:
1. Declare the command in Commands.vsct
2. Update the package version number in Package.tt. You have to do this whenever you change
Commands.vsct
3. Write methods in the CommandSet class to make the command visible and to define what you want the
command to do.
For samples, see the Visualization and Modeling SDK website.

NOTE
You can also modify the behavior of some existing commands such as Cut, Paste, Select All, and Print by overriding
methods in CommandSet.cs. For more information, see How to: Modify a Standard Menu Command.

Defining a Command using MEF


Managed Extension Framework (MEF) provides an alternative method of defining menu commands on the
diagram menu. Its primary purpose is to enable a DSL to be extended by you or by other parties. Users can choose
to install just the DSL, or can install both the DSL and extensions. However, MEF also reduces the work of defining
shortcut menu commands, after the initial work to enable MEF on the DSL.
Use the method in this topic if:
1. You want to define menu commands on menus other than the right-click shortcut menu.
2. You want to define specific groupings of commands in the menu.
3. You do not want to enable others to extend the DSL with their own commands.
4. You only want to define one command.
Otherwise, consider using the MEF method to define commands. For more information, see Extend your
DSL by using MEF.

Declare the Command in Commands.Vsct


Menu commands are declared in DslPackage\Commands.vsct. These definitions specify the labels of the menu
items and where they appear on the menus.
The file that you edit, Commands.vsct, imports definitions from several .h files, which are located in the directory
Visual Studio SDK install path\VisualStudioIntegration\Common\Inc. It also includes GeneratedVsct.vsct, which is
generated from your DSL definition.
For more information about .vsct files, see Visual Studio Command Table (.Vsct) Files.
To add the command
1. In Solution Explorer, under the DslPackage project, open Commands.vsct.
2. In the Commands element, define one or more buttons and a group. A button is an item on the menu. A
group is a section in the menu. To define these items, add the following elements:

<!-- Define a group - a section in the menu -->


<Groups>
<Group guid="guidCustomMenuCmdSet" id="grpidMyMenuGroup" priority="0x0100">
<!-- These symbols are defined in GeneratedVSCT.vsct -->
<Parent guid="guidCmdSet" id="menuidContext" />
</Group>
</Groups>
<!-- Define a button - a menu item - inside the Group -->
<Buttons>
<Button guid="guidCustomMenuCmdSet" id="cmdidMyContextMenuCommand"
priority="0x0100" type="Button">
<Parent guid="guidCustomMenuCmdSet" id="grpidMyMenuGroup"/>
<!-- If you do not want to place the command in your own Group,
use Parent guid="guidCmdSet" id="grpidContextMain".
These symbols are defined in GeneratedVSCT.vsct -->
<CommandFlag>DynamicVisibility</CommandFlag>
<Strings>
<ButtonText>My Context Menu Command</ButtonText>
</Strings>
</Button>
</Buttons>

NOTE
Each button or group is identified by a GUID and an integer ID. You can create several groups and buttons with the
same GUID. However, they must have different IDs. The GUID names and ID names are translated to actual GUIDs
and numeric IDs in the <Symbols> node.

3. Add a visibility constraint for the command so that it is loaded only in the context of your domain-specific
language. For more information, see VisibilityConstraints Element.
To do this, add the following elements in the CommandTable element after the Commands element.

<VisibilityConstraints>
<!-- Ensures the command is only loaded for this DSL -->
<VisibilityItem guid="guidCustomMenuCmdSet" id="cmdidMyContextMenuCommand"
context="guidEditor"/>
</VisibilityConstraints>

4. Define the names that you used for the guids and ids. To do this, add a Symbols element in the
CommandTable element after the Commands element.

<Symbols>
<!-- Substitute a unique GUID for the placeholder: -->
<GuidSymbol name="guidCustomMenuCmdSet"
value="{00000000-0000-0000-0000-000000000000}" >
<IDSymbol name="grpidMyMenuGroup" value="0x01001"/>
<IDSymbol name="cmdidMyContextMenuCommand" value="0x00001"/>
</GuidSymbol>
</Symbols>
5. Replace {000...000} with a GUID that identifies your groups and menu items. To obtain a new GUID, use
the Create GUID tool on the Tools menu.

NOTE
If you add more groups or menu items, you can use the same GUID. However, you must use new values for the
IDSymbols .

6. In the code you have copied from this procedure, replace each occurrence of the following strings with
your own strings:
grpidMyMenuGroup

cmdidMyContextMenuCommand

guidCustomMenuCmdSet

My Context Menu Command

Update the Package Version in Package.tt


Whenever you add or change a command, update the version parameter of the ProvideMenuResourceAttribute
that is applied to the package class before you release the new version of your domain-specific language.
Because the package class is defined in a generated file, update the attribute in the text template file that generates
the Package.cs file.
To update the Package.tt file
1. In Solution Explorer, in the DslPackage project, in the GeneratedCode folder, open the Package.tt file.
2. Locate the ProvideMenuResource attribute.
3. Increment the version parameter of the attribute, which is the second parameter. If you want, you can
write the parameter name explicitly to remind you of its purpose. For example:
[VSShell::ProvideMenuResource("1000.ctmenu", version: 2 )]

Define the Behavior of the Command


Your DSL already has some commands that are implemented in a partial class that is declared in
DslPackage\GeneratedCode\CommandSet.cs. To add new commands, you must extend this class by creating a
new file that contains a partial declaration of the same class. The name of the class is usually <YourDslName>
CommandSet . It is useful to begin by verifying the name of the class and inspecting its contents.

The command set class is derived from CommandSet.


To extend the CommandSet class
1. In Solution Explorer, in the DslPackage project, open the GeneratedCode folder and then look under
CommandSet.tt and open its generated file CommandSet.cs. Note the namespace and the name of the first
class that is defined there. For example, you might see:
namespace Company.Language1

{ ... internal partial class Language1CommandSet : ...

2. In DslPackage, create a folder that is named Custom Code. In this folder, create a new class file that is
named CommandSet.cs .
3. In the new file, write a partial declaration that has the same namespace and name as the generated partial
class. For example:
namespace Company.Language1 /* Make sure this is correct */

{ internal partial class Language1CommandSet { ...

Note If you used the class template to create the new file, you must correct both the namespace and the
class name.
Extend the Command Set class
Your command set code will typically need to import the following namespaces:

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Shell;

Adjust the namespace and the class name to match those in the generated CommandSet.cs:

namespace Company.Language1 /* Make sure this is correct */


{
// Same class as the generated class.
internal partial class Language1CommandSet
{

You have to define two methods, one to determine when the command will be visible on the context menu, and
the other to perform the command. These methods are not overrides; instead, you register them in a list of
commands.
Define when the command will be visible
For each command, define an OnStatus... method that determines whether the command will appear on the
menu, and whether it will be enabled or greyed out. Set the Visible and Enabled properties of the MenuCommand ,
as shown in the following example. This method is called in order to construct the shortcut menu every time that
the user right-clicks the diagram, so it must work quickly.
In this example, the command is visible only when the user has selected a particular type of shape, and is enabled
only when at least one of the selected elements is in a particular state. The example is based on the Class Diagram
DSL template, and ClassShape and ModelClass are types that are defined in the DSL:
private void OnStatusMyContextMenuCommand(object sender, EventArgs e)
{
MenuCommand command = sender as MenuCommand;
command.Visible = command.Enabled = false;
foreach (object selectedObject in this.CurrentSelection)
{
ClassShape shape = selectedObject as ClassShape;
if (shape != null)
{
// Visibility depends on what is selected.
command.Visible = true;
ModelClass element = shape.ModelElement as ModelClass;
// Enabled depends on state of selection.
if (element != null && element.Comments.Count == 0)
{
command.Enabled = true;
return; // seen enough
} } } }

The following fragments are frequently useful in OnStatus methods:


this.CurrentSelection . The shape that the user right-clicked is always included in this list. If the user clicks
on a blank part of the diagram, the Diagram is the only member of the list.
this.IsDiagramSelected() - true if the user clicked a blank part of the diagram.
this.IsCurrentDiagramEmpty()

this.IsSingleSelection() - the user did not select multiple objects


this.SingleSelection - the shape or diagram that the user right-clicked
shape.ModelElement as MyLanguageElement - the model element represented by a shape.
As a general guideline, make the Visible property depend on what is selected, and make the Enabled
property depend on the state of the selected elements.
An OnStatus method should not change the state of the Store.
Define what the command does
For each command, define an OnMenu... method that performs the required action when the user clicks the menu
command.
If you make changes to model elements, you must do so inside a transaction. For more information, see How to:
Modify a Standard Menu Command.
In this example, ClassShape , ModelClass , and Comment are types that are defined in the DSL, which is derived
from the Class Diagram DSL template.
private void OnMenuMyContextMenuCommand(object sender, EventArgs e)
{
MenuCommand command = sender as MenuCommand;
Store store = this.CurrentDocData.Store;
// Changes to elements and shapes must be performed in a Transaction.
using (Transaction transaction =
store.TransactionManager.BeginTransaction("My command"))
{
foreach (object selectedObject in this.CurrentSelection)
{
// ClassShape is defined in my DSL.
ClassShape shape = selectedObject as ClassShape;
if (shape != null)
{
// ModelClass is defined in my DSL.
ModelClass element = shape.ModelElement as ModelClass;
if (element != null)
{
// Do required action here - for example:

// Create a new element. Comment is defined in my DSL.


Comment newComment = new Comment(element.Partition);
// Every element must be the target of an embedding link.
element.ModelRoot.Comments.Add(newComment);
// Set properties of new element.
newComment.Text = "This is a comment";
// Create a reference link to existing object.
element.Comments.Add(newComment);
}
}
}
transaction.Commit(); // Don't forget this!
}
}

For more information about how to navigate from object to object in the model, and about how to create objects
and links, see How to: Modify a Standard Menu Command.
Register the command
Repeat in C# the declarations of the GUID and ID values that you made in the Symbols section of
CommandSet.vsct:

private Guid guidCustomMenuCmdSet =


new Guid("00000000-0000-0000-0000-000000000000");
private const int grpidMyMenuGroup = 0x01001;
private const int cmdidMyContextMenuCommand = 1;

Use the same GUID value as you inserted in Commands.vsct.

NOTE
If you change the Symbols section of the VSCT file, you must also change these declarations to match. You should also
increment the version number in Package.tt

Register your menu commands as part of this command set. GetMenuCommands() is called once when the diagram
is initialized:
protected override IList<MenuCommand> GetMenuCommands()
{
// Get the list of generated commands.
IList<MenuCommand> commands = base.GetMenuCommands();
// Add a custom command:
DynamicStatusMenuCommand myContextMenuCommand =
new DynamicStatusMenuCommand(
new EventHandler(OnStatusMyContextMenuCommand),
new EventHandler(OnMenuMyContextMenuCommand),
new CommandID(guidCustomMenuCmdSet, cmdidMyContextMenuCommand));
commands.Add(myContextMenuCommand);
// Add more commands here.
return commands;
}

Test the Command


Build and run the DSL in an experimental instance of Visual Studio. The command should appear in the shortcut
menu in the situations you have specified.
To exercise the command
1. On the Solution Explorer toolbar, click Transform All Templates.
2. Press F5 to rebuild the solution, and start debugging the domain-specific language in the experimental
build.
3. In the experimental build, open a sample diagram.
4. Right-click various items in the diagram to verify that the command is correctly enabled or disabled, and
appropriately shown or hidden, depending on the selected item.

Troubleshooting
Command does not appear in menu:
The command will appear only in debugging instances of Visual Studio, until you install the DSL package.
For more information, see Deploying Domain-Specific Language Solutions.
Make sure that your experimental sample has the correct file name extension for this DSL. To check the file
name extension, open DslDefinition.dsl in the main instance of Visual Studio. Then in DSL Explorer, right-
click the Editor node, and then click Properties. In the Properties window, examine the FileExtension
property.
Did you increment the package version number?
Set a breakpoint at the beginning of your OnStatus method. It should break when you right-click over any
part of the diagram.
OnStatus method is not called:
Make sure that the GUIDs and IDs in your CommandSet code match those in the Symbols section of
Commands.vsct.
In Commands.vsct, make sure that the GUID and ID in every Parent node identify the correct parent
Group.
In a Visual Studio command prompt, type devenv /rootsuffix exp /setup. Then restart the debugging
instance of Visual Studio.
Step through the OnStatus method to verify that command.Visible and command.Enabled are set to true.
Wrong menu text appears, or command appears in the wrong place:
Make sure that the combination of GUID and ID is unique to this command.
Make sure that you have uninstalled earlier versions of the package.

See Also
Writing Code to Customise a Domain-Specific Language
How to: Modify a Standard Menu Command
Deploying Domain-Specific Language Solutions
Sample code: Circuit Diagrams

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
How to: Add a Drag-and-Drop Handler
10/18/2017 15 min to read Edit Online

You can add handlers for drag-and-drop events to your DSL, so that users can drag items onto your diagram
from other diagrams or from other parts of Visual Studio. You can also add handlers for events such as double-
clicks. Together, drag-and-drop and double-click handlers are known as gesture handlers.
This topic discusses drag-and-drop gestures that originate on other diagrams. For move and copy events within
a single diagram, consider the alternative of defining a subclass of ElementOperations . For more information, see
Customizing Copy Behavior. You might also be able to customize the DSL definition.

In this topic
The first two sections describe alternative methods of defining a gesture handler:
Defining Gesture Handlers by Overriding ShapeElement methods. OnDragDrop , OnDoubleClick ,
OnDragOver , and other methods can be overridden.

Defining Gesture Handlers by using MEF. Use this method if you want third-party developers to be
able to define their own handlers to your DSL. Users can choose to install the third-party
extensions after they have installed your DSL.
How to Decode the Dragged Item. Elements can be dragged from any window or from the desktop, as
well as from a DSL.
How to Get the Original Dragged Item. If the dragged item is a DSL element, you can open the source
model and access the element.
Using Mouse Actions: Dragging Compartment Items. This sample demonstrates a lower-level handler that
intercepts mouse actions on a shape's fields. The example lets the user re-order the items in a
compartment by dragging with the mouse.

Defining Gesture Handlers by Overriding ShapeElement Methods


Add a new code file to your DSL project. For a gesture handler, you usually must have at least the following
using statements:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;

In the new file, define a partial class for the shape or diagram class that should respond to the drag operation.
Override the following methods:
OnDragOver- This method is called when the mouse pointer enters the shape during a drag operation.
Your method should inspect the item that the user is dragging, and set the Effect property to indicate
whether the user can drop the item on this shape. The Effect property determines the appearance of the
cursor while it is over this shape, and also determines whether OnDragDrop() will be called when the user
releases the mouse button.
partial class MyShape // MyShape generated from DSL Definition.
{
public override void OnDragOver(DiagramDragEventArgs e)
{
base.OnDragOver(e);
if (e.Effect == System.Windows.Forms.DragDropEffects.None
&& IsAcceptableDropItem(e)) // To be defined
{
e.Effect = System.Windows.Forms.DragDropEffects.Copy;
}
}

OnDragDrop - This method is called if the user releases the mouse button while the mouse pointer rests
over this shape or diagram, if OnDragOver(DiagramDragEventArgs e) previously set e.Effect to a value
other than None .

public override void OnDragDrop(DiagramDragEventArgs e)


{
if (!IsAcceptableDropItem(e))
{
base.OnDragDrop(e);
}
else
{ // Process the dragged item, for example merging a copy into the diagram
ProcessDragDropItem(e); // To be defined
}
}

OnDoubleClick - This method is called when the user double-clicks the shape or diagram.
For more information, see How to: Intercept a Click on a Shape or Decorator.
Define IsAcceptableDropItem(e) to determine whether the dragged item is acceptable, and
ProcessDragDropItem(e) to update your model when the item is dropped. These methods must first
extract the item from the event arguments. For information about how to do that, see How to get a
reference to the dragged item.

Defining Gesture Handlers by using MEF


MEF (Managed Extensibility Framework) lets you define components that can be installed with minimal
configuration. For more information, see Managed Extensibility Framework (MEF).
To define a MEF gesture handler
1. Add to your Dsl and DslPackage projects the MefExtension files that are described in Extend your DSL
by using MEF.
2. You can now define a gesture handler as a MEF component:
// This attribute is defined in the generated file
// DslPackage\MefExtension\DesignerExtensionMetaDataAttribute.cs:
[MyDslGestureExtension]
public class MyGestureHandlerClassName : IGestureExtension
{
/// <summary>
/// Called to determine whether a drag onto the diagram can be accepted.
/// </summary>
/// <param name="diagramDragEventArgs">Contains a link to the item that is being dragged</param>
/// <param name="targetMergeElement">The shape or connector that the mouse is over</param>
/// <returns>True if the item can be accepted on the targetMergeElement.</returns>
public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs)
{
MyShape target = targetMergeElement as MyShape;
if (target == null) return false;
if (target.IsAcceptableDropItem(diagramDragEventArgs)) return true;
return false;
}
public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
{
MyShape target = targetMergeElement as MyShape;
if (target == null || ! target.IsAcceptableDropItem(diagramDragEventArgs)) return;
// Process the dragged item, for example merging a copy into the diagram:
target.ProcessDragDropItem(diagramDragEventArgs);
}

You can create more than one gesture handler component, such as when you have different types of
dragged objects.
3. Add partial class definitions for the target shape, connector or diagram classes, and define the methods
IsAcceptableDropItem() and ProcessDragDropItem() . These methods must begin by extracting the
dragged item from the event arguments. For more information, see How to get a reference to the dragged
item.

How to decode the dragged item


When the user drags an item onto your diagram, or from one part of your diagram to another, information
about the item that is being dragged is available in DiagramDragEventArgs . Because the drag operation could
have started at any object on the screen, the data can be available in any one of a variety of formats. Your code
must recognize the formats with which it is capable of dealing.
To discover the formats in which your drag source information is available, run your code in debugging mode,
setting a breakpoint at the entry to OnDragOver() or CanDragDrop() . Inspect the values of the
DiagramDragEventArgs parameter. The information is provided in two forms:

IDataObject Data - This property carries serialized versions of the source objects, usually in more than
one format. Its most useful functions are:
diagramEventArgs.Data.GetDataFormats() - Lists the formats in which you can decode the dragged
object. For example, if the user drags a file from the desktop, the available formats include the file
name (" FileNameW ").
diagramEventArgs.Data.GetData(format) - Decodes the dragged object in the specified format. Cast
the object to the appropriate type. For example:
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;

You can also transmit objects such as model bus references from the source in your own custom
format. For more information, see How to Send Model Bus References in a Drag and Drop.
ElementGroupPrototype Prototype - Use this property if you want users to drag items from a DSL or a
UML model. An element group prototype contains one or more objects, links, and their property values. It
is also used in paste operations and when you are adding an element from the toolbox. In a prototype,
objects and their types are identified by Guid. For example, this code allows the user to drag class
elements from a UML diagram or UML Model Explorer:

private bool IsAcceptableDropItem(DiagramDragEventArgs e)


{
return e.Prototype != null && e.Prototype.RootProtoElements.Any(element =>
element.DomainClassId.ToString()
== "3866d10c-cc4e-438b-b46f-bb24380e1678"); // Accept UML class shapes.
// Or, from another DSL: SourceNamespace.SourceShapeClass.DomainClassId
}

To accept UML shapes, determine the Guids of the UML shape classes by experiment. Remember that
there is usually more than one type of element on any diagram. Remember also that an object dragged
from a DSL or UML diagram is the shape, not the model element.
DiagramDragEventArgs also has properties that indicate the current mouse pointer position and whether
the user is pressing the CTRL, ALT, or SHIFT keys.

How to get the original of a dragged element


The Data and Prototype properties of the event arguments contain only a reference to the dragged shape.
Usually, if you want to create an object in the target DSL that is derived from the prototype in some way, you
need to obtain access to the original, for example, reading the file contents, or navigating to the model element
represented by a shape. You can use Visual Studio Model Bus to help with this.
To prepare a DSL project for Model Bus
1. Make the source DSL accessible by Visual Studio Model Bus:
a. Download and install the Visual Studio Model Bus extension, if it is not already installed. For more
information, see Visualization and Modeling SDK.
b. Open the DSL definition file of the source DSL in DSL Designer. Right-click the design surface and
then click Enable Modelbus. In the dialog box, choose one or both of the options. Click OK. A new
project "ModelBus" is added to the DSL solution.
c. Click Transform All Templates and rebuild the solution.
To send an object from a source DSL
1. In your ElementOperations subclass, override Copy() so that it encodes a Model Bus Reference (MBR)
into the IDataObject. This method will be called when the user starts to drag from the source diagram. The
encoded MBR will then be available in the IDataObject when the user drops in the target diagram.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Shell;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Integration;
using Microsoft.VisualStudio.Modeling.Integration.Shell;
using System.Drawing; // PointF
using System.Collections.Generic; // ICollection
using System.Windows.Forms; // for IDataObject
...
public class MyElementOperations : DesignSurfaceElementOperations
{
public override void Copy(System.Windows.Forms.IDataObject data,
System.Collections.Generic.ICollection<ModelElement> elements, ClosureType closureType,
System.Drawing.PointF sourcePosition)
{
base.Copy(data, elements, closureType, sourcePosition);

// Get the ModelBus service:


IModelBus modelBus =
this.Store.GetService(typeof(SModelBus)) as IModelBus;
DocData docData = ((VSDiagramView)this.Diagram.ActiveDiagramView).DocData;
string modelFile = docData.FileName;
// Get an adapterManager for the target DSL:
ModelBusAdapterManager manager =
(modelBus.FindAdapterManagers(modelFile).First());
ModelBusReference modelReference = manager.CreateReference(modelFile);
ModelBusReference elementReference = null;
using (ModelBusAdapter adapter = modelBus.CreateAdapter(modelReference))
{
elementReference = adapter.GetElementReference(elements.First());
}

data.SetData("ModelBusReference", elementReference);
}
...}

To receive a Model Bus Reference from a DSL in a target DSL or UML project
1. In the target DSL project, add project references to:
The source Dsl project.
The source ModelBus project.
2. In the gesture handler code file, add the following namespace references:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.Integration;
using SourceDslNamespace;
using SourceDslNamespace.ModelBusAdapters;

3. The following sample illustrates how to get access to the source model element:
partial class MyTargetShape // or diagram or connector
{
internal void ProcessDragDropItem(DiagramDragEventArgs diagramDragEventArgs)
{
// Verify that we're being passed an Object Shape.
ElementGroupPrototype prototype = diagramDragEventArgs.Prototype;
if (prototype == null) return;
if (Company.InstanceDiagrams.ObjectShape.DomainClassId
!= prototype.RootProtoElements.First().DomainClassId)
return;
// - This is an ObjectShape.
// - We need to access the originating Store, find the shape, and get its object.

IModelBus modelBus = targetDropElement.Store.GetService(typeof(SModelBus)) as IModelBus;

// Unpack the MBR that was packed in Copy:


ModelBusReference reference = diagramDragEventArgs.Data.GetData("ModelBusReference") as
ModelBusReference;
using (SourceDslAdapter adapter = modelBus.CreateAdapter(reference) as SourceDslAdapter)
{
using (ILinkedUndoTransaction t = LinkedUndoContext.BeginTransaction("doing things"))
{
// Quickest way to get the shape from the MBR:
ObjectShape firstShape = adapter.ResolveElementReference<ObjectShape>(reference);

// But actually there might be several shapes - so get them from the prototype instead:
IElementDirectory remoteDirectory = adapter.Store.ElementDirectory;
foreach (Guid shapeGuid in prototype.SourceRootElementIds)
{
PresentationElement pe = remoteDirectory.FindElement(shapeGuid) as PresentationElement;
if (pe == null) continue;
SourceElement instance = pe.ModelElement as SourceElement;
if (instance == null) continue;

// Do something with the object:


instance...
}
t.Commit();
}
}
}

To accept an element sourced from a UML model


The following code sample accepts an object dropped from a UML diagram.
using Microsoft.VisualStudio.ArchitectureTools.Extensibility;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Uml.Classes;
using System;
using System.ComponentModel.Composition;
using System.Linq;
...
partial class TargetShape
{
internal void ProcessDragDropItem(DiagramDragEventArgs diagramDragEventArgs)
{
EnvDTE.DTE dte = this.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
// Find the UML project
foreach (EnvDTE.Project project in dte.Solution.Projects)
{
IModelingProject modelingProject = project as IModelingProject;
if (modelingProject == null) continue; // not a modeling project
IModelStore store = modelingProject.Store;
if (store == null) return;

foreach (IDiagram dd in store.Diagrams())


{
// Get Modeling.Diagram that implements UML.IDiagram:
Diagram diagram = dd.GetObject<Diagram>();

foreach (Guid elementId in e.Prototype.SourceRootElementIds)


{
ShapeElement shape = diagram.Partition.ElementDirectory.FindElement(elementId) as
ShapeElement;
if (shape == null) continue;
// This example assumes the shape is a UML class:
IClass classElement = shape.ModelElement as IClass;
if (classElement == null) continue;

// Now do something with the UML class element ...


}
}
break; // don't try any more projects
} } }

Using Mouse Actions: Dragging Compartment Items


You can write a handler that intercepts mouse actions on a shape's fields. The following example lets the user re-
order the items in a compartment by dragging with the mouse.
To build this example, create a solution by using the Class Diagrams solution template. Add a code file and add
the following code. Adjust the namespace to be the same as your own.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;

// This sample allows users to re-order items in a compartment shape by dragging.

// This example is built on the "Class Diagrams" solution template of VMSDK (DSL Tools).
// You will need to change the following domain class names to your own:
// ClassShape = a compartment shape
// ClassModelElement = the domain class displayed using a ClassShape
// ClassModelElement = the domain class displayed using a ClassShape
// This code assumes that the embedding relationships displayed in the compartments
// don't use inheritance (don't have base or derived domain relationships).

namespace Company.CompartmentDrag // EDIT.


{
/// <summary>
/// Manage the mouse while dragging a compartment item.
/// </summary>
public class CompartmentDragMouseAction : MouseAction
{
private ModelElement sourceChild;
private ClassShape sourceShape;
private RectangleD sourceCompartmentBounds;

public CompartmentDragMouseAction(ModelElement sourceChildElement, ClassShape sourceParentShape,


RectangleD bounds)
: base (sourceParentShape.Diagram)
{
sourceChild = sourceChildElement;
sourceShape = sourceParentShape;
sourceCompartmentBounds = bounds; // For cursor.
}

/// <summary>
/// Call back to the source shape to drop the dragged item.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
sourceShape.DoMouseUp(sourceChild, e);
this.Cancel(e.DiagramClientView);
e.Handled = true;
}

/// <summary>
/// Ideally, this shouldn't happen. This action should only be active
/// while the mouse is still pressed. However, it can happen if you
/// move the mouse rapidly out of the source shape, let go, and then
/// click somewhere else in the source shape. Yuk.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(DiagramMouseEventArgs e)
{
base.OnMouseDown(e);
this.Cancel(e.DiagramClientView);
e.Handled = false;
}

/// <summary>
/// Display an appropriate cursor while the drag is in progress:
/// Up-down arrow if we are inside the original compartment.
/// No entry if we are elsewhere.
/// </summary>
/// <param name="currentCursor"></param>
/// <param name="diagramClientView"></param>
/// <param name="mousePosition"></param>
/// <returns></returns>
public override System.Windows.Forms.Cursor GetCursor(System.Windows.Forms.Cursor currentCursor,
DiagramClientView diagramClientView, PointD mousePosition)
{
// If the cursor is inside the original compartment, show up-down cursor.
return sourceCompartmentBounds.Contains(mousePosition)
? System.Windows.Forms.Cursors.SizeNS // Up-down arrow.
: System.Windows.Forms.Cursors.No;
}
}
/// <summary>
/// Override some methods of the compartment shape.
/// *** GenerateDoubleDerived must be set for this shape in DslDefinition.dsl. ****
/// </summary>
public partial class ClassShape
{
/// <summary>
/// Model element that is being dragged.
/// </summary>
private static ClassModelElement dragStartElement = null;
/// <summary>
/// Absolute bounds of the compartment, used to set the cursor.
/// </summary>
private static RectangleD compartmentBounds;

/// <summary>
/// Attach mouse listeners to the compartments for the shape.
/// This is called once per compartment shape.
/// The base method creates the compartments for this shape.
/// </summary>
public override void EnsureCompartments()
{
base.EnsureCompartments();
foreach (Compartment compartment in this.NestedChildShapes.OfType<Compartment>())
{
compartment.MouseDown += new DiagramMouseEventHandler(compartment_MouseDown);
compartment.MouseUp += new DiagramMouseEventHandler(compartment_MouseUp);
compartment.MouseMove += new DiagramMouseEventHandler(compartment_MouseMove);
}
}

/// <summary>
/// Remember which item the mouse was dragged from.
/// We don't create an Action immediately, as this would inhibit the
/// inline text editing feature. Instead, we just remember the details
/// and will create an Action when/if the mouse moves off this list item.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseDown(object sender, DiagramMouseEventArgs e)
{
dragStartElement = e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>().FirstOrDefault();
compartmentBounds = e.HitDiagramItem.Shape.AbsoluteBoundingBox;
}

/// <summary>
/// When the mouse moves away from the initial list item, but still inside the compartment,
/// create an Action to supervise the cursor and handle subsequent mouse events.
/// Transfer the details of the initial mouse position to the Action.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseMove(object sender, DiagramMouseEventArgs e)
{
if (dragStartElement != null)
{
if (dragStartElement != e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>
().FirstOrDefault())
{
e.DiagramClientView.ActiveMouseAction = new CompartmentDragMouseAction(dragStartElement, this,
compartmentBounds);
dragStartElement = null;
}
}
}

/// <summary>
/// User has released the mouse button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseUp(object sender, DiagramMouseEventArgs e)
{
dragStartElement = null;
}

/// <summary>
/// Forget the source item if mouse up occurs outside the
/// compartment.
/// </summary>
/// <param name="e"></param>
public override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
dragStartElement = null;
}

/// <summary>
/// Called by the Action when the user releases the mouse.
/// If we are still on the same compartment but in a different list item,
/// move the starting item to the position of the current one.
/// </summary>
/// <param name="dragFrom"></param>
/// <param name="e"></param>
public void DoMouseUp(ModelElement dragFrom, DiagramMouseEventArgs e)
{
// Original or "from" item:
ClassModelElement dragFromElement = dragFrom as ClassModelElement;
// Current or "to" item:
ClassModelElement dragToElement = e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>
().FirstOrDefault();
if (dragFromElement != null && dragToElement != null)
{
// Find the common parent model element, and the relationship links:
ElementLink parentToLink = GetEmbeddingLink(dragToElement);
ElementLink parentFromLink = GetEmbeddingLink(dragFromElement);
if (parentToLink != parentFromLink && parentFromLink != null && parentToLink != null)
{
// Get the static relationship and role (= end of relationship):
DomainRelationshipInfo relationshipFrom = parentFromLink.GetDomainRelationship();
DomainRoleInfo parentFromRole = relationshipFrom.DomainRoles[0];
// Get the node in which the element is embedded, usually the element displayed in the shape:
ModelElement parentFrom = parentFromLink.LinkedElements[0];

// Same again for the target:


DomainRelationshipInfo relationshipTo = parentToLink.GetDomainRelationship();
DomainRoleInfo parentToRole = relationshipTo.DomainRoles[0];
ModelElement parentTo = parentToLink.LinkedElements[0];

// Mouse went down and up in same parent and same compartment:


if (parentTo == parentFrom && relationshipTo == relationshipFrom)
{
// Find index of target position:
int newIndex = 0;
var elementLinks = parentToRole.GetElementLinks(parentTo);
foreach (ElementLink link in elementLinks)
{
if (link == parentToLink) { break; }
newIndex++;
}

if (newIndex < elementLinks.Count)


{
using (Transaction t = parentFrom.Store.TransactionManager.BeginTransaction("Move list item"))
{
parentFromLink.MoveToIndex(parentFromRole, newIndex);
t.Commit();
}
}
}
}
}
}
}

/// <summary>
/// Get the embedding link to this element.
/// Assumes there is no inheritance between embedding relationships.
/// (If there is, you need to make sure you've got the relationship
/// that is represented in the shape compartment.)
/// </summary>
/// <param name="child"></param>
/// <returns></returns>
ElementLink GetEmbeddingLink(ClassModelElement child)
{
foreach (DomainRoleInfo role in child.GetDomainClass().AllEmbeddedByDomainRoles)
{
foreach (ElementLink link in role.OppositeDomainRole.GetElementLinks(child))
{
// Just the assume the first embedding link is the only one.
// Not a valid assumption if one relationship is derived from another.
return link;
}
}
return null;
}
}
}

See Also
Customizing Copy Behavior
Deploying Domain-Specific Language Solutions

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
How to: Intercept a Click on a Shape or Decorator
10/18/2017 9 min to read Edit Online

The following procedures demonstrate how to intercept a click on a shape or an icon decorator. You can intercept
clicks, double-clicks, drags, and other gestures, and make the element respond.

To Intercept Clicks on Shapes


In the Dsl project, in a code file that is separate from the generated code files, write a partial class definition for the
shape class. Override OnDoubleClick() or one of the other methods that has a name beginning with On... . For
example:

public partial class MyShape // change


{
public override void OnDoubleClick(DiagramPointEventArgs e)
{
base.OnDoubleClick(e);
System.Windows.Forms.MessageBox.Show("Click");
e.Handled = true;
} }

NOTE
Set e.Handled to true , unless you want the event to be passed to the containing shape or diagram.

To Intercept Clicks on Decorators


Image decorators are carried on an instance of ImageField class, which has an OnDoubleClick method. You can
intercept the clicks if you write an ImageField subclass. The fields are set up in the InitializeShapeFields method.
Therefore, you must change that method to instantiate your subclass instead of the regular ImageField. The
InitializeShapeFields method is in the generated code of the shape class. You can override the shape class if you
set its Generates Double Derived property as described in the following procedure.
Although InitializeShapeFields is an instance method, it is called only once for each class. Therefore, only one
instance of ClickableImageField exists for each field in each class, not one instance for each shape in the diagram.
When the user double-clicks an instance, you must identify which instance has been hit, as the code in the example
demonstrates.
To intercept a click on an icon decorator
1. Open or create a DSL solution.
2. Choose or create a shape that has an icon decorator, and map it to a domain class.
3. In a code file that is separate from the files in the GeneratedCode folder, create the new subclass of
ImageField:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;

namespace Fabrikam.MyDsl { // Change to your namespace


internal class ClickableImageField : ImageField
{
// You can also override OnClick and so on.
public override void OnDoubleClick(DiagramPointEventArgs e)
{
base.OnDoubleClick(e);
// Work out which instance was hit.
MyShape shapeHit = e.HitDiagramItem.Shape as MyShape;
if (shapeHit != null)
{
MyDomainClass element =
shapeHit.ModelElement as MyDomainClass;
System.Windows.Forms.MessageBox.Show(
"Double click on shape for " + element.Name);
// If we do not set Handled, the event will
// be passed to the containing shape:
e.Handled = true;
}
}

public ClickableImageField(string fieldName)


: base(fieldName)
{ }
}

You should set Handled to true if you do not want the event to be passed to the containing shape.
4. Override the InitializeShapeFields method in your shape classs by adding the following partial class
definition.

public partial class MyShape // change


{
protected override void InitializeShapeFields
(IList<ShapeField> shapeFields)
{
base.InitializeShapeFields(shapeFields);
// You can see the above method in MyShapeBase
// in the generated Shapes.cs
// It has already added fields for the Icons.
// So you will have to retrieve them and replace with your own.
ShapeField unwantedField = shapeFields.First
(field => field.Name == "IconDecorator1");
shapeFields.Remove(unwantedField);

// Now replicate the generated code from the base class


// in Shape.cs, but with your own image constructor.
ImageField field2 = new ClickableImageField("IconDecorator1");
field2.DefaultImage = ImageHelper.GetImage(
MyDslDomainModel.SingletonResourceManager
.GetObject("MyShapeIconDecorator1DefaultImage"));
shapeFields.Add(field2);
}

5. Build and run the solution.


6. Double-click the icon on an instance of the shape. Your test message should appear.
Intercepting clicks and drags on CompartmentShape lists
The following sample allows users to re-order items in a compartment shape by dragging them. To run this code:
1. Create a new DSL solution by using the Class Diagrams solution template.
You can also work with a solution of your own that contains compartment shapes. This code assumes that
there is an embedding relationship between the model elements represented by the shape, and the
elements represented in the compartment list items.
2. Set the Generates Double Derived property of the compartment shape.
3. Add this code in a file in the Dsl project.
4. Adjust the domain class and shape names in this code to match your own DSL.
In summary, the code works as follows. In this example, ClassShape is the name of the compartment shape.
A set of mouse event handlers is attached to each compartment instance when it is created.
The ClassShape.MouseDown event stores the current item.
When the mouse moves out of the current item, an instance of MouseAction is created, which sets the
cursor and captures the mouse until it is released.
To avoid interfering with other mouse actions, such as selecting the text of an item, the MouseAction is not
created until the mouse has left the original item.
An alternative to creating a MouseAction would be simply to listen for MouseUp. However, this would not
work properly if the user releases the mouse after dragging it outside the compartment. The MouseAction
is able to perform the appropriate action no matter where the mouse is released.
When the mouse is released, MouseAction.MouseUp rearranges the order of the links between the model
elements.
The change of role order fires a rule that updates the display. This behavior is already defined, and no
additional code is required.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;

// This sample allows users to re-order items in a compartment shape by dragging.

// This example is built on the "Class Diagrams" solution template of VMSDK (DSL Tools).
// You will need to change the following domain class names to your own:
// ClassShape = a compartment shape
// ClassModelElement = the domain class displayed using a ClassShape
// This code assumes that the embedding relationships
// displayed in the compartments don't use inheritance
// (don't have base or derived domain relationships).

namespace Company.CompartmentDrag
{
/// <summary>
/// Manage the mouse while dragging a compartment item.
/// </summary>
public class CompartmentDragMouseAction : MouseAction
{
private ModelElement sourceChild;
private ClassShape sourceShape;
private RectangleD sourceCompartmentBounds;
private RectangleD sourceCompartmentBounds;

public CompartmentDragMouseAction(ModelElement sourceChildElement, ClassShape sourceParentShape, RectangleD


bounds)
: base (sourceParentShape.Diagram)
{
sourceChild = sourceChildElement;
sourceShape = sourceParentShape;
sourceCompartmentBounds = bounds; // For cursor.
}

/// <summary>
/// Call back to the source shape to drop the dragged item.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
sourceShape.DoMouseUp(sourceChild, e);
this.Cancel(e.DiagramClientView);
e.Handled = true;
}

/// <summary>
/// Ideally, this shouldn't happen. This action should only be active
/// while the mouse is still pressed. However, it can happen if you
/// move the mouse rapidly out of the source shape, let go, and then
/// click somewhere else in the source shape.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(DiagramMouseEventArgs e)
{
base.OnMouseDown(e);
this.Cancel(e.DiagramClientView);
e.Handled = false;
}

/// <summary>
/// Display an appropriate cursor while the drag is in progress:
/// Up-down arrow if we are inside the original compartment.
/// No entry if we are elsewhere.
/// </summary>
/// <param name="currentCursor"></param>
/// <param name="diagramClientView"></param>
/// <param name="mousePosition"></param>
/// <returns></returns>
public override System.Windows.Forms.Cursor GetCursor(System.Windows.Forms.Cursor currentCursor,
DiagramClientView diagramClientView, PointD mousePosition)
{
// If the cursor is inside the original compartment, show up-down cursor.
return sourceCompartmentBounds.Contains(mousePosition)
? System.Windows.Forms.Cursors.SizeNS // Up-down arrow.
: System.Windows.Forms.Cursors.No;
}
}

/// <summary>
/// Override some methods of the compartment shape.
/// *** GenerateDoubleDerived must be set for this shape in DslDefinition.dsl. ****
/// </summary>
public partial class ClassShape
{
/// <summary>
/// Model element that is being dragged.
/// </summary>
private static ClassModelElement dragStartElement = null;
/// <summary>
/// Absolute bounds of the compartment, used to set the cursor.
/// </summary>
private static RectangleD compartmentBounds;
private static RectangleD compartmentBounds;

/// <summary>
/// Attach mouse listeners to the compartments for the shape.
/// This is called once per compartment shape.
/// The base method creates the compartments for this shape.
/// </summary>
public override void EnsureCompartments()
{
base.EnsureCompartments();
foreach (Compartment compartment in this.NestedChildShapes.OfType<Compartment>())
{
compartment.MouseDown += new DiagramMouseEventHandler(compartment_MouseDown);
compartment.MouseUp += new DiagramMouseEventHandler(compartment_MouseUp);
compartment.MouseMove += new DiagramMouseEventHandler(compartment_MouseMove);
}
}

/// <summary>
/// Remember which item the mouse was dragged from.
/// We don't create an Action immediately, as this would inhibit the
/// inline text editing feature. Instead, we just remember the details
/// and will create an Action when/if the mouse moves off this list item.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseDown(object sender, DiagramMouseEventArgs e)
{
dragStartElement = e.HitDiagramItem.RepresentedElements
.OfType<ClassModelElement>().FirstOrDefault();
compartmentBounds = e.HitDiagramItem.Shape.AbsoluteBoundingBox;
}

/// <summary>
/// When the mouse moves away from the initial list item,
/// but still inside the compartment, create an Action
/// to supervise the cursor and handle subsequent mouse events.
/// Transfer the details of the initial mouse position to the Action.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseMove(object sender, DiagramMouseEventArgs e)
{
if (dragStartElement != null)
{
if (dragStartElement != e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>().FirstOrDefault())
{
e.DiagramClientView.ActiveMouseAction = new CompartmentDragMouseAction(dragStartElement, this,
compartmentBounds);
dragStartElement = null;
}
}
}

/// <summary>
/// User has released the mouse button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseUp(object sender, DiagramMouseEventArgs e)
{
dragStartElement = null;
}

/// <summary>
/// Forget the source item if mouse up occurs outside the
/// compartment.
/// </summary>
/// <param name="e"></param>
public override void OnMouseUp(DiagramMouseEventArgs e)
public override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
dragStartElement = null;
}

/// <summary>
/// Called by the Action when the user releases the mouse.
/// If we are still on the same compartment but in a different list item,
/// move the starting item to the position of the current one.
/// </summary>
/// <param name="dragFrom"></param>
/// <param name="e"></param>
public void DoMouseUp(ModelElement dragFrom, DiagramMouseEventArgs e)
{
// Original or "from" item:
ClassModelElement dragFromElement = dragFrom as ClassModelElement;
// Current or "to" item:
ClassModelElement dragToElement = e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>
().FirstOrDefault();
if (dragFromElement != null && dragToElement != null)
{
// Find the common parent model element, and the relationship links:
ElementLink parentToLink = GetEmbeddingLink(dragToElement);
ElementLink parentFromLink = GetEmbeddingLink(dragFromElement);
if (parentToLink != parentFromLink && parentFromLink != null && parentToLink != null)
{
// Get the static relationship and role (= end of relationship):
DomainRelationshipInfo relationshipFrom = parentFromLink.GetDomainRelationship();
DomainRoleInfo parentFromRole = relationshipFrom.DomainRoles[0];
// Get the node in which the element is embedded, usually the element displayed in the shape:
ModelElement parentFrom = parentFromLink.LinkedElements[0];

// Same again for the target:


DomainRelationshipInfo relationshipTo = parentToLink.GetDomainRelationship();
DomainRoleInfo parentToRole = relationshipTo.DomainRoles[0];
ModelElement parentTo = parentToLink.LinkedElements[0];

// Mouse went down and up in same parent and same compartment:


if (parentTo == parentFrom && relationshipTo == relationshipFrom)
{
// Find index of target position:
int newIndex = 0;
var elementLinks = parentToRole.GetElementLinks(parentTo);
foreach (ElementLink link in elementLinks)
{
if (link == parentToLink) { break; }
newIndex++;
}

if (newIndex < elementLinks.Count)


{
using (Transaction t = parentFrom.Store.TransactionManager.BeginTransaction("Move list item"))
{
parentFromLink.MoveToIndex(parentFromRole, newIndex);
t.Commit();
}
}
}
}
}
}

/// <summary>
/// Get the embedding link to this element.
/// Assumes there is no inheritance between embedding relationships.
/// (If there is, you need to make sure you've got the relationship
/// that is represented in the shape compartment.)
/// </summary>
/// <param name="child"></param>
/// <param name="child"></param>
/// <returns></returns>
ElementLink GetEmbeddingLink(ClassModelElement child)
{
foreach (DomainRoleInfo role in child.GetDomainClass().AllEmbeddedByDomainRoles)
{
foreach (ElementLink link in role.OppositeDomainRole.GetElementLinks(child))
{
// Just the assume the first embedding link is the only one.
// Not a valid assumption if one relationship is derived from another.
return link;
}
}
return null;
}
}
}

See Also
Responding to and Propagating Changes
Properties of Decorators
How to: Access and Constrain the Current Selection
10/18/2017 5 min to read Edit Online

When you write a command or gesture handler for your domain-specific language, you can determine what
element the user right-clicked. You can also prevent some shapes or fields from being selected. For example, you
can arrange that when the user clicks an icon decorator, the shape that contains it is selected instead. Constraining
the selection in this manner reduces the number of handlers that you have to write. It also makes it easier for the
user, who can click anywhere in the shape without having to avoid the decorator.

Accessing the Current Selection from a Command Handler


The command set class for a domain-specific language contains the command handlers for your custom
commands. The CommandSet class, from which the command set class for a domain-specific language derives,
provides a few members for accessing the current selection.
Depending on the command, the command handler might need the selection in the model designer, the model
explorer, or the active window.
To access selection information
1. The CommandSet class defines the following members that can be used to access the current selection.

MEMBER DESCRIPTION

IsAnyDocumentSelectionCompartment method Returns true if any of the elements selected in the


model designer is a compartment shape; otherwise,
false .

IsDiagramSelected method Returns true if the diagram is selected in the model


designer; otherwise, false .

IsSingleDocumentSelection method Returns true if exactly one element is selected in the


model designer; otherwise, false .

IsSingleSelection method Returns true if exactly one element is selected in the


active window; otherwise, false .

CurrentDocumentSelection property Gets a read-only collection of the elements selected in the


model designer.

CurrentSelection property Gets a read-only collection of the elements selected in the


active window.

SingleDocumentSelection property Gets the primary element of the selection in the model
designer.

SingleSelection property Gets the primary element of the selection in the active
window.

2. The CurrentDocView property of the CommandSet class provides access to the DiagramDocView object that
represents the model designer window and provides additional access the selected elements in the model
designer.
3. In addition, the generated code defines an explorer tool window property and an explorer selection property
in the command set class for the domain-specific language.
The explorer tool window property returns an instance of the explorer tool window class for the
domain-specific language. The explorer tool window class derives from the
ModelExplorerToolWindow class and represents the model explorer for the domain-specific
language.
The ExplorerSelection property returns the selected element in the model explorer window for the
domain-specific language.

Determining which window is active


The IMonitorSelectionService interface contains defines members that provide access to the current selection state
in the shell. You can get an IMonitorSelectionService object from either the package class or the command set class
for the domain-specific language through the MonitorSelection property defined in the base class of each. The
package class derives from the ModelingPackage class, and the command set class derives from the CommandSet
class.
To determine from a command handler what type of window is active
1. The MonitorSelection property of the CommandSet class returns an IMonitorSelectionService object that
provides access to the current selection state in the shell.
2. The CurrentSelectionContainer property of the IMonitorSelectionService interface gets the active selection
container, which can be different from the active window.
3. Add the following properties to the command set class for you domain-specific language to determine what
type of window is active.

// using Microsoft.VisualStudio.Modeling.Shell;

// Returns true if the model designer is the active selection container;


// otherwise, false.
protected bool IsDesignerActive
{
get
{
return (this.MonitorSelection.CurrentSelectionContainer
is DiagramDocView);
}
}

// Returns true if the model explorer is the active selection container;


// otherwise, false.
protected bool IsExplorerActive
{
get
{
return (this.MonitorSelection.CurrentSelectionContainer
is ModelExplorerToolWindow);
}
}

Constraining the Selection


By adding selection rules, you can control which elements are selected when the user selects an element in the
model. For example, to allow the user to treat a number of elements as a single unit, you can use a selection rule.
To create a selection rule
1. Create a custom code file in the DSL project
2. Define a selection rule class that is derived from the DiagramSelectionRules class.
3. Override the GetCompliantSelection method of the selection rule class to apply the selection criteria.
4. Add a partial class definition for the ClassDiagram class to your custom code file.
The ClassDiagram class derives from the Diagram class and is defined in the generated code file, Diagram.cs,
in the DSL project.
5. Override the SelectionRules property of the ClassDiagram class to return the custom selection rule.
The default implementation of the SelectionRules property gets a selection rule object that does not modify
the selection.
Example
The following code file creates a selection rule that expands the selection to include all instances of each of the
domain shapes that was initially selected.

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace CompanyName.ProductName.GroupingDsl
{
public class CustomSelectionRules : DiagramSelectionRules
{
protected Diagram diagram;
protected IElementDirectory elementDirectory;

public CustomSelectionRules(Diagram diagram)


{
if (diagram == null) throw new ArgumentNullException();

this.diagram = diagram;
this.elementDirectory = diagram.Store.ElementDirectory;
}

/// <summary>Called by the design surface to allow selection filtering.


/// </summary>
/// <param name="currentSelection">[in] The current selection before any
/// ShapeElements are added or removed.</param>
/// <param name="proposedItemsToAdd">[in/out] The proposed DiagramItems to
/// be added to the selection.</param>
/// <param name="proposedItemsToRemove">[in/out] The proposed DiagramItems
/// to be removed from the selection.</param>
/// <param name="primaryItem">[in/out] The proposed DiagramItem to become
/// the primary DiagramItem of the selection. A null value signifies that
/// the last DiagramItem in the resultant selection should be assumed as
/// the primary DiagramItem.</param>
/// <returns>true if some or all of the selection was accepted; false if
/// the entire selection proposal was rejected. If false, appropriate
/// feedback will be given to the user to indicate that the selection was
/// rejected.</returns>
public override bool GetCompliantSelection(
SelectedShapesCollection currentSelection,
DiagramItemCollection proposedItemsToAdd,
DiagramItemCollection proposedItemsToRemove,
DiagramItem primaryItem)
{
if (currentSelection.Count == 0 && proposedItemsToAdd.Count == 0) return true;

HashSet<DomainClassInfo> itemsToAdd = new HashSet<DomainClassInfo>();


foreach (DiagramItem item in proposedItemsToAdd)
{
if (item.Shape != null)
itemsToAdd.Add(item.Shape.GetDomainClass());
}
proposedItemsToAdd.Clear();
foreach (DomainClassInfo classInfo in itemsToAdd)
{
foreach (ModelElement element
in this.elementDirectory.FindElements(classInfo, false))
{
if (element is ShapeElement)
{
proposedItemsToAdd.Add(
new DiagramItem((ShapeElement)element));
}
}
}

return true;
}
}

public partial class ClassDiagram


{
protected CustomSelectionRules customSelectionRules = null;

protected bool multipleSelectionMode = true;

public override DiagramSelectionRules SelectionRules


{
get
{
if (multipleSelectionMode)
{
if (customSelectionRules == null)
{
customSelectionRules = new CustomSelectionRules(this);
}
return customSelectionRules;
}
else
{
return base.SelectionRules;
}
}
}
}
}

See Also
CommandSet
ModelingPackage
DiagramDocView
ModelExplorerToolWindow
IMonitorSelectionService
DiagramSelectionRules
Diagram
Customizing Element Tools
10/18/2017 1 min to read Edit Online

In some DSL Definitions, you represent a single concept as a group of elements. For example, if you create a model
in which a component has a fixed set of ports, you always want the ports to be created at the same time as their
parent component. Therefore, you have to customize the element creation tool so that it creates a group of
elements instead of just one. To achieve this, you can customize how the element creation tool is initialized.
You can also override what happens when the tool is dragged onto the diagram or an element.

Customizing the Content of an Element Tool


Each element tool stores an instance of an ElementGroupPrototype (EGP), which contains a serialized version of
one or more model elements and links. By default, the EGP of an element tool contains one instance of the class
that you specify for the tool. You can change this by overriding YourLanguage
ToolboxHelper.CreateElementToolPrototype . This method is called when the DSL package is loaded.

A parameter of the method is the ID of the class that you specified in the DSL Definition. When the method is called
with the class that you are interested in, you can add extra elements into the EGP.
The EGP must include embedding links from one main element to the subsidiary elements. It can also include
reference links.
The following example creates a main element and two embedded elements. The main class is called Resistor, and
it has two embedding relationships to elements named Terminal. The embedding role properties are named
Terminal1 and Terminal2, and both have a multiplicity of 1..1.

using Microsoft.VisualStudio.Modeling; ...


public partial class CircuitDiagramToolboxHelper
{
protected override ElementGroupPrototype CreateElementToolPrototype(Store store, Guid domainClassId)
{
// A case for each tool to customize:
if (domainClassId == Resistor.DomainClassId)
{
// Set up the prototype elements and links:
Resistor resistor = new Resistor(store);
resistor.Terminal1 = new Terminal(store);
resistor.Terminal2 = new Terminal(store);
resistor.Terminal1.Name = "T1"; // embedding
resistor.Terminal2.Name = "T2"; // embedding
// We could also set up reference links.

// Create an element group prototype for the toolbox:


ElementGroup egp = new ElementGroup(store.DefaultPartition);
egp.AddGraph(resistor, true);
// We do not have to explicitly include embedded children.
return egp.CreatePrototype();
}
// Element tools for other classes:
else
return base.CreateElementToolPrototype(store, domainClassId);
}
}
See Also
Customizing Element Creation and Movement
Responding to and Propagating Changes
10/18/2017 3 min to read Edit Online

When an element is created, deleted or updated, you can write code that propagates the change to other parts of
the model, or to external resources such as files, databases, or other components.

In This Section
As a guideline, consider these techniques in the following order:

TECHNIQUE SCENARIOS FOR MORE INFORMATION

Define a Calculated domain property. A domain property whose value is Calculated and Custom Storage
calculated from other properties in the Properties
model. For example, a price that is the
sum of prices of related elements.

Define a Custom Storage domain A domain property stored in other Calculated and Custom Storage
property. parts of the model or externally. For Properties
example, you could parse an expression
string into a tree in the model.

Override change handlers such as Keep different elements in sync, and Domain Property Value Change
OnValueChanging and OnDeleting keep external values in sync with the Handlers
model.

Constrain values to defined ranges.

Called immediately before and after


property value and other changes. You
can terminate the change by throwing
an exception.

Rules You can define rules that are queued Rules Propagate Changes Within the
for execution just before the end of a Model
transaction in which a change has
happened. They are not executed on
Undo or Redo. Use them to keep one
part of the store in synch with another.

Store Events The modeling store provides Event Handlers Propagate Changes
notifications of events such as adding Outside the Model
or deleting an element or link, or
changing the value of a property. The
event is also executed on Undo and
Redo. Use store events to update
values that are not in the store.
TECHNIQUE SCENARIOS FOR MORE INFORMATION

.NET Events Shapes have event handlers that How to: Intercept a Click on a Shape or
respond to mouse clicks and other Decorator
gestures. You have to register for these
events for each object. Registration is
typically done in an override of
InitializeInstanceResources, and must
be done for each element.

These events usually occur outside a


transaction.

Bounds Rules A bounds rule is used specifically to BoundsRules Constrain Shape Location
constrain the bounds of a shape. and Size

Selection rules Selection rules specifically constrain How to: Access and Constrain the
what the user can select. Current Selection

OnAssocatedPropertyChanged Indicate the model elements' states Updating Shapes and Connectors to
using features of shapes and Reflect the Model
connectors such as shadow,
arrowheads, color, and line widths and
style.

Comparing Rules and Store Events


Change notifiers, rules, and events are run when changes occur in a model.
Rules are usually applied at the end transaction in which the change has occurred, and events are applied after
changes in a transaction are committed.
Use store events to synchronize the model with objects outside the Store, and rules to maintain consistency
within the Store.
Creating Custom Rules You create a custom rule as a derived class from an abstract rule. You must also
notify the framework about the custom rule. For more information, see Rules Propagate Changes Within
the Model.
Subscribing to Events Before you can subscribe to an event, create an event handler and delegate. Then
use the EventManagerDirectoryproperty to subscribe to the event. For more information, see Event
Handlers Propagate Changes Outside the Model.
Undoing Changes When you undo a transaction, events are raised, but rules are not applied. If a rule
changes a value and you undo that change, the value is reset to the original value during the undo action.
When an event is raised, you must manually change the value back to its original value. To learn more
about transactons and undo, see How to: Use Transactions to Update the Model.
Passing Event Arguments to Rules and Events Both events and rules are passed an EventArgs
parameter that has information about how the model changed.

See Also
How to: Intercept a Click on a Shape or Decorator
Writing Code to Customise a Domain-Specific Language
Calculated and Custom Storage Properties
10/18/2017 3 min to read Edit Online

All domain properties in a domain-specific language (DSL) can be displayed to the user on the diagram and in
your language explorer, and can be accessed by program code. However, properties differ in the way that their
values are stored.

Kinds of Domain Properties


In the DSL Definition, you can set the Kind of a domain property, as listed in the following table:

DOMAIN PROPERTY KIND DESCRIPTION

Standard (Default) A domain property that is saved in the store and serialized to
file.

Calculated A read-only domain property that is not saved in the store,


but is calculated from other values.

For example, Person.Age could be calculated from


Person.BirthDate .

You have to provide the code that performs the calculation.


Typically, you calculate the value from other domain
properties. However, you can also use external resources.

Custom Storage A domain property that is not saved directly in the store, but
can be both get and set.

You have to provide the methods that get and set the value.

For example, Person.FullAddress could be stored in


Person.StreetAddress , Person.City , and
Person.PostalCode .

You can also access external resources, for example to get


and set values from a database.

Your code should not set values in the store when


Store.InUndoRedoOrRollback is true. See Transactions and
Custom Setters.

Providing the code for a calculated or custom storage property


If you set the Kind of a domain property to Calculated or Custom Storage, you have to provide access methods.
When you build your solution, an error report will tell you what is required.
To define a Calculated or Custom Storage Property
1. In DslDefinition.dsl, select the domain property either in the diagram or in DSL Explorer.
2. In the Properties window, set the Kind field to Calculated or Custom Storage.
Make sure that you have also set its Type to what you want.
3. Click Transform All Templates in the toolbar of Solution Explorer.
4. On the Build menu, click Build Solution.
You receive the following error message: "YourClass does not contain a definition for GetYourProperty."
5. Double-click the error message.
Dsl\GeneratedCode\DomainClasses.cs or DomainRelationships.cs opens. Above the highlighted method
call, a comment prompts you to provide an implementation for GetYourProperty().

NOTE
This file is generated from DslDefinition.dsl. If you edit this file, your changes will be lost the next time that you click
Transform All Templates. Instead, add the required method in a separate file.

6. Create or open a class file in a separate folder, for example CustomCode\YourDomainClass.cs.


Make sure that the namespace is the same as in the generated code.
7. In the class file, write a partial implementation of the domain class. In the class, write a definition for the
missing Get method that resembles the following example:

namespace Company.FamilyTree
{ public partial class Person
{ int GetAgeValue()
{ return System.DateTime.Today.Year - this.BirthYear; }
} }

8. If you set Kind to Custom Storage, you will also have to provide a Set method. For example:

void SetAgeValue(int value)


{ if (!Store.InUndoRedoOrRollback)
this.BirthYear =
System.DateTime.Today.Year - value; }

Your code should not set values in the store when Store.InUndoRedoOrRollback is true. See Transactions
and Custom Setters.
9. Build and run the solution.
10. Test the property. Make sure that you try Undo and Redo.

Transactions and Custom Setters


In the Set method of Custom Storage property, you do not have to open a transaction, because the method is
usually called inside an active transaction.
However, the Set method might also be called if the user invokes Undo or Redo, or if a transaction is being rolled
back. When InUndoRedoOrRollback is true, your Set method should behave as follows:
It should not make changes in the store, such as assigning values to other domain properties. The undo
manager will set their values.
However, it should update any external resources, such as database or file contents, or objects outside the
store. This will make sure that they are kept in synchronism with the values in the store.
For example:
void SetAgeValue(int value)
{
// If we are in Undo, no changes to Store objects:
if (!this.Store.InUndoRedoOrRollback)
{
this.BirthYear = System.DateTime.Today.Year - value;
}
// But always update external objects:
System.IO.File.WriteAllText(AgeFile, value);
}

For more information about transactions, see Navigating and Updating a Model in Program Code.

See Also
Navigating and Updating a Model in Program Code
Properties of Domain Properties
How to Define a Domain-Specific Language
Domain Property Value Change Handlers
10/18/2017 5 min to read Edit Online

In a Visual Studio domain-specific language, when the value of a domain property changes, the OnValueChanging()
and OnValueChanged() methods are invoked in the domain property handler. To respond to the change, you can
override these methods.

Overriding the Property Handler methods


Each domain property of your domain-specific language is handled by a class that is nested inside its parent
domain class. Its name follows the format PropertyNamePropertyHandler. You can inspect this property handler
class in the file Dsl\Generated Code\DomainClasses.cs. In the class, OnValueChanging() is called immediately
before the value changes, and OnValueChanged() is called immediately after the value changes.
For example, suppose you have a domain class named Comment that has a string domain property named Text
and an integer property named TextLengthCount . To cause TextLengthCount always to contain the length of the
Text string, you could write the following code in a separate file in the Dsl project:

// Domain Class "Comment":


public partial class Comment
{
// Domain Property "Text":
partial class TextPropertyHandler
{
protected override void OnValueChanging(CommentBase element, string oldValue, string newValue)
{
base.OnValueChanging(element, oldValue, newValue);

// To update values outside the Store, write code here.

// Let the transaction manager handle undo:


Store store = element.Store;
if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return;

// Update values in the Store:


this.TextLengthCount = newValue.Length;
}
}
}

Notice the following points about property handlers:


The property handler methods are called both when the user makes changes to a domain property, and
when program code assigns a different value to the property.
The methods are called only when the value actually changes. The handler is not invoked if program code
assigns a value that is equal to the current value.
Calculated and custom storage domain properties do not have OnValueChanged and OnValueChanging
methods.
You cannot use a change handler to modify the new value. If you want to do that, for example to restrict the
value to a particular range, define a ChangeRule .
You cannot add a change handler to a property that represents a role of a relationship. Instead, define an
AddRule and a DeleteRule on the relationship class. These rules are triggered when the links are created or
changed. For more information, see Rules Propagate Changes Within the Model.
Changes in and out of the store
Property handler methods are called inside the transaction that initiated the change. Therefore, you can make
more changes in the store without opening a new transaction. Your changes might result in additional handler
calls.
When a transaction is being undone, redone, or rolled back, you should not make changes in the store, that is,
changes to model elements, relationships, shapes, connectors diagrams, or their properties.
Furthermore, you would usually not update values when the model is being loaded from the file.
Changes to the model should therefore be guarded by a test like this:

if (!store.InUndoRedoOrRollback
&& !store. InSerializationTransaction)
{ this.TextLength = ...; // in-store changes
}

By contrast, if your property handler propagates changes outside the store, for example, to a file, database, or non-
store variables, then you should always make those changes so that the external values are updated when the user
invokes undo or redo.
Canceling a change
If you want to prevent a change, you can roll back the current transaction. For example, you might want to ensure
that a property remains within a specific range.

if (newValue > 10)


{ store.TransactionManager.CurrentTransaction.Rollback();
System.Windows.Forms.MessageBox.Show("Value must be less than 10");
}

Alternative technique: Calculated Properties


The previous example shows how OnValueChanged() can be used to propagate values from one domain property
to another. Each property has its own stored value.
Instead, you could consider defining the derived property as a Calculated property. In that case, the property has
no storage of its own, and is defining function is evaluated whenever its value is required. For more information,
see Calculated and Custom Storage Properties.
Instead of the previous example, you could set the Kind field of TextLengthCount to be Calculated in the DSL
Definition. You would provide your own Get method for this domain property. The Get method would return the
current length of the Text string.
However, a potential drawback of calculated properties is that the expression is evaluated every time the value is
used, which might present a performance problem. Also, there is no OnValueChanging() and OnValueChanged()
on a calculated property.
Alternative technique: Change Rules
If you define a ChangeRule, it is executed at the end of a transaction in which a property's value changes. For more
information, see Rules Propagate Changes Within the Model.
If several changes are made in one transaction, the ChangeRule executes when they are all complete. By contrast,
the OnValue... methods are executed when some of the changes have not been performed. Depending on what
you want to achieve, this might make a ChangeRule more appropriate.
You can also use a ChangeRule to adjust the property's new value to keep it within a specific range.

WARNING
If a rule makes changes to the store content, other rules and property handlers might be triggered. If a rule changes the
property that triggered it, it will be called again. You must make sure that your rule definitions do not result in endless
triggering.

using Microsoft.VisualStudio.Modeling;
...
// Change rule on the domain class Comment:
[RuleOn(typeof(Comment), FireTime = TimeToFire.TopLevelCommit)]
class MyCommentTrimRule : ChangeRule
{
public override void
ElementPropertyChanged(ElementPropertyChangedEventArgs e)
{
base.ElementPropertyChanged(e);
Comment comment = e.ModelElement as Comment;

if (comment.Text.StartsWith(" ") || comment.Text.EndsWith(" "))


comment.Text = comment.Text.Trim();
// If changed, rule will trigger again.
}
}

// Register the rule:


public partial class MyDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{ return new Type[] { typeof(MyCommentTrimRule) };
}
}

Example
Description
The following example overrides the property handler of a domain property and notifies the user when a property
for the ExampleElement domain class has changed.
Code
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using DslDesign = global::Microsoft.VisualStudio.Modeling.Design;

namespace msft.FieldChangeSample
{
public partial class ExampleElement
{
internal sealed partial class NamePropertyHandler
{
protected override void OnValueChanged(ExampleElement element,
string oldValue, string newValue)
{
if (!this.Store.InUndoRedoOrRollback)
{
// make in-store changes here...
}
// This part is called even in undo:
System.Windows.Forms.MessageBox.Show("Value Has Changed");
base.OnValueChanged(element, oldValue, newValue);
}
}
}
}

See Also
Microsoft.VisualStudio.Modeling.DomainPropertyValueHandler`2.OnValueChanged*
Microsoft.VisualStudio.Modeling.DomainPropertyValueHandler`2.OnValueChanging*
Rules Propagate Changes Within the Model
10/18/2017 8 min to read Edit Online

You can create a store rule to propagate a change from one element to another in Visualization and Modeling
SDK (VMSDK). When a change occurs to any element in the Store, rules are scheduled to be executed, usually
when the outermost transaction is committed. There are different types of rules for different kinds of events,
such as adding an element, or deleting it. You can attach rules to specific types of elements, shapes, or diagrams.
Many built-in features are defined by rules: for example, rules ensure that a diagram is updated when the model
changes. You can customize your domain-specific language by adding your own rules.
Store rules are particularly useful for propagating changes inside the store - that is, changes to model elements,
relationships, shapes or connectors, and their domain properties. Rules do not run when the user invokes the
Undo or Redo commands. Instead, the transaction manager makes sure that the store contents are restored to
the correct state. If you want to propagate changes to resources outside the store, use Store Events. For more
information, see Event Handlers Propagate Changes Outside the Model.
For example, suppose that you want to specify that whenever the user (or your code) creates a new element of
type ExampleDomainClass, an additional element of another type is created in another part of the model. You
could write an AddRule and associate it with ExampleDomainClass. You would write code in the rule to create
the additional element.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;

namespace ExampleNamespace
{
// Attribute associates the rule with a domain class:
[RuleOn(typeof(ExampleDomainClass), FireTime=TimeToFire.TopLevelCommit)]
// The rule is a class derived from one of the abstract rules:
class MyAddRule : AddRule
{
// Override the abstract method:
public override void ElementAdded(ElementAddedEventArgs e)
{
base.ElementAdded(e);
ExampleDomainClass element = e.ModelElement;
Store store = element.Store;
// Ignore this call if we're currently loading a model:
if (store.TransactionManager.CurrentTransaction.IsSerializing)
return;

// Code here propagates change as required - for example:


AnotherDomainClass echo = new AnotherDomainClass(element.Partition);
echo.Name = element.Name;
echo.Parent = element.Parent;
}
}
// The rule must be registered:
public partial class ExampleDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(MyAddRule));
// If you add more rules, list them here.
return types.ToArray();
}
}
}

NOTE
The code of a rule should change the state only of elements inside the Store; that is, the rule should change only model
elements, relationships, shapes, connectors, diagrams, or their properties. If you want to propagate changes to resources
outside the store, define Store Events. For more information, see Event Handlers Propagate Changes Outside the Model

To define a rule
1. Define the rule as a class prefixed with the RuleOn attribute. The attribute associates the rule with one of
your domain classes, relationships, or diagram elements. The rule will be applied to every instance of this
class, which may be abstract.
2. Register the rule by adding it to the set returned by GetCustomDomainModelTypes() in your domain model
class.
3. Derive the rule class from one of the abstract Rule classes, and write the code of the execution method.
The following sections describe these steps in more detail.
To define a rule on a domain class
In a custom code file, define a class and prefix it with the RuleOnAttribute attribute:
[RuleOn(typeof(ExampleElement),
// Usual value - but required, because it is not the default:
FireTime = TimeToFire.TopLevelCommit)]
class MyRule ...

The subject type in the first parameter can be a domain class, domain relationship, shape, connector, or
diagram. Usually, you apply rules to domain classes and relationships.
The FireTime is usually TopLevelCommit . This ensures that the rule is executed only after all the primary
changes of the transaction have been made. The alternatives are Inline, which executes the rule soon after
the change; and LocalCommit, which executes the rule at the end of the current transaction (which might
not be the outermost). You can also set the priority of a rule to affect its ordering in the queue, but this is
an unreliable method of achieving the result you require.
You can specify an abstract class as the subject type.
The rule applies to all instances of the subject class.
The default value for FireTime is TimeToFire.TopLevelCommit. This causes the rule to be executed when
the outermost transaction is committed. An alternative is TimeToFire.Inline. This causes the rule to be
executed soon after the triggering event.
To register the rule
Add your rule class to the list of types returned by GetCustomDomainModelTypes in your domain model:

public partial class ExampleDomainModel


{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(MyAddRule));
// If you add more rules, list them here.
return types.ToArray();
}
}

If you are not sure of the name of your domain model class, look inside the file
Dsl\GeneratedCode\DomainModel.cs
Write this code in a custom code file in your DSL project.
To write the code of the rule
Derive the rule class from one of the following base classes:

BASE CLASS TRIGGER

AddRule An element, link, or shape is added.

Use this to detect new relationships, in addition to new


elements.
BASE CLASS TRIGGER

ChangeRule A domain property value is changed. The method


argument provides the old and new values.

For shapes, this rule is triggered when the built-in


AbsoluteBounds property changes, if the shape is
moved.

In many cases, it is more convenient to override


OnValueChanged or OnValueChanging in the property
handler. These methods are called immediately before
and after the change. By contrast, the rule usually runs
at the end of the transaction. For more information, see
Domain Property Value Change Handlers. Note: This
rule is not triggered when a link is created or deleted.
Instead, write an AddRule and a DeleteRule for the
domain relationship.

DeletingRule Triggered when an element or link is about to be


deleted. The property ModelElement.IsDeleting is true
until the end of the transaction.

DeleteRule Performed when an element or link has been deleted.


The rule is executed after all other rules have been
executed, including DeletingRules.
ModelElement.IsDeleting is false, and
ModelElement.IsDeleted is true. To allow for a
subsequent Undo, the element is not actually removed
from the memory, but it is removed from
Store.ElementDirectory.

MoveRule An element is moved from one store partition to


another.

(Notice that this is not related to the graphical position


of a shape.)

RolePlayerChangeRule This rule applies only to domain relationships. It is


triggered if you explicitly assign a model element to
either end of a link.

RolePlayerPositionChangeRule Triggered when the ordering of links to or from an


element is changed using the MoveBefore or
MoveToIndex methods on a link.

TransactionBeginningRule Executed when a transaction is created.

TransactionCommittingRule Executed when the transaction is about to be committed.

TransactionRollingBackRule Executed when the transaction is about to be rolled back.

Each class has a method that you override. Type override in your class to discover it. The parameter of
this method identifies the element that is being changed.
Notice the following points about rules:
1. The set of changes in a transaction might trigger many rules. Usually, the rules are executed when the
outermost transaction is committed. They are executed in an unspecified order.
2. A rule is always executed inside a transaction. Therefore, you do not have to create a new transaction to
make changes.
3. Rules are not executed when a transaction is rolled back, or when the Undo or Redo operations are
performed. These operations reset all the content of the Store to its previous state. Therefore, if your rule
changes the state of anything outside the Store, it might not keep in synchronism with the Store content.
To update state outside the Store, it is better to use Events. For more information, see Event Handlers
Propagate Changes Outside the Model.
4. Some rules are executed when a model is loaded from file. To determine whether loading or saving is in
progress, use store.TransactionManager.CurrentTransaction.IsSerializing .
5. If the code of your rule creates more rule triggers, they will be added to the end of the firing list, and will
be executed before the transaction completes. DeletedRules are executed after all other rules. One rule
can run many times in a transaction, one time for each change.
6. To pass information to and from rules, you can store information in the TransactionContext . This is just a
dictionary that is maintained during the transaction. It is disposed when the transaction ends. The event
arguments in each rule provide access to it. Remember that rules are not executed in a predictable order.
7. Use rules after considering other alternatives. For example, if you want to update a property when a value
changes, consider using a calculated property. If you want to constrain the size or location of a shape, use
a BoundsRule . If you want to respond to a change in a property value, add an OnValueChanged handler to
the property. For more information, see Responding to and Propagating Changes.

Example
The following example updates a property when a domain relationship is instantiated to link two elements. The
rule will be triggered not only when the user creates a link on a diagram, but also if program code creates a link.
To test this example, create a DSL using the Task Flow solution template, and insert the following code in a file in
the Dsl project. Build and run the solution, and open the Sample file in the Debugging project. Draw a Comment
Link between a Comment shape and a flow element. The text in the comment changes to report on the most
recent element that you have connected it to.
In practice, you would usually write a DeleteRule for every AddRule.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;

namespace Company.TaskRuleExample
{

[RuleOn(typeof(CommentReferencesSubjects))]
public class RoleRule : AddRule
{

public override void ElementAdded(ElementAddedEventArgs e)


{
base.ElementAdded(e);
CommentReferencesSubjects link = e.ModelElement as CommentReferencesSubjects;
Comment comment = link.Comment;
FlowElement subject = link.Subject;
Transaction current = link.Store.TransactionManager.CurrentTransaction;
// Don't want to run when we're just loading from file:
if (current.IsSerializing) return;
comment.Text = "Flow has " + subject.FlowTo.Count + " outgoing connections";
}

public partial class TaskRuleExampleDomainModel


{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(RoleRule));
return types.ToArray();
}
}

See Also
Event Handlers Propagate Changes Outside the Model
BoundsRules Constrain Shape Location and Size
Event Handlers Propagate Changes Outside the
Model
10/18/2017 5 min to read Edit Online

In Visualization and Modeling SDK, you can define store event handlers to propagate changes to resources
outside the store, such as non-store variables, files, models in other stores, or other Visual Studio extensions.
Store event handlers are executed after the end of the transaction in which the triggering event occurred. They
are also executed in an Undo or Redo operation. Therefore, unlike store rules, store events are most useful for
updating values that are outside the store. Unlike .NET events, store event handlers are registered to listen to a
class: you do not have to register a separate handler for each instance. For more information about how to
choose between different ways to handle changes, see Responding to and Propagating Changes.
The graphical surface and other user interface controls are examples of external resources that can be handled
by store events.
To define a store event
1. Choose the type of event that you want to monitor. For a full list, look at the properties of
EventManagerDirectory. Each property corresponds to a type of event. The most frequently used event
types are:
ElementAdded - triggered when a model element, relationship link, shape or connector is created.
ElementPropertyChanged - triggered when the value of a Normal domain property is changed.
The event is triggered only if the new and old values are not equal. The event cannot be applied to
calculated and custom storage properties.
It cannot be applied to the role properties that correspond to relationship links. Instead, use
ElementAdded to monitor the domain relationship.

ElementDeleted - triggered after a model element, relationship, shape or connector has been
deleted. You can still access the property values of the element, but it will have no relationships to
other elements.
2. Add a partial class definition for YourDslDocData in a separate code file in the DslPackage project.
3. Write the code of the event as a method, as in the following example. It can be static , unless you want
to access DocData .
4. Override OnDocumentLoaded() to register the handler. If you have more than one handler, you can register
them all in the same place.
The location of the registration code is not critical. DocView.LoadView() is an alternative location.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;

namespace Company.MusicLib
{
partial class MusicLibDocData
{
// Register store events here or in DocView.LoadView().
protected override void OnDocumentLoaded()
{
base.OnDocumentLoaded(); // Don't forget this.

#region Store event handler registration.


Store store = this.Store;
EventManagerDirectory emd = store.EventManagerDirectory;
DomainRelationshipInfo linkInfo = store.DomainDataDirectory
.FindDomainRelationship(typeof(ArtistAppearsInAlbum));
emd.ElementAdded.Add(linkInfo,
new EventHandler<ElementAddedEventArgs>(AddLink));
emd.ElementDeleted.Add(linkInfo,
new EventHandler<ElementDeletedEventArgs>(RemoveLink));

#endregion Store event handlers.


}

private void AddLink(object sender, ElementAddedEventArgs e)


{
ArtistAppearsInAlbum link = e.ModelElement as ArtistAppearsInAlbum;
if (link != null)
ExternalDatabase.Add(link.Artist.Name, link.Album.Title);
}
private void RemoveLink(object sender, ElementDeletedEventArgs e)
{
ArtistAppearsInAlbum link = e.ModelElement as ArtistAppearsInAlbum;
if (link != null)
ExternalDatabase.Delete(link.Artist.Name, link.Album.Title);
}
}

Using Events to Make Undoable Adjustments in the Store


Store events are not normally used for propagating changes inside the store, because the event handler executes
after the transaction is committed. Instead, you would use a store rule. For more information, see Rules
Propagate Changes Within the Model.
However, you could use an event handler to make additional updates to the store, if you want the user to be able
to undo the additional updates separately from the original event. For example, suppose that lower case
characters are the usual convention for album titles. You could write a store event handler that corrects the title
to lower case after the user has typed it in upper case. But the user could use the Undo command to cancel your
correction, restoring the upper case characters. A second Undo would remove the user's change.
By contrast, if you wrote a store rule to do the same thing, the user's change and your correction would be in the
same transaction, so that the user could not undo the adjustment without losing the original change.
partial class MusicLibDocView
{
// Register store events here or in DocData.OnDocumentLoaded().
protected override void LoadView()
{
/* Register store event handler for Album Title property. */
// Get reflection data for property:
DomainPropertyInfo propertyInfo =
this.DocData.Store.DomainDataDirectory
.FindDomainProperty(Album.TitleDomainPropertyId);
// Add to property handler list:
this.DocData.Store.EventManagerDirectory
.ElementPropertyChanged.Add(propertyInfo,
new EventHandler<ElementPropertyChangedEventArgs>
(AlbumTitleAdjuster));

/*
// Alternatively, you can set one handler for
// all properties of a class.
// Your handler has to determine which property changed.
DomainClassInfo classInfo = this.Store.DomainDataDirectory
.FindDomainClass(typeof(Album));
this.Store.EventManagerDirectory
.ElementPropertyChanged.Add(classInfo,
new EventHandler<ElementPropertyChangedEventArgs>
(AlbumTitleAdjuster));
*/
return base.LoadView();
}

// Undoable adjustment after a property is changed.


// Method can be static since no local access.
private static void AlbumTitleAdjuster(object sender,
ElementPropertyChangedEventArgs e)
{
Album album = e.ModelElement as Album;
Store store = album.Store;

// We mustn't update the store in an Undo:


if (store.InUndoRedoOrRollback
|| store.InSerializationTransaction)
return;

if (e.DomainProperty.Id == Album.TitleDomainPropertyId)
{
string newValue = (string)e.NewValue;
string lowerCase = newValue.ToLowerInvariant();
if (!newValue.Equals(lowerCase))
{
using (Transaction t = store.TransactionManager
.BeginTransaction("adjust album title"))
{
album.Title = lowerCase;
t.Commit();
} // Beware! This could trigger the event again.
}
}
// else other properties of this class.
}

If you write an event that updates the store:


Use store.InUndoRedoOrRollback to avoid making changes to model elements in Undo. The transaction
manager will set everything in the store back to its original state.
Use store.InSerializationTransaction to avoid making changes while the model is being loaded from
file.
Your changes will cause further events to be triggered. Make sure that you avoid an infinite loop.

Store Event types


Each event type corresponds to a collection in Store.EventManagerDirectory. You can add or remove event
handlers at any time, but it is usual to add them when the document is loaded.

EVENTMANAGERDIRECTORY PROPERTY NAME EXECUTED WHEN

ElementAdded An instance of a domain class, domain relationship, shape,


connector or diagram is created.

ElementDeleted A model element has been removed from the store's


element directory and is no longer the source or target of
any relationship. The element is not actually deleted from
memory, but is retained in case of a future Undo.

ElementEventsBegun Invoked at the end of an outer transaction.

ElementEventsEnded Invoked when all other events have been processed.

ElementMoved A model element has been moved from one store partition
to another.

This is not related to the location of a shape on the diagram.

ElementPropertyChanged The value of a domain property has changed. This is


executed only if the old and new values are unequal.

RolePlayerChanged One of the two roles (ends) of a relationship references a


new element.

RolePlayerOrderChanged In a role with multiplicity greater than 1, the sequence of


links has changed.

TransactionBeginning

TransactionCommitted

TransactionRolledBack

See Also
Responding to and Propagating Changes
Sample code: Circuit Diagrams

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.
BoundsRules Constrain Shape Location and Size
10/18/2017 1 min to read Edit Online

A Bounds Rule is a class that defines limits on the size and location of a shape. It provides a method that is called
repeatedly while a user is dragging a shape or the corners or sides of a shape.
The following example constrains a rectangular shape to be a bar of fixed size, either horizontal or vertical. When
the user drags the corners or sides, the outline flips between the two permitted configurations of height and width.
The bounds rule is a class derived from BoundsRules. An instance of the rule is created in the shape:

using Microsoft.VisualStudio.Modeling.Diagrams; ...


public partial class BarShape
{
/// <summary>
/// Rule invoked when the user is resizing a shape.
/// </summary>
public override BoundsRules BoundsRules
{ get { return new BarBoundsRule(); } }
}
/// <summary>
/// Rule invoked when the user is changing a shape's outline.
/// Provides real-time mouse rubber-band feedback, so must work fast.
/// </summary>
public class BarBoundsRule: BoundsRules
{
public override RectangleD GetCompliantBounds
(ShapeElement shape, RectangleD proposedBounds)
{
double thickness = 0.1;
if (proposedBounds.Height > proposedBounds.Width)
{
// There is a minimum width for a shape; the width
// will actually be set to the lesser of
// thickness and that minimum.
return new RectangleD(proposedBounds.Location,
new SizeD(thickness, proposedBounds.Height));
}
else
{
// There is a minimum height for a shape; the
// height will actually be set to the lesser of
// thickness and that minimum.
return new RectangleD(proposedBounds.Location,
new SizeD(proposedBounds.Width, thickness));
} } }

Notice that both the location and size can be constrained if you want.

See Also
BoundsRules
Responding to and Propagating Changes
Updating Shapes and Connectors to Reflect the
Model
10/18/2017 4 min to read Edit Online

In a domain-specific language in Visual Studio, you can make the appearance of a shape reflect the state of the
underlying model.
The code examples in this topic should be added to a .cs file in your Dsl project. You will need these statements
in each file:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

Set Shape Map properties to control the visibility of a decorator


You can control the visibility of a decorator without writing program code, by configuring the mapping between
the shape and the domain class in the DSL Definition. For more information, see How to Define a Domain-Specific
Language.

Expose the color and style of a shape as properties


In the DSL Definition, right-click the shape class, point to Add Exposed, and then click one of the items such as Fill
Color.
The shape now has a domain property that you can set in program code or as a user. For example, to set it in the
program code of a command or rule, you could write:
shape.FillColor = System.Drawing.Color.Red;

If you want to make the property variable only under program control, and not by the user, select the new domain
property such as Fill Color in the DSL Definition diagram. Then, in the Properties window, set Is Browsable to
false or set Is UI Readonly to true .

Define Change Rules to make color, style or location depend on model


element properties
You can define rules that update the appearance the shape dependent on other parts of the model. For example,
you could define a Change Rule on a model element that updates the color of its shape dependent on the
properties of the model element. For more information about Change Rules, see Rules Propagate Changes Within
the Model.
You should use rules only to update properties that are maintained within the Store, because rules are not invoked
when the Undo command is performed. This does not include some graphical features such as the size and
visibility of a shape. To update those features of a shape, see Updating Non-Store Graphical features.
The following example assumes that you have exposed FillColor as a domain property as described in the
previous section.
[RuleOn(typeof(ExampleElement))]
class ExampleElementPropertyRule : ChangeRule
{
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
{
base.ElementPropertyChanged(e);
ExampleElement element = e.ModelElement as ExampleElement;
// The rule is called for every property that is updated.
// Therefore, verify which property changed:
if (e.DomainProperty.Id == ExampleElement.NameDomainPropertyId)
{
// There is usually only one shape:
foreach (PresentationElement pel in PresentationViewsSubject.GetPresentation(element))
{
ExampleShape shape = pel as ExampleShape;
// Replace this with a useful condition:
shape.FillColor = element.Name.EndsWith("3")
? System.Drawing.Color.Red : System.Drawing.Color.Green;
}
}
}
}
// The rule must be registered:
public partial class OnAssociatedPropertyExptDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
types.Add(typeof(ExampleElementPropertyRule));
// If you add more rules, list them here.
return types.ToArray();
}
}

Use OnChildConfigured to initialize a shape's properties


To set the properties of a shape when it is first created, the override OnChildConfigured() in a partial definition of
your diagram class. The diagram class is specified in your DSL Definition, and the generated code is in
Dsl\Generated Code\Diagram.cs. For example:

partial class MyLanguageDiagram


{
protected override void OnChildConfigured(ShapeElement child, bool childWasPlaced, bool
createdDuringViewFixup)
{
base.OnChildConfigured(child, childWasPlaced, createdDuringViewFixup);
ExampleShape shape = child as ExampleShape;
if (shape != null)
{
if (!createdDuringViewFixup) return; // Ignore load from file.
ExampleElement element = shape.ModelElement as ExampleElement;
// Replace with a useful condition:
shape.FillColor = element.Name.EndsWith("3")
? System.Drawing.Color.Red : System.Drawing.Color.Green;
}
// else deal with other types of shapes and connectors.
}
}

This method can be used both for domain properties and non-store features, such as the size of the shape.

Use AssociateValueWith() to update other features of a shape


For some features of a shape, such as whether it has a shadow, or the arrow style of a connector, there is no built-
in method of exposing the feature as a domain property. Changes to such features are not under the control of the
transaction system. Therefore, it is not appropriate to update them using rules, because rules are not invoked when
the user performs the Undo command.
Instead, you can update such features by using OnAssociatedPropertyChanged. In the following example, the
arrow style of a connector is controlled by a value of a domain property in the relationship that the connector
displays:

public partial class ArrowConnector // My connector class.


{
/// <summary>
/// Called whenever a registered property changes in the associated model element.
/// </summary>
/// <param name="e"></param>
protected override void
OnAssociatedPropertyChanged(VisualStudio.Modeling.Diagrams.PropertyChangedEventArgs e)
{
base.OnAssociatedPropertyChanged(e);
// Can be called for any property change. Therefore,
// Verify that this is the property we're interested in:
if ("IsDirected".Equals(e.PropertyName))
{
if (e.NewValue.Equals(true))
{ // Update the shape's built-in Decorator feature:
this.DecoratorTo = LinkDecorator.DecoratorEmptyArrow;
}
else
{
this.DecoratorTo = null; // No arrowhead.
}
}
}
// OnAssociatedPropertyChanged is called only for properties
// that have been registered using AssociateValueWith().
// InitializeResources is a convenient place to call this.
// This method is invoked only once per class, even though
// it is an instance method.
protected override void InitializeResources(StyleSet classStyleSet)
{
base.InitializeResources(classStyleSet);
AssociateValueWith(this.Store, Wire.IsDirectedDomainPropertyId);
// Add other properties here.
}
}

AssociateValueWith() should be called one time for each domain property that you want to register. After it has
been called, any changes to the specified property will call OnAssociatedPropertyChanged() in any shapes that
present the property's model element.
It is not necessary to call AssociateValueWith() for each instance. Although InitializeResources is an instance
method, it is invoked only one time for each shape class.
Generating Code from a Domain-Specific
Language
10/18/2017 1 min to read Edit Online

Microsoft Domain-Specific Language Tools provides a powerful way to generate code, documents,
configuration files and other artifacts from data represented in models. Using Domain-Specific Language Tools,
you can create a set of classes that represent your data, and you can write your text templates in classes whose
names and properties reflect that data.
For example, Fabrikam has an XML file of customer names and e-mail addresses. Their developers create a
model in which Customer is a class, with properties name and e-mail. They write several text templates to
process the data, including this fragment which produces a table of all the customers as part of an HTML page:

<table>
<# foreach (Customer c in ContactList) { #>
<tr><td> <#= c.FullName #> </td>
<td> <#= c.EmailAddress #> </td> </tr>
<# } #> </table>

When the customer database is processed, the XML file is read into the model store. A directive processor,
created by using Domain-Specific Language Tools, makes the Customer class available to the code in the text
template. Many text templates can be run against the same store.
Text templates are essential to Domain-Specific Language Tools. They are used to generate the source code for
the elements of the domain model as well as for the VSPackage and the controls that are used to integrate the
tools with Visual Studio.
This section discusses some of the ways to create, modify, and debug text templates used in Domain-Specific
Language Tools.

In This Section
Accessing Models from Text Templates
Provides basic information about referring to domain-specific language in text templates.
Walkthrough: Debugging a Text Template that Accesses a Model
Describes how to do troubleshooting and debugging on a text template that refers to a domain-specific
language.
Walkthrough: Connecting a Host to a Generated Directive Processor
Describes how to connect a custom host to a generated directive processor.
The DslTextTransform Command
Describes the command file that executes the TextTransform executable on the command line for text templates
that reference domain-specific languages.

Reference
Writing a T4 Text Template
Provides the syntax of text template directives and control blocks.

Related Sections
Design-Time Code Generation by using T4 Text Templates
Explains the text template transformation process.
Code Generation in a Build Process
Read this topic if you are generating files from a DSL on a build server.
Accessing Models from Text Templates
10/18/2017 7 min to read Edit Online

By using text templates, you can create report files, source code files, and other text files that are based on domain-
specific language models. For basic information about text templates, see Code Generation and T4 Text Templates.
The text templates will work in the experimental mode when you are debugging your DSL, and will also work on a
computer on which you have deployed the DSL.

NOTE
When you create a DSL solution, sample text template *.tt files are generated in the debugging project. When you change
the names of the domain classes, these templates will no longer work. Nevertheless, they include the basic directives that you
need, and provide examples that you can update to match your DSL.

To access a model from a text template:


Set the inherit property of the template directive to ModelingTextTransformation. This provides access to the
Store.
Specify directive processors for the DSL that you want to access. This loads the assemblies for your DSL so
that you can use its domain classes, properties, and relationships in the code of your text template. It also
loads the model file that you specify.
A .tt file similar to the following example is created in the Debugging project when you create a new
Visual Studio solution from the DSL Minimal Language template.

<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>


<#@ output extension=".txt" #>
<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1'" #>

This text will be output directly.

This is the name of the model: <#= this.ModelRoot.Name #>

Here is a list of elements in the model:


<#
// When you change the DSL Definition, some of the code below may not work.
foreach (ExampleElement element in this.ExampleModel.Elements)
{#>
<#= element.Name #>
<#
}
#>

Notice the following points about this template:


The template can use the domain classes, properties, and relationships that you defined in the DSL
Definition.
The template loads the model file that you specify in the requires property.
A property in this contains the root element. From there, your code can navigate to other elements of the
model. The name of the property is usually the same as the root domain class of your DSL. In this example, it
is this.ExampleModel .
Although the language in which the code fragments are written is C#, you can generate text of any kind. You
can alternatively write the code in Visual Basic by adding the property language="VB" to the template
directive.
To debug the template, add debug="true" to the template directive. The template will open in another
instance of Visual Studio if an exception occurs. If you want to break into the debugger at a specific point in
the code, insert the statement System.Diagnostics.Debugger.Break();
For more information, see Debugging a T4 Text Template.

About the DSL directive processor


The template can use the domain classes that you defined in your DSL Definition. This is brought about by a
directive that usually appears near the start of the template. In the previous example, it is the following.

<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1'" #>

The name of the directive ( MyLanguage , in this example) is derived from the name of your DSL. It invokes a
directive processor that is generated as part of your DSL. You can find its source code in
Dsl\GeneratedCode\DirectiveProcessor.cs.
The DSL directive processor performs two principal tasks:
It effectively inserts assembly and import directives into the template that references your DSL. This lets you
use your domain classes in the template code.
It loads the file that you specify in the requires parameter, and sets a property in this that refers to the
root element of the loaded model.

Validating the model before running the template


You can cause the model to be validated before the template is executed.

<#@ MyLanguage processor="MyLanguageDirectiveProcessor"


requires="fileName='Sample.myDsl1';validation='open|load|save|menu'" #>

Notice that:
1. The filename and validation parameters are separated with ";" and there must be no other separators or
spaces.
2. The list of validation categories determines which validation methods will be executed. Multiple categories
should be separated with "|" and there must be no other separators or spaces.
If an error is found, it will be reported in the errors window, and the result file will contain an error message.

Accessing multiple models from a text template


NOTE
This method lets you read multiple models in the same template but does not support ModelBus references. To read models
that are interlinked by ModelBus References, see Using Visual Studio ModelBus in a Text Template.

If you want to access more than one model from the same text template, you must call the generated directive
processor one time for each model. You must specify the file name of each model in the requires parameter. You
must specify the names that you want to use for the root domain class in the provides parameter. You must
specify different values for the provides parameters in each of the directive calls. For example, assume that you
have three model files called Library.xyz, School.xyz, and Work.xyz. To access them from the same text template,
you must write three directive calls that resemble the following ones.

<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='Library.xyz'"


provides="ExampleModel=LibraryModel" #>
<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='School.xyz'"
provides="ExampleModel=SchoolModel" #>
<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='Work.xyz'"
provides="ExampleModel=WorkModel" #>

NOTE
This example code is for a language that is based on the Minimal Language solution template.

To access the models in your text template, you can now write code similar to the code in the following example.

<#
foreach (ExampleElement element in this.LibraryModel.Elements)
...
foreach (ExampleElement element in this.SchoolModel.Elements)
...
foreach (ExampleElement element in this.WorkModel.Elements)
...
#>

<#
For Each element As ExampleElement In Me.LibraryModel.Elements
...
For Each element As ExampleElement In Me.SchoolModel.Elements
...
For Each element As ExampleElement In Me.WorkModel.Elements
...
#>

Loading models dynamically


If you want to determine at runtime which models to load, you can load a model file dynamically in your program
code, instead of using the DSL-specific directive.
However, one of the functions of the DSL-specific directive is to import the DSL namespace, so that the template
code can use the domain classes defined in that DSL. Because you are not using the directive, you must add
<assembly> and <import> directives for all the models that you might load. This is easy if the different models
that you might load are all instances of the same DSL.
To load the file, the most effective method is by using Visual Studio ModelBus. In a typical scenario, your text
template will use a DSL-specific directive to load the first model in the usual way. That model would contain
ModelBus References to another model. You can use ModelBus to open the referenced model and access a
particular element. For more information, see Using Visual Studio ModelBus in a Text Template.
In a less usual scenario, you might want to open a model file for which you have only a filename, and which might
not be in the current Visual Studio project. In this case, you can open the file by using the technique described in
How to: Open a Model from File in Program Code.
Generating multiple files from a template
If you want to generate a several files - for example, to generate a separate file for each element in a model, there
are several possible approaches. By default, only one file is produced from each template file.
Splitting a long file
In this method, you use a template to generate a single file, separated by a delimiter. Then you split the file into its
parts. There are two templates, one to generate the single file, and the other to split it.
LoopTemplate.t4 generates the long single file. Notice that its file extension is ".t4", because it should not be
processed directly when you click Transform All Templates. This template takes a parameter, which specifies the
delimiter string that separates the segments:

<#@ template ninherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>


<#@ parameter name="delimiter" type="System.String" #>
<#@ output extension=".txt" #>
<#@ MyDSL processor="MyDSLDirectiveProcessor"
requires="fileName='SampleModel.mydsl1';validation='open|load|save|menu'" #>
<#
// Create a file segment for each element:
foreach (ExampleElement element in this.ExampleModel.Elements)
{
// First item is the delimiter:
#>
<#= string.Format(delimiter, element.Id) #>

Element: <#= element.Name #>


<#
// Here you generate more content derived from the element.
}
#>

LoopSplitter.tt invokes LoopTemplate.t4 , and then splits the resulting file into its segments. Notice that this
template does not have to be a modeling template, because it does not read the model.
<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="System.IO" #>

<#
// Get the local path:
string itemTemplatePath = this.Host.ResolvePath("LoopTemplate.t4");
string dir = Path.GetDirectoryName(itemTemplatePath);

// Get the template for generating each file:


string loopTemplate = File.ReadAllText(itemTemplatePath);

Engine engine = new Engine();

// Pass parameter to new template:


string delimiterGuid = Guid.NewGuid().ToString();
string delimiter = "::::" + delimiterGuid + ":::";
CallContext.LogicalSetData("delimiter", delimiter + "{0}:::");
string joinedFiles = engine.ProcessTemplate(loopTemplate, this.Host);

string [] separateFiles = joinedFiles.Split(new string [] {delimiter}, StringSplitOptions.None);

foreach (string nameAndFile in separateFiles)


{
if (string.IsNullOrWhiteSpace(nameAndFile)) continue;
string[] parts = nameAndFile.Split(new string[]{":::"}, 2, StringSplitOptions.None);
if (parts.Length < 2) continue;
#>
Generate: [<#= dir #>] [<#= parts[0] #>]
<#
// Generate a file from this item:
File.WriteAllText(Path.Combine(dir, parts[0] + ".txt"), parts[1]);
}
#>
Using Visual Studio ModelBus in a Text Template
10/18/2017 12 min to read Edit Online

If you write text templates that read a model that contains Visual Studio ModelBus references, you might want to
resolve the references to access the target models. In that case, you have to adapt the text templates and the
referenced domain-specific languages (DSLs):
The DSL that is the target of the references must have a ModelBus Adapter that is configured for access
from text templates. If you also access the DSL from other code, the reconfigured adapter is required in
addition to the standard ModelBus Adapter.
The adapter manager must inherit from VsTextTemplatingModelingAdapterManager and must have the
attribute [HostSpecific(HostName)] .
The template must inherit from ModelBusEnabledTextTransformation.

NOTE
If you want to read DSL models that do not contain ModelBus references, you can use the directive processors that are
generated in your DSL projects. For more information, see Accessing Models from Text Templates.

For more information about text templates, see Design-Time Code Generation by using T4 Text Templates.

Creating a Model Bus Adapter for Access from Text Templates


To resolve a ModelBus reference in a text template, the target DSL must have a compatible adapter. Text templates
execute in a separate AppDomain from the Visual Studio document editors, and therefore the adapter has to load
the model instead of accessing it through DTE.
To create a ModelBus Adapter that is compatible with text templates
1. If the target DSL solution does not have a ModelBusAdapter project, create one by using the Modelbus
Extension wizard:
a. Download and install the Visual Studio ModelBus Extension, if you have not already done this. For
more information, see Visualization and Modeling SDK.
b. Open the DSL definition file. Right-click the design surface and then click Enable Modelbus.
c. In the dialog box, select I want to expose this DSL to the ModelBus. You can select both options if
you want this DSL both to expose its models and to consume references to other DSLs.
d. Click OK. A new project "ModelBusAdapter" is added to the DSL solution.
e. Click Transform All Templates.
f. Rebuild the solution.
2. If you want to access the DSL both from a text template and from other code, such as command, duplicate
the ModelBusAdapter project:
a. In Windows Explorer, copy and paste the folder that contains ModelBusAdapter.csproj.
b. Rename the project file (for example, to T4ModelBusAdapter.csproj).
c. In Solution Explorer, right-click the solution node, point to Add, and then click Existing Project.
Locate the new adapter project, T4ModelBusAdapter.csproj.
d. In each *.tt file of the new project, change the namespace.
e. Right-click the new project in Solution Explorer and then click Properties. In the properties editor,
change the names of the generated assembly and the default namespace.
f. In the DslPackage project, add a reference to the new adapter project so that it has references to both
adapters.
g. In DslPackage\source.extension.tt, add a line that references your new adapter project.

<MefComponent>|T4ModelBusAdapter|</MefComponent>

h. Transform All Templates and rebuild the solution. No build errors should occur.
3. In the new adapter project, add references to the following assemblies:
Microsoft.VisualStudio.TextTemplating.11.0
Microsoft.VisualStudio.TextTemplating.Modeling.11.0
4. In AdapterManager.tt:
Change the declaration of AdapterManagerBase so that it inherits from
VsTextTemplatingModelingAdapterManager.
public partial class <#= dslName =>AdapterManagerBase :

Microsoft.VisualStudio.TextTemplating.Modeling.VsTextTemplatingModelingAdapterManager { ...

Near the end of the file, replace the HostSpecific attribute before the AdapterManager class. Remove
the following line:
[DslIntegration::HostSpecific(DslIntegrationShell::VsModelingAdapterManager.HostName)]

Insert the following line:


[Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]

This attribute filters the set of adapters that is available when a modelbus consumer searches for an
adapter.
5. Transform All Templates and rebuild the solution. No build errors should occur.

Writing a Text Template That Can Resolve ModelBus References


Typically, you begin with a template that reads and generates files from a "source" DSL. This template uses the
directive that is generated in the source DSL project to read source model files in the manner that is described in
Accessing Models from Text Templates. However, the source DSL contains ModelBus References to a "target" DSL.
You therefore want to enable the template code to resolve the references and access the target DSL. You therefore
must adapt the template by following these steps:
Change the base class of the template to ModelBusEnabledTextTransformation.
Include hostspecific="true" in the template directive.
Add assembly references to the target DSL and its adapter, and to enable ModelBus.
You do not need the directive that is generated as part of the target DSL.
<#@ template debug="true" hostspecific="true" language="C#"
inherits="Microsoft.VisualStudio.TextTemplating.Modeling.ModelBusEnabledTextTransformation" #>
<#@ SourceDsl processor="SourceDslDirectiveProcessor" requires="fileName='Sample.source'" #>
<#@ output extension=".txt" #>
<#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0" #>
<#@ assembly name = "Company.TargetDsl.Dsl.dll" #>
<#@ assembly name = "Company.TargetDsl.T4ModelBusAdapter.dll" #>
<#@ assembly name = "System.Core" #>
<#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
<#@ import namespace="Company.TargetDsl" #>
<#@ import namespace="Company.TargetDsl.T4ModelBusAdapters" #>
<#@ import namespace="System.Linq" #>
<#
SourceModelRoot source = this.ModelRoot; // Usual access to source model.
// In the source DSL Definition, the root element has a model reference:
using (TargetAdapter adapter = this.ModelBus.CreateAdapter(source.ModelReference) as TargetAdapter)
{if (adapter != null)
{
// Get the root of the target model:
TargetRoot target = adapter.ModelRoot;
// The source DSL Definition has a class "SourceElement" embedded under the root.
// (Let's assume they're all in the same model file):
foreach (SourceElement sourceElement in source.Elements)
{
// In the source DSL Definition, each SourceElement has a MBR property:
ModelBusReference elementReference = sourceElement.ReferenceToTarget;
// Resolve the target model element:
TargetElement element = adapter.ResolveElementReference<TargetElement>(elementReference);
#>
The source <#= sourceElement.Name #> is linked to: <#= element.Name #> in target model: <#= target.Name
#>.
<#
}
}}
// Other useful code: this.Host.ResolvePath(filename) gets an absolute filename
// from a path that is relative to the text template.
#>

When this text template is executed, the SourceDsl directive loads the file Sample.source . The template can access
the elements of that model, starting from this.ModelRoot . The code can use the domain classes and properties of
that DSL.
In addition, the template can resolve ModelBus References. Where the references point to the Target model, the
assembly directives let the code use the domain classes and properties of that model's DSL.
If you do not use a directive that is generated by a DSL project, you should also include the following.

<#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.11.0" #>


<#@ assembly name = "Microsoft.VisualStudio.TextTemplating.Modeling.11.0" #>

Use this.ModelBus to obtain access to the ModelBus.

Walkthrough: Testing a Text Template That Uses ModelBus


In this walkthrough, you follow these steps:
1. Construct two DSLs. One DSL, the Consumer, has a ModelBusReference property that can refer to the other
DSL, the Provider.
2. Create two ModelBus Adapters in the Provider: one for access by text templates, the other for ordinary
code.
3. Create instance models of the DSLs in a single experimental project.
4. Set a domain property in one model to point to the other model.
5. Write a double-click handler that opens the model that is pointed to.
6. Write a text template that can load the first model, follow the reference to the other model, and read the
other model.
Construct a DSL that is accessible to ModelBus
1. Create a new DSL solution. For this example, select the Task Flow solution template. Set the language name
to MBProvider and the file name extension to ".provide".
2. In the DSL Definition diagram, right-click a blank part of the diagram that is not near the top, and then click
Enable Modelbus.
If you do not see Enable Modelbus, you must download and install the VMSDK ModelBus extension.
Find it on the VMSDK site: Visualization and Modeling SDK.
3. In the Enable Modelbus dialog box, select Expose this DSL to the ModelBus, and then click OK.
A new project, ModelBusAdapter , is added to the solution.
You now have a DSL that can be accessed by text templates through ModelBus. References to it can be
resolved in the code of commands, event handlers, or rules, all of which operate in the AppDomain of the
model file editor. However, text templates run in a separate AppDomain and cannot access a model when it
is being edited. If you want to access ModelBus references to this DSL from a text template, you must have
a separate ModelBusAdapter.
To create a ModelBus Adapter that is configured for Text Templates
1. In Windows Explorer, copy and paste the folder that contains ModelBusAdapter.csproj.
Name the folder T4ModelBusAdapter.
Rename the project file T4ModelBusAdapter.csproj.
2. In Solution Explorer, add T4ModelBusAdapter to the MBProvider solution. Right-click the solution node,
point to Add, and then click Existing Project.
3. Right-click the T4ModelBusAdapter project node and then click Properties. In the project properties
window, change the Assembly Name and Default Namespace to Company.MBProvider.T4ModelBusAdapters
.
4. In each *.tt file in T4ModelBusAdapter, insert "T4" into the last part of the namespace, so that the line
resembles the following.
namespace <#= CodeGenerationUtilities.GetPackageNamespace(this.Dsl) #>.T4ModelBusAdapters

5. In the DslPackage project, add a project reference to T4ModelBusAdapter .


6. In DslPackage\source.extension.tt, add the following line under <Content> .
<MefComponent>|T4ModelBusAdapter|</MefComponent>

7. In the T4ModelBusAdapter project, add a reference to:


Microsoft.VisualStudio.TextTemplating.Modeling.11.0
8. Open T4ModelBusAdapter\AdapterManager.tt:
a. Change the base class of AdapterManagerBase to VsTextTemplatingModelingAdapterManager. This
part of the file now resembles the following.
namespace <#= CodeGenerationUtilities.GetPackageNamespace(this.Dsl) #>.T4ModelBusAdapters
{
/// <summary>
/// Adapter manager base class (double derived pattern) for the <#= dslName #> Designer
/// </summary>
public partial class <#= dslName #>AdapterManagerBase
: Microsoft.VisualStudio.TextTemplating.Modeling.VsTextTemplatingModelingAdapterManager
{

b. Near the end of the file, insert the following additional attribute in front of class AdapterManager.
[Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]

The result resembles the following.

/// <summary>
/// ModelBus modeling adapter manager for a <#= dslName #>Adapter model adapter
/// </summary>
[Mef::Export(typeof(DslIntegration::ModelBusAdapterManager))]
[Mef::ExportMetadata(DslIntegration::CompositionAttributes.AdapterIdKey,<#= dslName
#>Adapter.AdapterId)]
[DslIntegration::HostSpecific(DslIntegrationShell::VsModelingAdapterManager.HostName)]
[Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]
public partial class <#= dslName #>AdapterManager : <#= dslName #>AdapterManagerBase
{
}

9. Click Transform All Templates in the title bar of Solution Explorer.


10. Rebuild the solution. Click F5.
11. Verify that the DSL is working by pressing F5. In the experimental project, open Sample.provider . Close the
experimental instance of Visual Studio.
ModelBus References to this DSL can now be resolved in text templates and also in ordinary code.
Construct a DSL with a ModelBus Reference domain property
1. Create a new DSL by using the Minimal Language solution template. Name the language MBConsumer and
set the file name extension to ".consume".
2. In the DSL project, add a reference to the MBProvider DSL assembly. Right-click MBConsumer\Dsl\References
and then click Add Reference. In the Browse tab, locate
MBProvider\Dsl\bin\Debug\Company.MBProvider.Dsl.dll

This enables you to create code that uses the other DSL. If you want to create references to several DSLs,
add them also.
3. In the DSL Definition diagram, right-click the diagram and then click Enable ModelBus. In the dialog box,
select Enable this DSL to Consume the ModelBus.
4. In the class ExampleElement , add a new domain property MBR , and in the Properties window, set its type to
ModelBusReference .
5. Right-click the domain property on the diagram and then click Edit ModelBusReference specific
properties. In the dialog box, select a model element.
Set the file dialog filter to the following.
Provider File|*.provide

The substring after "|" is a filter for the file selection dialog box. You could set it to allow any files by using
*.*
In the Model Element type list, enter the names of one ore more domain classes in the provider DSL (for
example, Company.MBProvider.Task). They can be abstract classes. If you leave the list blank, the user can
set the reference to any element.
6. Close the dialog and Transform All Templates.
You have created a DSL that can contain references to elements in another DSL.
Create a ModelBus reference to another file in the solution
1. In the MBConsumer solution, press CTRL+F5. An experimental instance of Visual Studio opens in the
MBConsumer\Debugging project.
2. Add a copy of Sample.provide to the MBConsumer\Debugging project. This is necessary because a
ModelBus reference must refer to a file in the same solution.
a. Right-click the Debugging project, point to Add, and then click Existing Item.
b. In the Add Item dialog, set the filter to All Files (*.*).
c. Navigate to MBProvider\Debugging\Sample.provide and then click Add.
3. Open Sample.consume .
4. Click one example shape, and in the Properties window, click [...] in the MBR property. In the dialog box,
click Browse and select Sample.provide . In the elements window, expand the type Task and select one of
the elements.
5. Save the file.
(Do not yet close the experimental instance of Visual Studio.)
You have created a model that contains a ModelBus reference to an element in another model.
Resolve a ModelBus Reference in a text template
1. In the experimental instance of Visual Studio, open a sample text template file. Set its content as follows.
<#@ template debug="true" hostspecific="true" language="C#"
inherits="Microsoft.VisualStudio.TextTemplating.Modeling.ModelBusEnabledTextTransformation" #>
<#@ MBConsumer processor="MBConsumerDirectiveProcessor" requires="fileName='Sample.consume'" #>
<#@ output extension=".txt" #>
<#@ assembly name = "Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0" #>
<#@ assembly name = "Company.MBProvider.Dsl.dll" #>
<#@ import namespace="Microsoft.VisualStudio.Modeling.Integration" #>
<#@ import namespace="Company.MBProvider" #>
<#
// Property provided by the Consumer directive processor:
ExampleModel consumerModel = this.ExampleModel;
// Iterate through Consumer model, listing the elements:
foreach (ExampleElement element in consumerModel.Elements)
{
#>
<#= element.Name #>
<#
if (element.MBR != null)
using (ModelBusAdapter adapter = this.ModelBus.CreateAdapter(element.MBR))
{
// If we allowed multiple types or DSLs in the MBR, discover type here.
Task task = adapter.ResolveElementReference<Task>(element.MBR);
#>
<#= element.Name #> is linked to Task: <#= task==null ? "(null)" : task.Name #>
<#
}
}
#>

Notice the following points:


a. The hostSpecific and inherits attributes of the template directive must be set.
b. The consumer model is accessed in the usual manner through the directive processor that was
generated in that DSL.
c. The assembly and import directives must be able to access ModelBus and the types of the provider
DSL.
d. If you know that many MBRs are linked to the same model, it is better to call CreateAdapter only one
time.
2. Save the template. Verify that the resulting text file resembles the following.

ExampleElement1
ExampleElement2
ExampleElement2 is linked to Task: Task2

Resolve a ModelBus reference in a gesture handler


1. Close the experimental instance of Visual Studio, if it is running.
2. Add a file that is named MBConsumer\Dsl\Custom.cs and set its content to the following.
namespace Company.MB2Consume
{
using Microsoft.VisualStudio.Modeling.Integration;
using Company.MB3Provider;

public partial class ExampleShape


{
public override void OnDoubleClick(Microsoft.VisualStudio.Modeling.Diagrams.DiagramPointEventArgs
e)
{
base.OnDoubleClick(e);
ExampleElement element = this.ModelElement as ExampleElement;
if (element.MBR != null)
{
IModelBus modelbus = this.Store.GetService(typeof(SModelBus)) as IModelBus;
using (ModelBusAdapter adapter = modelbus.CreateAdapter(element.MBR))
{
Task task = adapter.ResolveElementReference<Task>(element.MBR);
// Open a window on this model:
ModelBusView view = adapter.GetDefaultView();
view.Show();
view.SetSelection(element.MBR);
}
}
}
}
}

3. Press CTRL+F5.
4. In the experimental instance of Visual Studio, open Debugging\Sample.consume .
5. Double-click one shape.
If you have set the MBR on that element, the referenced model opens and the referenced element is
selected.

See Also
Integrating Models by using Visual Studio Modelbus
Code Generation and T4 Text Templates

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Walkthrough: Debugging a Text Template that
Accesses a Model
10/18/2017 4 min to read Edit Online

When you modify or add text templates in a domain-specific language solution, you may get errors when the
engine transforms the template to source code or when it compiles the generated code. The following walkthrough
demonstrates some of the things you can do to debug a text template.

NOTE
For more information about text templates in general, see Code Generation and T4 Text Templates. For more information
about debugging text templates, see Walkthrough: Debugging a Text Template.

Creating a Domain-Specific Language Solution


In this procedure, you create a domain-specific language solution that has the following characteristics:
Name: DebuggingTestLanguage
Solution template: Minimal Language
File extension: .ddd
Company name: Fabrikam
For more information about creating a domain-specific language solution, see How to: Create a Domain-
Specific Language Solution.

Creating a text template


Add a text template to your solution.
To create a text template
1. Build the solution and start running it in the debugger. (On the Build menu, click Rebuild Solution, and
then on the Debug menu, click Start Debugging.) A new instance of Visual Studio opens the Debugging
project.
2. Add a text file named DebugTest.tt to the Debugging project.
3. Make sure that the Custom Tool property of DebugTest.tt is set to TextTemplatingFileGenerator .

Debugging directives that access a model from a text template


Before you can access a model from the statements and expressions in a text template, you must first call a
generated directive processor. Calling the generated directive processor makes the classes in your model available
to the text template code as properties. For more information, see Accessing Models from Text Templates.
In the following procedures, you will debug an incorrect directive name and an incorrect property name.
To debug an incorrect directive name
1. Replace the code in DebugTest.tt with the following code:
NOTE
The code contains an error. You are introducing the error in order to debug it.

<#@ template language="C#"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ modelRoot processor="DebuggingTestLanguageDirectiveProcessor" requires="fileName='Sample.ddd'"
provides="ExampleModel=ExampleModel" #>

Model: <#= this.ExampleModel #>


<#
foreach (ExampleElement element in this.ExampleModel.Elements)
{
#>
Element: <#= element.Name #>
<#
}
#>

<#@ template language="VB"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ modelRoot processor="DebuggingTestLanguageDirectiveProcessor" requires="fileName='Sample.ddd'"
provides="ExampleModel=ExampleModel" #>

Model: <#= Me.ExampleModel #>


<#
For Each element as ExampleElement in Me.ExampleModel.Elements
#>
Element: <#= element.Name #>
<#
Next
#>

2. In Solution Explorer, right-click DebugTest.tt, and then click Run Custom Tool.
The Error List window displays this error:
The processor named 'DebuggingTestLanguageDirectiveProcessor' does not support the directive
named 'modelRoot'. The transformation will not be run.
In this case, the directive call contains an incorrect directive name. You have specified modelRoot as the
directive name, but the correct directive name is DebuggingTestLanguage .
3. Double-click the error in the Error List window to jump to the code.
4. To correct the code, change the directive name to DebuggingTestLanguage .
The change is highlighted.

<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"


requires="fileName='Sample.ddd'" provides="ExampleModel=ExampleModel" #>

<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"


requires="fileName='Sample.ddd'" provides="ExampleModel=ExampleModel" #>

5. In Solution Explorer, right-click DebugTest.tt, and then click Run Custom Tool.
Now the system transforms the text template and generates the corresponding output file. You will not see
any errors in the Error List window.
To debug an incorrect property name
1. Replace the code in DebugTest.tt with the following code:

NOTE
The code contains an error. You are introducing the error in order to debug it.

<#@ template language="C#"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"
requires="fileName='Sample.ddd'" provides="ExampleModel=LibraryModel" #>

Model: <#= this.ExampleModel #>


<#
foreach (ExampleElement element in this.ExampleModel.Elements)
{
#>
Element: <#= element.Name #>
<#
}
#>

<#@ template language="VB"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"
requires="fileName='Sample.ddd'" provides="ExampleModel=LibraryModel" #>

Model: <#= Me.ExampleModel #>


<#
For Each element as ExampleElement in Me.ExampleModel.Elements
#>
Element: <#= element.Name #>
<#
Next
#>

2. In the Solution Explorer, right-click DebugTest.tt, and then click Run Custom Tool.
The Error List window appears and displays one of these errors:
(C#)
Compiling transformation: Microsoft.VisualStudio.TextTemplating<GUID>.
GeneratedTextTransformation' does not contain a definition for 'ExampleModel'
(Visual Basic)
Compiling transformation: 'ExampleModel' is not a member of
'Microsoft.VisualStudio.TextTemplating<GUID>.GeneratedTextTransformation'.
In this case, the text template code contains an incorrect property name. You have specified ExampleModel as
the property name, but the correct property name is LibraryModel . You can find the correct property name
in the provides parameter, as shown in the following code:
<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"
requires="fileName='Sample.ddd'" provides="ExampleModel=LibraryModel" #>

3. Double-click the error in the Error List window to jump to the code.
4. To correct the code, change the property name to LibraryModel in the text template code.
The changes are highlighted.

<#@ template language="C#"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"
requires="fileName='Sample.ddd'" provides="ExampleModel=LibraryModel" #>

Model: <#= this.LibraryModel #>


<#
foreach (ExampleElement element in this.LibraryModel.Elements)
{
#>
Element: <#= element.Name #>
<#
}
#>

<#@ template language="VB"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation"#>
<#@ output extension=".txt" #>
<#@ DebuggingTestLanguage processor="DebuggingTestLanguageDirectiveProcessor"
requires="fileName='Sample.ddd'" provides="ExampleModel=LibraryModel" #>

Model: <#= Me.LibraryModel #>


<#
For Each element as ExampleElement in Me.LibraryModel.Elements
#>
Element: <#= element.Name #>
<#
Next
#>

5. In Solution Explorer, right-click DebugTest.tt, and then click Run Custom Tool.
Now the system transforms the text template and generates the corresponding output file. You will not see
any errors in the Error List window.
Walkthrough: Connecting a Host to a Generated
Directive Processor
10/18/2017 7 min to read Edit Online

You can write your own host that processes text templates. A basic custom host is demonstrated in Walkthrough:
Creating a Custom Text Template Host. You could extend that host to add functions such as generating multiple
output files.
In this walkthrough, you expand your custom host so that it supports text templates that call directive processors.
When you define a domain-specific language, it generates a directive processor for the domain model. The directive
processor makes it easier for users to write templates that access the model, reducing the need to write assembly
and import directives in the templates.

WARNING
This walkthrough builds on Walkthrough: Creating a Custom Text Template Host. Perform that walkthrough first.

This walkthrough includes the following tasks:


Using Domain-Specific Language Tools to generate a directive processor that is based on a domain model.
Connecting a custom text template host to the generated directive processor.
Testing the custom host with the generated directive processor.

Prerequisites
To define a DSL, you must have installed the following components:

Visual Studio http://go.microsoft.com/fwlink/?LinkId=185579

Visual Studio SDK http://go.microsoft.com/fwlink/?LinkId=185580

Visual Studio Visualization and Modeling SDK

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

In addition, you must have the custom text template transformation created in Walkthrough: Creating a Custom
Text Template Host.

Using Domain-Specific Language Tools to Generate a Directive


Processor
In this walkthrough, you use the Domain-Specific Language Designer Wizard to create a domain-specific language
for the solution DSLMinimalTest.
To use Domain-Specific Language Tools to generate a directive processor that is based on a domain model
1. Create a domain-specific language solution that has the following characteristics:
Name: DSLMinimalTest
Solution template: Minimal Language
File extension: min
Company name: Fabrikam
For more information about creating a domain-specific language solution, see How to: Create a
Domain-Specific Language Solution.
2. On the Build menu, click Build Solution.

IMPORTANT
This step generates the directive processor and adds the key for it in the registry.

3. On the Debug menu, click Start Debugging.


A second instance of Visual Studio opens.
4. In the experimental build, in Solution Explorer, double-click the file sample.min.
The file opens in the designer. Notice that the model has two elements, ExampleElement1 and
ExampleElement2, and a link between them.
5. Close the second instance of Visual Studio.
6. Save the solution, and then close the Domain-Specific Language Designer.

Connecting a Custom Text Template Host to a Directive Processor


After you generate the directive processor, you connect the directive processor and the custom text template host
that you created in Walkthrough: Creating a Custom Text Template Host.
To connect a custom text template host to the generated directive processor
1. Open the CustomHost solution.
2. On the Project menu, click Add Reference.
The Add Reference dialog box opens with the .NET tab displayed.
3. Add the following references:
Microsoft.VisualStudio.Modeling.Sdk.11.0
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0
Microsoft.VisualStudio.TextTemplating.11.0
Microsoft.VisualStudio.TextTemplating.Interfaces.11.0
Microsoft.VisualStudio.TextTemplating.Modeling.11.0
Microsoft.VisualStudio.TextTemplating.VSHost.11.0
4. At the top of Program.cs or Module1.vb, add the following line of code:

using Microsoft.Win32;
Imports Microsoft.Win32

5. Locate the code for the property StandardAssemblyReferences , and replace it with the following code:

NOTE
In this step, you add references to the assemblies that are required by the generated directive processor that your
host will support.

//the host can provide standard assembly references


//the engine will use these references when compiling and
//executing the generated transformation class
//--------------------------------------------------------------
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
//if this host searches standard paths and the GAC
//we can specify the assembly name like this:
//"System"
//since this host only resolves assemblies from the
//fully qualified path and name of the assembly
//this is a quick way to get the code to give us the
//fully qualified path and name of the System assembly
//---------------------------------------------------------
typeof(System.Uri).Assembly.Location,
typeof(System.Uri).Assembly.Location,
typeof(Microsoft.VisualStudio.Modeling.ModelElement).Assembly.Location,
typeof(Microsoft.VisualStudio.Modeling.Diagrams.BinaryLinkShape).Assembly.Location,
typeof(Microsoft.VisualStudio.TextTemplating.VSHost.ITextTemplating).Assembly.Location,

typeof(Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation).Assembly.Location

};
}
}

6. Locate the code for the function ResolveDirectiveProcessor , and replace it with the following code:

IMPORTANT
This code contains hard-coded references to the name of the generated directive processor to which you want to
connect. You could easily make this more general, in which case it looks for all directive processors listed in the
registry and tries to find a match. In that case, the host would work with any generated directive processor.

//the engine calls this method based on the directives the user has
//specified it in the text template
//this method can be called 0, 1, or more times
//---------------------------------------------------------------------
public Type ResolveDirectiveProcessor(string processorName)
{
//check the processor name, and if it is the name of the processor the
//host wants to support, return the type of the processor
//---------------------------------------------------------------------
if (string.Compare(processorName, "DSLMinimalTestDirectiveProcessor",
StringComparison.InvariantCultureIgnoreCase) == 0)
{
try
try
{
string keyName =
@"Software\Microsoft\VisualStudio\10.0Exp_Config\TextTemplating\DirectiveProcessors\DSLMinimalTestDirect
iveProcessor";
using (RegistryKey specificKey = Registry.CurrentUser.OpenSubKey(keyName))
{
if (specificKey != null)
{
List<string> names = new List<String>(specificKey.GetValueNames());
string classValue = specificKey.GetValue("Class") as string;
if (!string.IsNullOrEmpty(classValue))
{
string loadValue = string.Empty;
System.Reflection.Assembly processorAssembly = null;
if (names.Contains("Assembly"))
{
loadValue = specificKey.GetValue("Assembly") as string;
if (!string.IsNullOrEmpty(loadValue))
{
//the assembly must be installed in the GAC
processorAssembly = System.Reflection.Assembly.Load(loadValue);
}
}
else if (names.Contains("CodeBase"))
{
loadValue = specificKey.GetValue("CodeBase") as string;
if (!string.IsNullOrEmpty(loadValue))
{
//loading local assembly
processorAssembly =
System.Reflection.Assembly.LoadFrom(loadValue);
}
}
if (processorAssembly == null)
{
throw new Exception("Directive Processor not found");
}
Type processorType = processorAssembly.GetType(classValue);
if (processorType == null)
{
throw new Exception("Directive Processor not found");
}
return processorType;
}
}
}
}
catch (Exception e)
{
//if the directive processor can not be found, throw an error
throw new Exception("Directive Processor not found");
}
}

//if the directive processor is not one this host wants to support
throw new Exception("Directive Processor not supported");
}

7. On the File menu, click Save All.


8. On the Build menu, click Build Solution.

Testing the Custom Host with the Directive Processor


To test the custom text template host, first you must write a text template that calls the generated directive
processor. Then you run the custom host, pass to it the name of the text template, and verify that the directive is
processed correctly.
To create a text template to test the custom host
1. Create a text file, and name it TestTemplateWithDP.tt . You can use any text editor, such as Notepad, to create
the file.
2. Add the following to the text file:

NOTE
The programming language of the text template does not need to match that of the custom host.

Text Template Host Test

<#@ template debug="true"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>
<# //this is the call to the examplemodel directive in the generated directive processor #>
<#@ DSLMinimalTest processor="DSLMinimalTestDirectiveProcessor" requires="fileName='<Your
Path>\Sample.min'" provides="ExampleModel=ExampleModel" #>
<# //uncomment this line to test that the host allows the engine to set the extension #>
<# //@ output extension=".htm" #>

<# //uncomment this line if you want to see the generated transformation class #>
<# //System.Diagnostics.Debugger.Break(); #>
<# //this code uses the results of the examplemodel directive #>
<#
foreach ( ExampleElement box in this.ExampleModel.Elements )
{
WriteLine("Box: {0}", box.Name);

foreach (ExampleElement linkedTo in box.Targets)


{
WriteLine("Linked to: {0}", linkedTo.Name);
}

foreach (ExampleElement linkedFrom in box.Sources)


{
WriteLine("Linked from: {0}", linkedFrom.Name);
}

WriteLine("");
}
#>
Text Template Host Test

<#@ template debug="true" language="VB"


inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>

<# 'this is the call to the examplemodel directive in the generated directive processor #>
<#@ DSLMinimalTest processor="DSLMinimalTestDirectiveProcessor" requires="fileName='<Your
Path>\Sample.min'" provides="ExampleModel=ExampleModel" #>

<# 'Uncomment this line to test that the host allows the engine to set the extension. #>
<# '@ output extension=".htm" #>

<# 'Uncomment this line if you want to see the generated transformation class. #>
<# 'System.Diagnostics.Debugger.Break() #>

<# 'this code uses the results of the examplemodel directive #>

<#
For Each box as ExampleElement In Me.ExampleModel.Elements

WriteLine("Box: {0}", box.Name)

For Each LinkedTo as ExampleElement In box.Targets


WriteLine("Linked to: {0}", LinkedTo.Name)
Next

For Each LinkedFrom as ExampleElement In box.Sources


WriteLine("Linked from: {0}", LinkedFrom.Name)
Next

WriteLine("")

Next
#>

3. In the code, replace <YOUR PATH> with the path of the Sample.min file from the design-specific language
you created in the first procedure.
4. Save and close the file.
To test the custom host
1. Open a Command Prompt window.
2. Type the path of the executable file for the custom host, but do not press ENTER yet.
For example, type:
<YOUR PATH>CustomHost\bin\Debug\CustomHost.exe

NOTE
Instead of typing the address, you can browse to the file CustomHost.exe in Windows Explorer, and then drag the
file into the Command Prompt window.

3. Type a space.
4. Type the path of the text template file, and then press ENTER.
For example, type:
<YOUR PATH>TestTemplateWithDP.txt
NOTE
Instead of typing the address, you can browse to the file TestTemplateWithDP.txt in Windows Explorer, and then
drag the file into the Command Prompt window.

The custom host application runs and starts the text template transformation process.
5. In Windows Explorer, browse to the folder that contains the file TestTemplateWithDP.txt.
The folder also contains the file TestTemplateWithDP1.txt.
6. Open this file to see the results of the text template transformation.
The results of the generated text output appears and should look like this:

Text Template Host Test

Box: ExampleElement1
Linked to: ExampleElement2

Box: ExampleElement2
Linked from: ExampleElement1

See Also
Walkthrough: Creating a Custom Text Template Host
The DslTextTransform Command
10/18/2017 1 min to read Edit Online

DslTextTransform.cmd is a script that calls TextTransform.exe and runs it with common options. You can use
DslTextTransformation.cmd to automate a nightly build of your Domain-Specific Language Tools projects. For more
information, see Generating Files with the TextTransform Utility.
DslTextTransform.cmd is located in the following directory:
<Visual Studio SDK Installation Path>\VisualStudioIntegration\Tools\Bin
You can specify the following arguments as input to DslTextTransform.cmd:
The output directory of the domain model project.
The output directory of the designer definition project.
The location of the text template file.
DslTextTransform.cmd processes the specified text template file using the default directive processors and
assemblies. If you create custom directive processors, you can create your own batch file that calls
TextTransform.exe. In this batch file, you can specify your assemblies and the associated custom directive
processors.
Customizing File Storage and XML Serialization
10/18/2017 12 min to read Edit Online

When the user saves an instance, or model, of a domain-specific language (DSL) in Visual Studio, an XML file is
created or updated. The file can be reloaded to recreate the model in the Store.
You can customize the serialization scheme by adjusting the settings under Xml Serialization Behavior in DSL
Explorer. There is a node under Xml Serialization Behavior for every domain class, property, and relationship.
The relationships are located under their source classes. There are also nodes corresponding to the shape,
connector, and diagram classes.
You can also write program code for more advanced customization.

NOTE
If you want to save the model in a particular format, but you do not need to reload it from that form, consider using text
templates to generate output from the model, instead of a custom serialization scheme. For more information, see
Generating Code from a Domain-Specific Language.

Model and Diagram Files


Each model is usually saved in two files:
The model file has a name such as Model1.mydsl. It stores the model elements and relationships and
their properties. The file extension such as .mydsl is determined by the FileExtension property of the
Editor node in the DSL Definition.
The diagram file has a name such as Model1.mydsl.diagram. It stores the shapes, connectors, and their
positions, colors, line thicknesses, and other details of the appearance of the diagram. If the user deletes a
.diagram file, the essential information in the model is not lost. Only the layout of the diagram is lost.
When the model file is opened, a default set of shapes and connectors will be created.
To change the file extension of a DSL
1. Open the DSL Definition. In DSL Explorer, click the Editor node.
2. In the Properties window, edit the FileExtension property. Do not include the initial "." of the file name
extension.
3. In Solution Explorer, change the name of the two item template files in
DslPackage\ProjectItemTemplates. These files have names that follow this format:
myDsl.diagram

myDsl.myDsl

The Default Serialization Scheme


To create an example for this topic, the following DSL Definition was used.
This DSL was used to create a model that has the following appearance on the screen.

This model was saved and then re-opened in the XML text editor:

<?xml version="1.0" encoding="utf-8"?>


<familyTreeModel xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core"
dslVersion="1.0.0.0" Id="f817b728-e920-458e-bb99-98edc469d78f"
xmlns="http://schemas.microsoft.com/dsltools/FamilyTree">
<people>
<person name="Henry VIII" birthYear="1491" deathYear="1547" age="519">
<children>
<personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />
<personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Mary" />
</children>
</person>
<person name="Elizabeth I" birthYear="1533" deathYear="1603" age="477" />
<person name="Mary" birthYear="1515" deathYear="1558" age="495" />
</people>
</familyTreeModel>

Notice the following points about the serialized model:


Each XML node has a name that is the same as a domain class name, except that the initial letter is
lowercase. For example, familyTreeModel and person .
Domain properties such as Name and BirthYear are serialized as attributes in the XML nodes. Again, the
initial character of the property name is converted to lowercase.
Each relationship is serialized as an XML node nested inside the source end of the relationship. The node
has the same name as the source role property, but with a lower case initial character.
For example, in the DSL Definition, a role that is named People is sourced at the FamilyTree class. In the
XML, this is represented by the node named people nested inside the familyTreeModel node.
The target end of each embedding relationship is serialized as a node nested under the relationship. For
example, the people node contains several person nodes.
The target end of each reference relationship is serialized as a moniker, which encodes a reference to the
target element.
For example, under a person node, there can be a children relationship. This node contains monikers
such as:

<personMoniker name="/f817b728-e920-458e-bb99-98edc469d78f/Elizabeth I" />

Understanding Monikers
Monikers are used to represent cross-references between different parts of the model and diagram files. They are
also used in the .diagram file to refer to nodes in the model file. There are two forms of moniker:
Id monikers quote the GUID of the target element. For example:

<personShapeMoniker Id="f79734c0-3da1-4d72-9514-848fa9e75157" />

Qualified key monikers identify the target element by the value of a designated domain property called the
moniker key. The moniker of the target element is prefixed by the moniker of its parent element in the tree
of embedding relationships.
The following examples are taken from a DSL in which there is a domain class named Album, which has an
embedding relationship to a domain class named Song:

<albumMoniker title="/My Favorites/Jazz after Teatime" />


<songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />

Qualified key monikers will be used if the target class has a domain property for which the option Is
Moniker Key is set to true in Xml Serialization Behavior. In the example, this option is set for domain
properties named "Title" in the domain classes "Album" and "Song".
Qualified key monikers are easier to read than ID monikers. If you intend the XML of your model files to be
read by people, consider using qualified key monikers. However, it is possible for the user to set more than
one element to have the same moniker key. Duplicate keys could cause the file not to reload correctly.
Therefore, if you define a domain class that is referenced using qualified key monikers, you should
consider ways of preventing the user from saving a file that has duplicate monikers.
To set a domain class to be referenced by ID monikers
1. Make sure that Is Moniker Key is false for every domain property in the class and its base classes.
a. In DSL Explorer, expand Xml Serialization Behavior\Class Data\<the domain class>\Element
Data.
b. Verify that Is Moniker Key is false for every domain property.
c. If the domain class has a base class, repeat the procedure in that class.
2. Set Serialize Id = true for the domain class.
This property can be found under Xml Serialization Behavior.
To set a domain class to be referenced by qualified key monikers
Set Is Moniker Key for a domain property of an existing domain class. The type of the property must be
string .
1. In DSL Explorer, expand Xml Serialization Behavior\Class Data\<the domain class>\Element
Data, and then select the domain property.
2. In the Properties window, set Is Moniker Key to true .
- or -
Create a new domain class using the Named Domain Class tool.
This tool creates a new class that has a domain property called Name. The Is Element Name and Is
Moniker Key properties of this domain property are initialized to true .
- or -
Create an Inheritance relationship from the domain class to another class that has a moniker key property.
Avoiding Duplicate Monikers
If you use qualified key monikers, it is possible that two elements in a user's model could have the same value in
the key property. For example, if your DSL has a class Person that has a property Name, the user could set the
Names of two elements to be the same. Although the model could be saved to file, it would not reload correctly.
There are several methods that help avoid this situation:
Set Is Element Name = true for the key domain property. Select the domain property on the DSL
Definition diagram and then set the value in the Properties window.
When the user creates a new instance of the class, this value causes the domain property to be
automatically assigned a different value. The default behavior adds a number to the end of the class name.
This does not prevent the user from changing the name to a duplicate, but it helps in the case when the
user does not set the value before saving the model.
Enable validation for the DSL. In DSL Explorer, select Editor\Validation, and set the Uses... properties to
true .

There is an automatically-generated validation method that checks for ambiguities. The method is in the
Load validation category. This makes sure that the user will be warned that it might not be possible to re-
open the file.
For more information, see Validation in a Domain-Specific Language.
Moniker Paths and Qualifiers
A qualified key moniker ends with the moniker key, and is prefixed with the moniker of its parent in the
embedding tree. For example, if the moniker of an Album is:

<albumMoniker title="/My Favorites/Jazz after Teatime" />

Then one of the Songs in that Album could be:

<songMoniker title="/My Favorites/Jazz after Teatime/Hot tea" />

However, if Albums are referenced by ID instead, then the monikers would be as follows:

<albumMoniker Id="77472c3a-9bf9-4085-976a-d97a4745237c" />


<songMoniker title="/77472c3a-9bf9-4085-976a-d97a4745237c/Hot tea" />
Notice that because a GUID is unique, it is never prefixed by the moniker of its parent.
If you know that a particular domain property will always have a unique value within a model, you can set Is
Moniker Qualifier to true for that property. This will cause it to be used as a qualifier, without using the
moniker of the parent. For example, if you set both Is Moniker Qualifier and Is Moniker Key for the Title
domain property of the Album class, the model's name or identifier is not used in monikers for Album and its
embedded children:

<albumMoniker name="Jazz after Teatime" />


<songMoniker title="/Jazz after Teatime/Hot tea" />

Customizing the structure of the XML


To make the following customizations, expand the Xml Serialization Behavior node in DSL Explorer. Under a
domain class, expand the Element Data node to see the list of properties and relationships that are sourced at this
class. Select a relationship and adjust its options in the Properties window.
Set Omit Element to true to omit the source role node, leaving just the list of target elements. You should
not set this option if there is more than one relationship between the source and target classes.

<familyTreeModel ...>
<!-- The following node is omitted by using Omit Element: -->
<!-- <people> -->
<person name="Henry VIII" .../>
<person name="Elizabeth I" .../>
<!-- </people> -->
</familyTreeModel>

Set Use Full Form to embed the target nodes in nodes representing the relationship instances. This option
is set automatically when you add domain properties to a domain relationship.

<familyTreeModel ...>
<people>
<!-- The following node is inserted by using Use Full Form: -->
<familyTreeModelHasPeople myRelationshipProperty="x1">
<person name="Henry VIII" .../>
</familyTreeModelHasPeople>
<familyTreeModelHasPeople myRelationshipProperty="x2">
<person name="Elizabeth I" .../>
</familyTreeModelHasPeople>
</people>
</familyTreeModel>

Set Representation = Element to have a domain property saved as an element instead of as an attribute
value.

<person name="Elizabeth I" birthYear="1533">


<deathYear>1603</deathYear>
</person>

To change the order in which attributes and relationships are serialized, right-click an item under Element
Data, and use the Move Up or Move Down menu commands.
Major customization using program code
You can replace parts or all of the serialization algorithms.
We recommend that you study the code in Dsl\Generated Code\Serializer.cs and SerializationHelper.cs.
To customize the serialization of a particular class
1. Set Is Custom in the node for that class under Xml Serialization Behavior.
2. Transform All Templates, build the solution, and investigate the resulting compilation errors. Comments
near each error explain what code you have to provide.
To provide your own serialization for the whole model
1. Override methods in Dsl\GeneratedCode\SerializationHelper.cs

Options in Xml Serialization Behavior


In DSL Explorer, the Xml Serialization Behavior node contains a child node for each domain class, relationship,
shape, connector and diagram class. Under each of those nodes is a list of properties and relationships sourced at
that element. Relationships are represented both in their own right and under their source classes.
The following table summarizes the options that you can set in this section of the DSL Definition. In each case,
select an element in DSL Explorer, and set the options in the Properties window.
Xml Class data
These elements are found in DSL Explorer under Xml Serialization Behavior\Class Data.

Property Description

Has Custom Element Schema If True, indicates that the domain class has a custom element
schema

Is Custom Set this to True if you want to write your own serialization
and deserialization code for this domain class.

Build the solution and investigate the errors to discover


detailed instructions.

Domain Class Domain class to which this class data node applies. Read-
only.

Element Name Xml node name for elements of this class. The default value is
a lower-case version of the domain class name.

Moniker Attribute Name Name of the attribute used in moniker elements to contain
the reference. If blank, the name of the key property or id is
used.

In this example, it is "name":


<personMoniker name="/Mike Nash"/>

Moniker Element Name Name of the xml element used for monikers that refer to
elements of this class.

The default value is a lowercase version of the class name


suffixed with "Moniker". For example, personMoniker .
Moniker Type Name Name of the xsd type generated for monikers to elements of
this class. The XSD is in Dsl\Generated Code\*Schema.xsd

Serialize Id If True, the element GUID is included in the file. This must be
true if there is no property that is marked Is Moniker Key
and the DSL defines reference relationships to this class.

Type Name Name of the xml type generated in the xsd from the
designated domain class.

Notes Informal notes associated with this element

Xml Property Data


Xml Property nodes are found under the class nodes.

Property Description

Domain Property Property to which the xml serialization configuration data


applies. Read-only.

Is Moniker Key If True, the property is used as the key for creating monikers
that reference instances of this domain class.

Is Moniker Qualifier If True, the property is used for creating the qualifier in
monikers. If false, and if SerializeId is not true for this domain
class, monikers are qualified by the moniker of the parent
element in the embedding tree.

Representation If Attribute, the property is serialized as an xml attribute; if


Element, it is serialized as an element; if Ignore, it is not
serialized.

Xml Name Name used for the xml attribute or element representing the
property. By default, this is a lower-case version of the
domain property name.

Notes Informal notes associated with this element

Xml Role data


Role data nodes are found under the source class nodes.

PROPERTY DESCRIPTION

Has Custom Moniker Set this to true if you want to supply your own code for
generating and resolving monikers that traverse this
relationship.

For detailed instructions, build the solution, and then double-


click the error messages.

Domain Relationship Specifies the relationship to which these options apply. Read-
only.
PROPERTY DESCRIPTION

Omit Element If true, the XML node that corresponds to the source role is
omitted from the schema.

If there is more than one relationship between the source


and target classes, this role node distinguishes between links
that belong to the two relationships. We therefore
recommend that you do not set this option in this case.

Role Element Name Specifies the name of the XML element that is derived from
the source role. The default value is the role property name.

Use Full Form If true, each target element or moniker is enclosed in an XML
node representing the relationship. This should be set to true
if the relationship has its own domain properties.

See Also
Navigating and Updating a Model in Program Code
Generating Code from a Domain-Specific Language
Deploying Domain-Specific Language Solutions
10/18/2017 1 min to read Edit Online

You can install a domain-specific language on your own computer or on other computers. Visual Studio must
already be installed on the target computer.
MSI and VSIX Deployment of a DSL
Multiple DSLs in One Solution
VS Shell deployment
MSI and VSIX Deployment of a DSL
10/18/2017 4 min to read Edit Online

You can install a domain-specific language on your own computer or on other computers. Visual Studio must
already be installed on the target computer.

Choosing between VSIX and MSI Deployment


There are two methods of deploying a domain-specific language:

METHOD BENEFITS

VSX (Visual Studio Extension) Very easy to deploy: Copy and execute the .vsix file from the
DslPackage project.

For more information see Installing and Uninstalling a DSL by


using the VSX.

MSI (installer file) - Allows the user to open Visual Studio by double-clicking a
DSL file.
- Associates an icon with the DSL file type in the target
computer.
- Associates an XSD (XML schema) with the DSL file type. This
avoids warnings when the file is loaded into Visual Studio.

You must add a setup project to your solution to create an


MSI.

For more information, see Deploying a DSL by using an MSI


file.

Installing and Uninstalling a DSL by using the VSX


When your DSL is installed by this method, the user can open a DSL file from within Visual Studio, but the file
cannot be opened from Windows Explorer.
To install a DSL by using the VSX
1. In your computer, find the .vsix file that was built by your DSL Package project.
a. In Solution Explorer, right-click the DslPackage project, and then click Open Folder in Windows
Explorer.
b. Locate the file bin\*\YourProject.DslPackage.vsix
2. Copy the .vsix file to the target computer on which you want to install the DSL. This can be your own
computer or another one.
The target computer must have one of the editions of Visual Studio that supports DSLs at run time.
For more information, see Supported Visual Studio Editions for Visualization & Modeling SDK.
The target computer must have one of the editions of Visual Studio specified in
DslPackage\source.extensions.manifest.
3. On the target computer, double-click the .vsix file.
Visual Studio Extension Installer opens and installs the extension.
4. Start or restart Visual Studio.
5. To test the DSL, use Visual Studio to create a new file that has the extension that you defined for your DSL.
To uninstall a DSL that was installed by using VSX
1. On the Tools menu, click Extension Manager.
2. Expand Installed Extensions.
3. Select the extension in which the DSL is defined, and then click Uninstall.
Rarely, a faulty extension fails to load and creates a report in the error window, but does not appear in
Extension Manager. In that case, you can remove the extension by deleting the file from:
LocalAppData \Microsoft\VisualStudio\10.0\Extensions

Deploying a DSL in an MSI


By defining an MSI (Windows Installer) file for your DSL, you can allow users to open DSL files from Windows
Explorer. You can also associate an icon and short description with your file name extension. In addition, the MSI
can install an XSD that can be used to validate DSL files. If you want, you can add other components into the MSI
that will be installed at the same time.
For more information about MSI files and other deployment options, see Deploying Applications, Services, and
Components.
To build an MSI, you add a Setup project to your Visual Studio solution. The easiest method of creating a Setup
project is to use the CreateMsiSetupProject.tt template, which you can download from the VMSDK site.
To Deploy a DSL in an MSI
1. Set InstalledByMsi in the extension manifest. This prevents the VSX from being installed and uninstalled
except by the MSI. This is important if you will include other components in the MSI.
a. Open DslPackage\source.extension.tt
b. Insert the following line before <SupportedProducts> :

<InstalledByMsi>true</InstalledByMsi>

2. Create or edit an icon that will represent your DSL in Windows Explorer. For example, edit
DslPackage\Resources\File.ico
3. Make sure that the following attributes of your DSL are correct:
In DSL Explorer click the root node, and in Properties window, review:
Description
Version
Click the Editor node and in the Properties window, click Icon. Set the value to reference an icon file
in DslPackage\Resources, such as File.ico
On the Build menu, open Configuration Manager, and select the configuration that you want to
build, such as Release or Debug.
4. Go to Visualization and Modeling SDK home page, and from the Downloads tab, download
CreateMsiSetupProject.tt.
5. Add CreateMsiSetupProject.tt to your Dsl project.
Visual Studio will create a file named CreateMsiSetupProject.vdproj.
6. In Windows Explorer, copy Dsl\*.vdproj to a new folder named Setup.
(If you want, you can now exclude CreateMsiSetupProject.tt from your Dsl project.)
7. In Solution Explorer, add Setup\*.vdproj as an existing project.
8. On the Project menu, click Project Dependencies.
In the Project Dependencies dialog box, select the setup project.
Select the box next to DslPackage.
9. Rebuild the solution.
10. In Windows Explorer, locate the built MSI file in your Setup project.
Copy the MSI file to a computer on which you want to install your DSL. Double-click the MSI file. The
installer runs.
11. In the target computer, create a new file that has the file extension of your DSL. Verify that:
In Windows Explorer list view, the file appears with the icon and description that you defined.
When you double-click the file, Visual Studio starts, and opens the DSL file in your DSL editor.
If you prefer, you can create the Setup project manually, instead of using the text template. For a
walkthrough that includes this procedure see Chapter 5 of the Visualization and Modeling SDK Lab.
To uninstall a DSL that was installed from an MSI
1. In Windows, open the Programs and Features control panel.
2. Uninstall the DSL.
3. Restart Visual Studio.
Multiple DSLs in One Solution
10/18/2017 1 min to read Edit Online

You can package several DSLs as part of a single solution so that they are installed together.
You can use several techniques to integrate multiple DSLs. For more information, see Integrating Models by using
Visual Studio Modelbus and How to: Add a Drag-and-Drop Handler and Customizing Copy Behavior.
To build more than one DSL in the same solution
1. Create two or more DSL solutions and a VSIX project, and add all the projects to a single solution.
To create a new VSIX project: In the New Project dialog, select Visual C#, Extensibility, VSIX
Project.
Create two or more DSL solutions in the VSIX solution directory.
For each DSL, open a new instance of Visual Studio. Create the new DSL, and specify the same
solution folder as the VSIX solution.
Make sure that you create each DSL with a different filename extension.
Change the names of the Dsl and DslPackage projects so that they are all different. For example:
Dsl1 , DslPackage1 , Dsl2 , DslPackage2 .

In each DslPackage*\source.extension.tt, update this line to the correct Dsl project name:
string dslProjectName = "Dsl2";

In the VSIX solution, add the Dsl* and DslPackage* projects.


You might want to place each pair in its own solution folder.
2. Combine the VSIX manifests of the DSLs:
a. Open YourVsixProject\source.extension.manifest.
b. For each DSL, choose Add Content and add:
Dsl* project as a MEF Component
DslPackage* project as a MEF Component
DslPackage* project as a VS Package
3. Build the solution.
The resulting VSIX will install both DSLs. You can test them by using F5, or deploy
YourVsixProject\bin\Debug\*.vsix.

See Also
Integrating Models by using Visual Studio Modelbus
How to: Add a Drag-and-Drop Handler
Customizing Copy Behavior
VS Shell deployment
10/18/2017 1 min to read Edit Online

An isolated shell lets you determine which Visual Studio functionality you need to interact with your domain-
specific language and how that solution should appear. For more information about the Visual Studio isolated shell,
see Customizing the Isolated Shell. You can find more information about how to customize the isolated shell in
Customizing the Isolated Shell.
To set a Visual Studio Shell as the Deployment Target
1. In the DslPackage project, open source.extension.tt.
2. Under <SupportedProducts> insert:

<IsolatedShell Version="1.0">MyIsolatedShell</IsolatedShell>

Replace MyIsolatedShell with the name of your isolated shell package.


Creating a Windows Forms-Based Domain-Specific
Language
10/18/2017 8 min to read Edit Online

You can use Windows Forms to display the state of a domain-specific language (DSL) model, instead of using a
DSL diagram. This topic walks you through binding a Windows Form to a DSL, using the Visual Studio
Visualization and Modeling SDK.

A DSL instance, showing a Windows Form UI and the model explorer.

Creating a Windows Forms DSL


The Minimal WinForm Designer DSL template creates a minimal DSL that you can modify to suit your own
requirements.
To create a minimal WinForms DSL
1. Create a DSL from the Minimal WinForm Designer template.
In this walkthrough, the following names are assumed:

Solution and DSL name FarmApp

Namespace Company.FarmApp

2. Experiment with the initial example that the template provides:


a. Transform All Templates.
b. Build and run the sample (CTRL+F5).
c. In the experimental instance of Visual Studio, open the Sample file in the debugging project.
Notice that it is displayed in a Windows Forms control.
You can also see the elements of the model displayed in the Explorer.
Add some elements either in the form or the Explorer, and notice that they appear in the other
display.
In the main instance of Visual Studio, notice the following points about the DSL solution:
DslDefinition.dsl contains no diagram elements. This is because you will not use DSL diagrams to view
instance models of this DSL. Instead, you will bind a Windows Form to the model, and the elements on the
form will display the model.
In addition to the Dsl and DslPackage projects, the solution contains a third project named UI. UI project
contains the definition of a Windows Forms control. DslPackage depends on UI , and UI depends on Dsl
.
In the DslPackage project, UI\DocView.cs contains the code that displays the Windows Forms control that
is defined in the UI project.
The UI project contains a working sample of a form control bound to the DSL. However, it will not work
when you have changed the DSL Definition. The UI project contains:
A Windows Forms class named ModelViewControl .
A file named DataBinding.cs that contains an additional partial definition of ModelViewControl . To
see its content, in Solution Explorer, open the shortcut menu for the file and choose View Code.
About the UI Project
When you update the DSL Definition file to define your own DSL, you will have to update the control in the UI
project to display your DSL. Unlike the Dsl and DslPackage projects, the sample UI project is not generated
from DslDefinitionl.dsl . You can add .tt files to generate the code if you want, although that is not covered in this
walkthrough.

Updating the DSL Definition


The following the DSL definition is used in this walkthrough.
To update the DSL definition
1. Open DslDefinition.dsl in the DSL designer.
2. Delete ExampleElement
3. Rename the ExampleModel domain class to Farm .
Give it additional domain properties named Size of type Int32, and IsOrganic of type Boolean.

NOTE
If you delete the root domain class and then create a new root, you will have to reset the Editor Root Class property.
In DSL Explorer, select Editor. Then in the Properties window, set Root Class to Farm .

4. Use the Named Domain Class tool to create the following domain classes:
Field - Give this an additional domain property named Size .
Animal - In the Properties window, set Inheritance Modifier to Abstract.
5. Use the Domain Class tool to create the following classes:
Sheep

Goat

6. Use the Inheritance tool to make Goat and Sheep inherit from Animal .
7. Use the Embedding tool to embed Field and Animal under Farm .
8. You might want to tidy the diagram. To reduce the number of duplicate elements, use the Bring Subtree
Here command on the shortcut menu of leaf elements.
9. Transform All Templates in the toolbar of Solution Explorer.
10. Build the Dsl project.

NOTE
At this stage, the other projects will not build without errors. However, we want to build the Dsl project so that its
assembly is available to the Data Source Wizard.

Updating the UI Project


Now you can create a new user control that will display the information that is stored in the DSL model. The
easiest way to connect the user control to the model is through data bindings. The data binding adaptor type
named ModelingBindingSource is specifically designed to connect DSLs to non-VMSDK interfaces.
To define your DSL model as a data source
1. On the Data menu, choose Show Data Sources.
The Data Sources window opens.
Choose Add New Data Source. The Data Source Configuration Wizard opens.
2. Choose Object, Next.
Expand Dsl, Company.FarmApp, and select Farm, which is the root class of your model. Choose Finish.
In Solution Explorer, the UI project now contains Properties\DataSources\Farm.datasource
The properties and relationships of your model class appear in the Data Sources window.

To connect your model to a form


1. In the UI project, delete all the existing .cs files.
2. Add a new User Control file named FarmControl to the UI project.
3. In the Data Sources window, on the drop-down menu on Farm, choose Details.
Leave the default settings for the other properties.
4. Open FarmControl.cs in the design view.
Drag Farm from the Data Sources window onto FarmControl.
A set of controls appears, one for each property. The relationship properties do not generate controls.
5. Delete farmBindingNavigator. This is also automatically generated in the FarmControl designer, but it is
not useful for this application.
6. Using the toolbox, create two instances of DataGridView, and name them AnimalGridView and
FieldGridView .

NOTE
An alternative step is to drag the Animals and Fields items from the Data Sources window onto the control. This
action automatically creates data grids and bindings between the grid view and the data source. However, this
binding does not work correctly for DSLs. Therefore it is better to create the data grids and bindings manually.

7. If the Toolbox does not contain the ModelingBindingSource tool, add it. On the shortcut menu of the
Data tab, choose Choose Items. In the Choose Toolbox Items dialog, select ModelingBindingSource
from the .NET Framework Tab.
8. Using the Toolbox, create two instances of ModelingBindingSource, and name them AnimalBinding and
FieldBinding .

9. Set the DataSource property of each ModelingBindingSource to farmBindingSource.


Set the DataMember property to Animals or Fields.
10. Set the DataSource properties of AnimalGridView to AnimalBinding , and of FieldGridView to
FieldBinding .

11. Adjust the layout of the Farm control to your taste.


The ModelingBindingSource is an adapter that performs several functions that are specific to DSLs:
It wraps updates in a VMSDK Store Transaction.
For example, when the user deletes a row from the data view grid, a regular binding would result in a
transaction exception.
It ensures that, when the user selects a row, the Properties window displays the properties of the
corresponding model element, instead of the data grid row.

Schema of links between data sources and views.


To complete the bindings to the DSL
1. Add the following code in a separate code file in the UI project:

using System.ComponentModel;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;

namespace Company.FarmApp
{
partial class FarmControl
{
public IContainer Components { get { return components; } }

/// <summary>Binds the WinForms data source to the DSL model.


/// </summary>
/// <param name="nodelRoot">The root element of the model.</param>
public void DataBind(ModelElement modelRoot)
{
WinFormsDataBindingHelper.PreInitializeDataSources(this);
this.farmBindingSource.DataSource = modelRoot;
WinFormsDataBindingHelper.InitializeDataSources(this);
}
}
}

2. In the DslPackage project, edit DslPackage\DocView.tt to update the following variable definition:

string viewControlTypeName = "FarmControl";

Testing the DSL


The DSL solution can now build and run, although you might want to add further improvements later.
To test the DSL
1. Build and run the solution.
2. In the experimental instance of Visual Studio, open the Sample file.
3. In the FarmApp Explorer, open the shortcut menu on the Farm root node, and choose Add New Goat.
Goat1 appears in the Animals view.

WARNING
You must use the shortcut menu on the Farm node, not the Animals node.

4. Select the Farm root node and view its properties.


In the form view, change the Name or Size of the farm.
When you navigate away from each field in the form, the corresponding property changes in the Properties
window.

Enhancing the DSL


To make the properties update immediately
1. In the design view of FarmControl.cs, select a simple field such as Name, Size or IsOrganic.
2. In the Properties window, expand DataBindings and open (Advanced).
In the Formatting and Advanced Binding dialog, under Data Source Update Mode, choose
OnPropertyChanged.
3. Build and run the solution.
Verify that when you change the content of the field, the corresponding property of the Farm model
changes immediately.
To provide Add buttons
1. In the design view of FarmControl.cs, use the toolbox to create a button on the form.
Edit the name and text of the button, for example to New Sheep .
2. Open the code behind the button (for example by double-clicking it).
Edit it as follows:
private void NewSheepButton_Click(object sender, EventArgs e)
{
using (Transaction t = farm.Store.TransactionManager.BeginTransaction("Add sheep"))
{
elementOperations.MergeElementGroup(farm,
new ElementGroup(new Sheep(farm.Partition)));
t.Commit();
}
}

// The following code is shared with other add buttons:


private ElementOperations operationsCache = null;
private ElementOperations elementOperations
{
get
{
if (operationsCache == null)
{
operationsCache = new ElementOperations(farm.Store, farm.Partition);
}
return operationsCache;
}
}
private Farm farm
{
get { return this.farmBindingSource.DataSource as Farm; }
}

You will also need to insert the following directive:

using Microsoft.VisualStudio.Modeling;

3. Add similar buttons for Goats and Fields.


4. Build and run the solution.
5. Verify that the new button adds an item. The new item should appear in both the FarmApp Explorer and in
the appropriate data grid view.
You should be able to edit the name of the element in the data grid view. You can also delete it from there.

About the code to add an element


For the new element buttons, the following alternative code is slightly simpler.

private void NewSheepButton_Click(object sender, EventArgs e)


{
using (Transaction t = farm.Store.TransactionManager.BeginTransaction("Add sheep"))
{
farm.Animals.Add(new Sheep(farm.Partition)); ;
t.Commit();
}
}

However, this code does not set a default name for the new item. It does not run any customized merge that you
might have defined in the Element Merge Directives of the DSL, and it does not run any custom merge code
that might have been defined.
Therefore we recommend that you use ElementOperations to create new elements. For more information, see
Customizing Element Creation and Movement.

See Also
How to Define a Domain-Specific Language
Writing Code to Customise a Domain-Specific Language
Modeling SDK for Visual Studio - Domain-Specific Languages
Creating a WPF-Based Domain-Specific Language
10/18/2017 1 min to read Edit Online

You can create a domain-specific language that has a WPF designer instead of a graphical designer.
Information and samples about this feature can be found on the Visual Studio Visualization and Modeling Tools
website at http://go.microsoft.com/fwlink/?LinkId=186128

See Also
How to Define a Domain-Specific Language
Working with Domain-Specific Language Solutions
10/18/2017 1 min to read Edit Online

A domain-specific language (DSL) is created using a Visual Studio solution. This section is about how to set up and
customize the solution.

In this Section
About Domain-Specific Languages
Discusses how to develop DSLs and how to apply them.
Overview of Domain-Specific Language Tools
Explains the major tasks in developing and distributing a DSL.
Overview of the Domain-Specific Language Tools User Interface
Describes the windows that are associated with DSLs.
Choosing a Domain-Specific Language Solution Template
Explains how to choose a template to start your DSL project.
How to: Create a Domain-Specific Language Solution
Describes the steps to set up a DSL.
Working with the DSL Definition Diagram
Explains the parts of the diagram.
Working with the Domain-Specific Language Explorer
Explains the content of the explorer
How to: Change the Namespace of a Domain-Specific Language
The code you generate from a DSL has a namespace that you set when you initiate your solution, but you can
change it afterwards.
How to: Extend the Domain-Specific Language Designer
You can customize the DSL Designer with menu commands.
How to Automate Transform All Templates
To avoid clicking Transform All Templates, you can make it an automatic part of the build.
About Domain-Specific Languages
10/18/2017 6 min to read Edit Online

Unlike a general-purpose language such as C# or UML, a domain-specific language (DSL) is designed to express
statements in a particular problem space, or domain.
Well-known DSLs include regular expressions and SQL. Each DSL is much better than a general-purpose language
for describing operations on text strings or a database, but much worse for describing ideas that are outside its
own scope. Individual industries also have their own DSLs. For example, in the telecommunications industry, call
description languages are widely used to specify the sequence of states in a telephone call, and in the air travel
industry a standard DSL is used to describe flight bookings.
Your business and your project also deal with special sets of concepts that could be described with a DSL. For
example, you could define a DSL for one of these applications:
Plan of navigation paths in a Web site.
Wiring diagrams for electronic components.
Networks of conveyor belts and baggage handling equipment for an airport.
When you design a DSL, you define a domain class for each of the important concepts in the domain, such
as a Web page, lamp, or airport check-in desk. You define domain relationships such as hyperlink, wire, or a
conveyor belt to link the concepts together.
Users of your DSL create models. Models are instances of the DSL. For example, they describe a particular
Web site, or the wiring of a particular device, or the baggage handling system in a particular airport.
Your users can view a model as a diagram or as a Windows form. Models can also be viewed as XML, which
is how they are stored. When you define a DSL, you define how the instances of each domain class and
relationship appear on the user's screen. A typical DSL is displayed as a collection of icons or rectangles
connected by arrows.
The following figure shows a small model in a diagrammatic DSL:

What you can do with DSLs


A typical application of a DSL is to generate program code or other artifacts. When you define your DSL, you can
define text templates that read a model of the DSL and generate text files.
For example, you could write templates that take an airport plan and generate part of the software for baggage
handling, as well as some of the user documents that describe the plan.
When you have defined a DSL, you can distribute it to other users who can install it on their own computers. Users
of your DSL can create and edit models in Visual Studio.
You can also define menu commands and other tools that help users edit the DSL, validation constraints to help
ensure that the DSL is used correctly, and item templates that help users create new instances. You can wrap one or
more DSLs with their tools and other Visual Studio extensions as an integrated package.
Typically, a domain-specific language is created when a development team has to write similar code for several
products. For example, a company that specializes in baggage handling systems might define a baggage track DSL
from which they can generate some of the code for each installation. The benefits of the DSL are that it can be
understood by their customers, that the code generated from it is reliable, and that the system can be rapidly
updated if the customers' requirements change.
Domain-Specific Language Tools lets you create a domain-specific language that has your own graphical designer
and your own diagram notation, and then use the language to generate appropriate source code for each project.

Domain-Specific Development
Domain-specific development is the process of identifying the parts of your applications that can be modeled by
using a domain-specific language, and then constructing the language and deploying it to the application
developers. The developers use the domain-specific language to construct models that are specific to their
applications, use the models to generate source code, and then use the source code to develop the applications.

Aspects of Graphical Domain-Specific Development


A graphical domain-specific language must include the following features:
Notation
Domain model
Artifact generation
Serialization
Integration with Visual Studio
Notation
A domain-specific language must have a reasonably small set of elements that can be easily defined and extended
to represent domain-specific constructs. A notation consists of shapes, which represent the elements, and
connectors, which represent the relationships between elements, on a graphical diagram surface. In Domain-
Specific Language Tools, the shapes can be extended and refined to represent the elements of your domain-specific
language.
Domain Model
A domain-specific language must combine the set of elements and the relationships between them into a coherent
grammar. It must also define whether combinations of elements and relationships are valid. For example,
programming languages typically prevent circular inheritance, in which one class is derived from a second class
and the second class is derived from the first class. Constraints can also be used to express business logic, for
example, one person cannot be a dependent of himself. Domain-Specific Language Tools uses constraints to
express the kinds of restrictions that most domain-specific languages require.
Artifact Generation
One of the main purposes of a domain-specific language is to generate an artifact, for example, source code, an
XML file, or some other usable data. Typically, a change in the model means a change in the artifact. You can use
Domain-Specific Language Tools to generate artifacts and to regenerate them when you change the model.
Serialization
A domain-specific language must be persisted in some form that can be edited, saved, closed, and reloaded.
Domain-Specific Language Tools uses an XML format that lets you define and customize how your domain-specific
language is serialized or persisted.
Integration with Visual Studio
Because Domain-Specific Language Tools is hosted in Visual Studio, it extends many Visual Studio windows and
controls. It also lets you customize the behavior of menu commands, toolbox items, and other elements of the user
interface.
You can also create a Model Bus Adapter for your domain-specific language. This adapter lets you reference a
model and elements within a model, and lets you write code that can access and update an instance of the DSL. By
using the powerful Model Bus mechanism, you can write Visual Studio extensions that work with multiple models.
You can also write stand-alone applications that work with models. For more information, see Integrating Models
by using Visual Studio Modelbus.

Benefits of Domain-Specific Development


A domain-specific language can provide the following benefits:
Contains constructs that exactly fit the problem space.
Unlike general-purpose languages, a domain-specific language consists of elements and relationships that
directly represent the logic of the problem space. For example, an insurance policy application must include
elements for policies and claims. A domain-specific language makes it easier to design the application, and
find and correct errors of logic.
Lets non-developers and people who do not know the domain understand the overall design.
By using a graphical domain-specific language, you can create a visual representation of the domain so that
non-developers can easily understand the design of the application.
Makes it easier to create a prototype of the final application.
Developers can use the code that their model generates to create a prototype application that they can show
to clients.

The Process of Domain-Specific Development


Most software development teams that use domain-specific languages follow these steps to create and use their
models:
The team distinguishes the variable parts of the domain from the parts that never change.
The developers write code for the fixed parts and leave extension points for the variable parts.
The lead software developer or the architect creates a domain-specific language that incorporates the design
patterns of the fixed parts of the domain and the extension points for the variable parts.
The lead software developer or the architect deploys the domain-specific language to the developers of the
various applications that the team produces.
Every developer creates a model that applies to the specific application.
Overview of Domain-Specific Language Tools
10/18/2017 2 min to read Edit Online

Domain-Specific Language Tools (DSL Tools), which are hosted in Visual Studio, let you design a domain-specific
language and then generate everything that users must have to create models that are based on the language.
The following tools are included in DSL Tools:
A project wizard that uses different solution templates to help you start developing your domain-specific
language.
A graphical designer for creating and editing your domain-specific language definition.
A validation engine that makes sure that the domain-specific language definition is well-formed, and
displays errors and warnings if there are problems.
A code generator that takes a domain-specific language definition as input and produces source code as
output.

The DSL Tools Solution


The Domain-Specific Designer Wizard provides the following solution templates:
Task Flow
Class Diagrams
Minimal Language
Component Models
Minimal WPF
Minimal Windows.Forms
DSL Library
For more information, see Choosing a Domain-Specific Language Solution Template.
The wizard creates a Visual Studio solution that has the following projects:
Dsl
The Dsl project defines the domain-specific language and its editing and processing tools.
DslPackage
The DslPackage project determines how the language tools integrate with Visual Studio.

The DSL Tools Graphical Interface


You can use the DSL Tools graphical interface to add elements and relationships to your domain-specific language.
After you have added the elements, you can define their appearance by mapping them to shapes, customizing
colors, and adding decorators. You can also add the elements to the toolbox.

Validation in DSL Tools


Dsl provides one level of validation to make sure that the domain model meets the basic requirements for code
generation. Typically, when you create your own domain-specific language, you would add your own validation to
express your business logic rules. For more information about custom validation, see Validation in a Domain-
Specific Language.
We recommend that you validate your domain-specific language often when you are designing it. If your domain-
specific language has validation errors, you cannot generate source code. The process of generating source code
from the templates is performed by clicking Transform All Templates in the toolbar of Solution Explorer.
Whenever you modify the language definition, also make sure to Transform All Templates. For more
information, see How to: Create a Domain-Specific Language Solution.

Customization of DSL Tools


You can provide additional code to refine the behavior of the model and to define constraints over your language.
If required, you can make significant changes by modifying the text templates.

Distributing Your DSL Solution


DSL Tools generates a package that is hosted in Visual Studio. The package displays a toolbox, a DSL explorer, and
other UI elements that let users create models by using your domain-specific language.
When you build and run the DSL Tools solution in Visual Studio, a second instance of Visual Studio shows you how
your domain-specific language looks to the user of the language. After you verify that everything works correctly,
you can distribute the .vsix file that you will find in the build folder of the DslPackage project. This file can be
used to install the DSL as a Visual Studio extension on other computers. For more information, see Deploying
Domain-Specific Language Solutions.

See Also
The Experimental Instance
Domain-Specific Language Tools Glossary
Overview of the Domain-Specific Language Tools
User Interface
10/18/2017 2 min to read Edit Online

When you first open a Domain-Specific Language Tools (DSL Tools) solution in Visual Studio, the user interface
will resemble the following picture.

The following table explains how the parts of the UI are used.

ELEMENT DEFINITION

Diagram The diagram displays the domain model.

The diagram has two sides. One side defines the types of the
elements in your models. The other side defines how your
models will appear on the screen.

Toolbox Drag tools from the toolbox to add domain classes and shape
types to the diagram. To add relationships, connectors and
shape maps, click the tool, then click the source node on the
diagram, and then the target node.

DSL Explorer DSL Explorer appears when a DSL definition is the active
window. It shows the DSL as a tree. DSL Explorer lets you edit
features of the model that are not displayed on the diagram.
For example, you can add toolbox items and switch on the
validation process by using the DSL Explorer.

DSL Details window The DSL Details window shows properties of the domain
model's elements that allow you to control how elements are
displayed, and how elements are copied and deleted.

- By default, the DSL Details window appears next to the


Error List and Output windows.

The Domain Model Diagram


The domain model diagram is divided into two parts. One side of the diagram shows the elements and
relationships in the model. The other side shows how the model is to be displayed, and includes the shapes that
are used to display the elements and the properties of the model diagram. The following picture shows the
elements of the diagram.
The following table explains some of the elements of the domain model diagram.

TERM DEFINITION

Domain Class Domain classes are the types of elements in your models.

A domain class can appear more than once in a diagram, if it


is the target of more than one relationship.

To add a domain class, drag the domain class tool from the
Toolbox to the Classes and Relationships side of the
diagram.

Domain Relationship Domain relationships are the types of links between elements
in your models.

An embedding relationship indicates that the target element


is owned or contained by the source element, and appears as
a solid line. Every element in a model should be the target of
one embedding relationship, so that the model forms a tree. A
reference relationship indicates a general link between model
elements, and appears as a dashed line. Any element can have
any number of reference links.

Create a relationship by clicking the tool on the Toolbox,


clicking the source domain class, and then clicking the target
class.

Shapes and Connectors Shapes specify how model elements should be displayed on a
DSL diagram., Connectors specify lines on a DSL diagram that
can be used to display relationships.

To create a shape or connector, drag the tool to the Diagram


Elements side of the diagram.

Shape Maps A shape map appears as a line on the domain model diagram,
linking a shape to the domain class that it displays, or a
connector to the domain relationship that it displays.

See Also
Overview of Domain-Specific Language Tools
Domain-Specific Language Tools Glossary
Customizing and Extending a Domain-Specific Language
Choosing a Domain-Specific Language Solution
Template
10/18/2017 2 min to read Edit Online

To create a domain-specific language solution, choose one of the solution templates that are available in the
Domain-Specific Language Designer Wizard. By choosing the template that most closely resembles the language
that you want to create, you can minimize the modifications that you have to make to the starting solution.
The following solution templates are available in the Domain-Specific Language Designer Wizard.

TEMPLATE FEATURES DESCRIPTION

Class Diagrams - Compartment shapes Use this solution template if your


- Class inheritance domain-specific language includes
- Relationship inheritance entities and relationships that have
- Shape inheritance properties. This template creates a
- Relationship properties domain-specific language that
resembles UML class diagrams. The
main entities are classes and interfaces,
together with association,
generalization, and implementation
relationships. A class or interface
appears as a box that contains a list of
attributes.

Component Diagrams - Ports Use this solution template if your


domain-specific language includes
components, that is, parts of a software
system. This template creates a
domain-specific language that
resembles UML component diagrams.
The main entities are components and
ports, which appear as small shapes on
the outside of components.

Task Flow Diagrams - Image and geometry shapes Use this solution template if your
- Swimlanes domain-specific language includes
workflows, states, or sequences. This
template creates a domain-specific
language that resembles UML activity
diagrams. The main entity is an activity,
and the main relationship is a transition
between activities. The template
includes several other elements such as
start state, final state, and a
synchronization bar.
TEMPLATE FEATURES DESCRIPTION

Minimal Language - One class and shape Use this solution template if your
- One relationship and connector domain-specific language does not
resemble the other templates. This
template creates a domain-specific
language that has two classes and one
relationship, which are represented in
the Toolbox as Box and Line. The
class and the relationship each have an
example string property.

Minimal WinForm Designer - A small model. Use this template if you want to build
- A Windows Form that displays the an application in which a DSL is bound
model. to a Windows Form, rather than a
graphical designer.

The form that acts as the user interface


for the language is in the folder Dsl\UI.

You should build the project before


opening the form designer.

For more information, see Creating a


Windows Forms-Based Domain-Specific
Language.

Minimal WPF Designer - A small model Use this template if you want to build
- A Windows Presentation Foundation an application in which a DSL is bound
user interface that displays the model to a WPF user interface, rather than a
graphical designer.

The designer for the user interface is in


the folder Dsl\UI.

You should build the project before


opening the UI designer.

For more information, see Creating a


WPF-Based Domain-Specific Language.

DSL Library - A minimal library Use this template if you want to build a
partial DSL definition that can be
imported into other DSL definitions.

See Also
Overview of Domain-Specific Language Tools
How to: Create a Domain-Specific Language
Solution
10/18/2017 4 min to read Edit Online

A domain-specific language (DSL) is created by using a specialized Visual Studio solution.

Prerequisites
Before you can start this procedure, you must first install these components:

Visual Studio http://go.microsoft.com/fwlink/?LinkID=185579

Visual Studio SDK http://go.microsoft.com/fwlink/?LinkID=185580

Visual Studio Visualization and Modeling SDK

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Creating a Domain-Specific Language Solution


To create a domain-specific language solution
1. Start the DSL Wizard.
a. On the File menu, point to New, and then click Project.
b. The New Project dialog box appears.
c. Under Project types, expand the Other Project Types node, and click Extensibility.
d. Click Domain-Specific Language Designer.
e. In the Name box, type a name for the solution. Click OK.
The Domain-Specific Language Designer Wizard appears.

NOTE
Preferably, the name that you type should be a valid Visual C# identifier, because it might be used to
generate code.
2. Choose a DSL template.
On the Select Domain-Specific Language Options page, select one of the solution templates such as
Minimal Language. Choose a template that is similar to the DSL that you want to create.
For more information about solution templates, see Choosing a Domain-Specific Language Solution
Template.
3. Enter a filename extension on the File Extension page. It should be unique in your computer, and in any
computers on which you want to install the DSL. You should see the message No applications or Visual
Studio editors use this extension.
If you have used the file name extension in previous experimental DSLs that have not been fully
installed, you can clear them out by using the Reset the Experimental Instance tool, which can be
found in the Visual Studio SDK menu.
If another Visual Studio Extension that uses this file extension has been fully installed on your
computer, consider uninstalling it. On the Tools menu, click Extension Manager.
4. Inspect, and if necessary adjust, the fields in the remaining pages of the wizard. When you are satisfied with
the settings, click Finish. For more information about the settings, see DSL Designer Wizard Pages.
The wizard creates a solution that has two projects, which are named Dsl and DslPackage.

NOTE
If you see a message that alerts you not to run text templates from untrusted sources, click OK. You can set this
message not to appear again.

The DSL Designer Wizard Pages


You can leave several of the fields unchanged from their default values. However, make sure that you set the File
Extension field.
Solution Settings page
Which Template would you like to base your domain specific language on?
Choose a template that is similar to the DSL that you want to create. The different templates provide convenient
starting points. When you select a solution template, the wizard displays a description. For more information
about solution templates, see Choosing a Domain-Specific Language Solution Template.
What do you want to name your domain-specific language?
Defaults to the solution name. Code is generated from this value. It must be valid as a C# class name.
File Extension page
What extension should model files use?
Type a new file extension.
Verify that this file extension has not already been registered for use in this computer, as follows:
Look under Other tools and applications registered to handle this extension. If you see the message No
applications or Visual Studio editors use this extension, then you can use this file extension.
If you see a list of tools or packages, you should do one of the following:
Type a different file extension.
- or -
Reset the Visual Studio Experimental Instance. This will unregister all of the DSLs that you have previously
built. On the Start menu, click All Programs, Microsoft Visual Studio 2010 SDK, Tools, and then Reset
the Microsoft Visual Studio 2010 Experimental instance. You can rebuild any other DSLs that you
want to use again.
- or -
If a Visual Studio Extension that uses this file extension has been fully installed on your computer, uninstall
it. On the Tools menu, click Extension Manager.
Product Settings page
What is the name of the product that the new domain-specific language belongs to?
Defaults to the DSL name.
This value is used in Windows Explorer (or File Explorer) to describe files that have this file extension.
What is the name of the company that the product belongs to?
Your company name.
This value is incorporated into the AssemblyInfo properties of your DSL package.
What is the root namespace for projects in this solution?
This defaults to a name composed from your company and product names.
Signing page
Create a strong name key file
The default option is to create a new key to sign your DSL assembly.
Use existing strong name key
Use this option if you want to integrate your DSL with another assembly.
For more information about strong naming, see Creating and Using Strong-Named Assemblies.

See Also
How to Define a Domain-Specific Language
Domain-Specific Language Tools Glossary
Working with the DSL Definition Diagram
10/18/2017 3 min to read Edit Online

The diagram of a Domain-Specific Language Tools definition is an important tool for defining the domain-specific
language. You can add elements to your domain model and define relationships on the diagram, and you can
modify the layout of the diagram to make it more readable.

The Layout of the Diagram


The Domain-Specific Language Tools definition diagram has two partitions, the Classes and Relationships
partition and the Diagram Elements partition. The Classes and Relationships partition displays domain classes,
domain relationships, and inheritance. The Diagram Elements partition displays shape classes, connector classes,
swimlane classes, and the generated designer diagram.
Domain classes can appear in multiple locations in the Classes and Relationships partitions. A domain class
definition displays an inheritance tree if it is the base class for other domain classes, and a relationships tree if it is
the source of embedding or reference relationships. Domain class placeholders appear as the targets of embedding
or reference relationships. By default, placeholder elements are displayed with the Domain Properties
compartment collapsed. They do not show inheritance, or embedding or reference relationships.
When you add a domain class, it appears in the lower part of the Classes and Relationships partition. When you
add an embedding or reference relationship, it is drawn under and to the right of the source domain class.
As you add domain classes and relationships, it may become difficult to locate a particular domain class. You can
find a domain class by right-clicking it in the DSL Explorer and then clicking Locate in Diagram.
The following sections describe how you can change the appearance of the diagram to make it easier to read.

Copying elements
You can use copy, cut and paste on elements in the DSL definition diagram.

Zooming In or Out on the Diagram


You can zoom in or out on the diagram by using the DSL Designer toolbar to set the zoom level.

Hiding Map Lines


Map lines are lines that are drawn between a domain class or domain relationship and the shape or connector to
which it is mapped. You can hide map lines by clicking the Show Map Lines button on the DSL Designer toolbar.
To show the lines, click the button again.

Changing the Diagram Layout


You can change the layout of the Classes and Relationships partition as follows.
Expand/Collapse
You can reduce the size of a compartment shape element that represents a domain class or a shape by right-
clicking it and then clicking Collapse. This hides the Domain Properties compartment of the shape. To show the
Domain Properties compartment again, right-click the shape and then click Expand.
Move Up/Down
You can move a domain class or diagram element up or down in the partition by right-clicking the element and
then clicking Move Up or Move Down. If you move a placeholder element that is displayed as the target of an
embedding or reference relationship, the relationship will move with it.
Expand/Collapse Relationships Tree
If a domain class plays the source role in embedding or reference relationships with other domain classes, you can
hide the relationships by right-clicking the domain class definition and then clicking Collapse Relationships Tree.
To show the relationships, right-click the definition element and then click Expand Relationships Tree.
Expand/Collapse Inheritance Tree
If a domain class is the base class of other domain classes, you can hide the inheritance tree by right-clicking the
domain class definition and then clicking Collapse Inheritance Tree. To show the inheritance tree, right-click the
definition element and then click Expand Inheritance Tree.
Bring Tree Here
You can consolidate the diagram by right-clicking a placeholder domain class and then clicking Bring Tree Here.
The placeholder domain class becomes a definition element and displays the inheritance and relationships trees.
The former definition element becomes a placeholder element if it is the target of a relationship or the child in an
inheritance relationship; otherwise, it disappears.
Split Tree
You can break out inheritance or relationships trees by right-clicking the domain class definition that displays them
and then clicking Split Tree. The definition element becomes a placeholder element, and the definition domain
class, together with its inheritance and relationships trees, is now displayed at the bottom of the partition.
Show As Class
If a domain relationship has derived relationships, or if it has embedding or reference relationships with other
domain relationships, you can display the relationship as a class by right-clicking the relationship and then clicking
Show As Class. The relationship will be displayed with a Domain Properties compartment and will show the
inheritance and relationships trees.

See Also
Domain-Specific Language Tools Glossary
Working with the Domain-Specific Language Explorer
10/18/2017 1 min to read Edit Online

The DSL Explorer displays several kinds of information in a tree view. For more information about most of the
items in the DSL Explorer, see How to Define a Domain-Specific Language.
It has the following nodes:
Connection Builders. A list of the connection builders in the model.
Connectors. A list of the connector diagram elements in the model.
Diagram. A set of properties that define the diagram, and lists of connector maps and shape maps.
Domain Classes. A list of the domain classes in the model.
Domain Relationships. A list of the domain relationships in the model.
Domain Types. A list of the built-in and external types that are used in the model.
Editor. A list of Toolbox tabs and the items they contain, and validation settings for the model. For more
information about validation, see Validation in a Domain-Specific Language.
Explorer Behavior. Settings that specify the behavior of the explorer in the generated designer. For more
information about how to customize the explorer, see Customizing the Model Explorer.
Shapes. A list of the shape diagram elements in the model.
Xml Serialization Behavior. Serialization settings for the model. For more information about serialization,
see Customizing File Storage and XML Serialization.

See Also
Domain-Specific Language Tools Glossary
How to: Change the Namespace of a Domain-
Specific Language
10/18/2017 1 min to read Edit Online

You can change the namespace of a domain-specific language. You must make the change in the DSL Explorer, in
the properties of the Dsl Package project, and in the assembly information.
To change the namespace of a domain-specific language
1. In DSL Explorer, click the Dsl node.
2. In the Properties window, change the Namespace property.
3. Save the solution and transform the templates.
4. On the Project menu, click Dsl Properties.
The properties for your project appear.
5. Click the Application tab.
6. Change the Default namespace property to the new namespace name.
7. If you also want to change the name of the assembly, change the Assembly name property.
8. If you have changed the Assembly name, open DslPackage\Package.tt and update this line:
string dslAssembly = "YourDSLassembly.Dsl.dll";

9. If you have written any custom code, make sure to change the namespace and class references in the code
files.
10. Reset the Visual Studio Experimental instance.
a. Delete \Users\{your name}\AppData\Local\Microsoft\VisualStudio\*Exp
b. On the Windows Start menu, choose All Programs, Microsoft Visual Studio 2010 SDK, Tools,
Reset the Experimental Instance.
11. On the Build menu, choose Rebuild Solution.

See Also
Domain-Specific Language Tools Glossary
How to: Extend the Domain-Specific Language
Designer
10/18/2017 5 min to read Edit Online

You can make extensions to the designer that you use to edit DSL Definitions. Types of extension that you can
make include adding menu commands, adding handlers for drag and double-click gestures, and rules that are
triggered when particular types of values or relationships change. The extensions can be packaged as a Visual
Studio Integration Extension (VSIX) and distributed to other users.
For sample code and more information about this feature, see the Visual Studio Visualization and Modeling SDK
(VMSDK) Web site.

Setting up the Solution


Set up a project that contains the code of your extension, and a VSIX project that exports the project. Your solution
can contain other projects that are incorporated into the same VSIX.
To create a DSL Designer Extension Solution
1. Create a new project using the Class Library project template. In the New Project dialog box, click Visual
C# and then in the middle window click Class Library.
This project will contain the code of your extensions.
2. Create a new project using the VSIX project template. In the New Project dialog box, expand Visual C#,
click Extensibility, and then in the middle window select VSIX Project.
Select Add to Solution.
Source.extension.vsixmanifest opens in the VSIX manifest editor.
3. Above the Content field, click Add Content.
4. In the Add Content dialog box, set Select a Content Type to MEF Component, and set Project to your
class library project.
5. Click Select Editions and make sure that Visual Studio Enterprise is checked.
6. Make sure that the VSIX project is the Startup project of the solution.
7. In the class library project, add references to the following assemblies:
Microsoft.VisualStudio.CoreUtility
Microsoft.VisualStudio.Modeling.Sdk.11.0
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0
Microsoft.VisualStudio.Modeling.Sdk.DslDefinition.11.0
Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0
System.ComponentModel.Composition
System.Drawing
System.Drawing.Design
System.Windows.Forms

Testing and Deployment


To test any of the extensions in this topic, build and run the solution. An experimental instance of Visual Studio
opens. In this instance, open a DSL solution. Edit the DslDefinition diagram. The extension behavior can be seen.
To deploy the extensions to the main Visual Studio, and to other computers, follow these steps:
1. Find the VSIX installation file, in your VSIX project in bin\*\*.vsix
2. Copy this file to the target computer, and then in Windows Explorer (or File Explorer), double-click it.
The Visual Studio Extension Manager opens to confirm that the extension has been installed.
To uninstall the extension, follow these steps:
3. in Visual Studio, on the Tools menu, click Extension Manager.
4. Select the extension and delete it.

Adding a Shortcut Menu Command


To make a shortcut menu command appear on the DSL Designer surface or in the DSL Explorer window, write a
class resembling the following.
The class must implement ICommandExtension and must have the attribute DslDefinitionModelCommandExtension .
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.DslDefinition;
using Microsoft.VisualStudio.Modeling.DslDefinition.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.DslDesigner;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;

namespace Fabrikam.SimpleDslDesignerExtension
{
/// <summary>
/// Command extending the DslDesigner.
/// </summary>
[DslDefinitionModelCommandExtension]
public class MyDslDesignerCommand : ICommandExtension
{
/// <summary>
/// Selection Context for this command
/// </summary>
[Import]
IVsSelectionContext SelectionContext { get; set; }

/// <summary>
/// Is the command visible and active?
/// This is called when the user right-clicks.
/// </summary>
public void QueryStatus(IMenuCommand command)
{
command.Visible = true;
// Is there any selected DomainClasses in the Dsl explorer?
command.Enabled =
SelectionContext.AtLeastOneSelected<DomainClass>();

// Is there any selected ClassShape on the design surface?


command.Enabled |=
(SelectionContext.GetCurrentSelection<ClassShape>()
.Count() > 0);
}
/// <summary>
/// Executes the command
/// </summary>
/// <param name="command">Command initiating this action</param>
public void Execute(IMenuCommand command)
{
...
}
/// <summary>
/// Label for the command
/// </summary>
public string Text
{
get { return "My Command"; }
}
}
}

Handling Mouse Gestures


The code is similar to the code of the menu command.
[DslDefinitionModelGestureExtension]
class MouseGesturesExtensions : IGestureExtension
{
/// <summary>
/// Double-clicking on a shape representing a Domain model element displays this model element in a dialog
box
/// </summary>
/// <param name="targetElement">Shape element on which the user has clicked</param>
/// <param name="diagramPointEventArgs">event args for this double-click</param>
public void OnDoubleClick(ShapeElement targetElement,
DiagramPointEventArgs diagramPointEventArgs)
{
ModelElement modelElement = PresentationElementHelper.
GetDslDefinitionModelElement(targetElement);
if (modelElement != null)
{
MessageBox.Show(string.Format(
"Double clicked on {0}", modelElement.ToString()),
"Model element double-clicked");
}
}

/// <summary>
/// Tells if the DslDesigner can consume the to-be-dropped information
/// </summary>
/// <param name="targetMergeElement">Shape on which we try to drop</param>
/// <param name="diagramDragEventArgs">Drop event</param>
/// <returns><c>true</c> if we can consume the to be dropped data, and <c>false</c> otherwise</returns>
public bool CanDragDrop(ShapeElement targetMergeElement,
DiagramDragEventArgs diagramDragEventArgs)
{
if (diagramDragEventArgs.Data.GetDataPresent(DataFormats.FileDrop))
{
diagramDragEventArgs.Effect = DragDropEffects.Copy;
return true;
}
return false;
}

/// <summary>
/// Processes the drop by displaying the dropped text
/// </summary>
/// <param name="targetMergeElement">Shape on which we dropped</param>
/// <param name="diagramDragEventArgs">Drop event</param>
public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
{
if (diagramDragEventArgs.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] droppedFiles =
diagramDragEventArgs.Data.
GetData(DataFormats.FileDrop) as string[];
MessageBox.Show(string.Format("Dropped text {0}",
string.Join("\r\n", droppedFiles)), "Dropped Text");
}
}
}

Responding to Value Changes


This handler needs a domain model to work correctly. We provide a simple domain model.
using System.Diagnostics;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.DslDefinition;
namespace Fabrikam.SimpleDslDesignerExtension
{
/// <summary>
/// Rule firing when the type of a domain model property is changed. The change is displayed
/// in the debugger (Output window of the Visual Studio instance debugging this extension)
/// </summary>
[RuleOn(typeof(PropertyHasType))]
public class DomainPropertyTypeChangedRule : RolePlayerChangeRule
{
/// <summary>
/// Method called when the Type of a Domain Property
/// is changed by the user in a DslDefinition
/// </summary>
/// <param name="e"></param>
public override void RolePlayerChanged(RolePlayerChangedEventArgs e)
{
// We are only interested in the type
if (e.DomainRole.Id == PropertyHasType.TypeDomainRoleId)
{
PropertyHasType relationship =
e.ElementLink as PropertyHasType;
DomainType newType = e.NewRolePlayer as DomainType;
DomainType oldType = e.OldRolePlayer as DomainType;
DomainProperty property = relationship.Property;

// We write about the Type change in the debugguer


Debug.WriteLine(string.Format("The type of the Domain property '{0}' of domain class '{1}' changed
from '{2}' to '{3}'",
property.Name,
property.Class.Name,
oldType.GetFullName(false),
newType.GetFullName(false))
} } } );

The following code implements a simple model. Create a new GUID to replace the placeholder.
using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.DslDefinition;
namespace Fabrikam.SimpleDslDesignerExtension
{
/// <summary>
/// Simplest possible domain model
/// needed only for extension rules.
/// </summary>
[DomainObjectId(SimpleDomainModelExtension.DomainModelId)]
public class SimpleDomainModelExtension : DomainModel
{
// Id of this domain model extension
// Please replace this with a new GUID:
public const string DomainModelId =
"00000000-0000-0000-0000-000000000000";

/// <summary>
/// Constructor for the domain model extension
/// </summary>
/// <param name="store">Store in which the domain model will be loaded</param>
public SimpleDomainModelExtension(Store store)
: base(store, new Guid(SimpleDomainModelExtension.DomainModelId))
{

/// <summary>
/// Rules brought by this domain model extension
/// </summary>
/// <returns></returns>
protected override System.Type[] GetCustomDomainModelTypes()
{
return new Type[] {
typeof(DomainPropertyTypeChangedRule)
};
}
}

/// <summary>
/// Provider for the DomainModelExtension
/// </summary>
[Export(typeof(DomainModelExtensionProvider))]
[ProvidesExtensionToDomainModel(typeof(DslDefinitionModelDomainModel))]
public class SimpleDomainModelExtensionProvider
: DomainModelExtensionProvider
{
/// <summary>
/// Extension model type
/// </summary>
public override Type DomainModelType
{
get
{
return typeof(SimpleDomainModelExtension);
}
}

}
}
Supported Visual Studio Editions for Visualization &
Modeling SDK
10/18/2017 1 min to read Edit Online

The following are lists of the Visual Studio editions that are supported with Domain-Specific Language Tools in the
authoring and deployment environments. For more information on these editions, see the Microsoft Visual Studio
Developer Center.

Authoring Edition
To define a DSL, you must have installed the following components:

Visual Studio http://go.microsoft.com/fwlink/?LinkId=185579

Visual Studio SDK http://go.microsoft.com/fwlink/?LinkId=185580

Visual Studio Visualization and Modeling SDK http://go.microsoft.com/fwlink/?LinkID=186128

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

Deployment Editions
Domain-Specific Language Tools supports the following configurations for deploying the domain-specific
languages that you build:
Visual Studio Enterprise
Visual Studio Professional
Visual Studio Shell (integrated mode) redistributable package redistributable package
Visual Studio Shell (isolated mode) redistributable package redistributable package

NOTE
To make a DSL able to run on a Shell product, you must set the Supported VS Edition field in the Extension Manifest. For
more information, see Deploying Domain-Specific Language Solutions.

See Also
Domain-Specific Language Tools Glossary
How to: Migrate a Domain-Specific Language to a
New Version
10/18/2017 2 min to read Edit Online

You can migrate projects that define and use domain-specific language to Visual Studio 2010 from the version of
Domain-Specific Language Tools that was distributed with Visual Studio 2008.
A migration tool is provided as part of Visual Studio SDK. The tool converts Visual Studio projects and solutions
that use or define DSL Tools.
You must run the migration tool explicitly: it is not launched automatically when you open a solution in Visual
Studio. The tool and detailed guidance document can be found at this path:
%Program Files%\Microsoft Visual Studio 2010
SDK\VisualStudioIntegration\Tools\DSLTools\DslProjectsMigrationTool.exe

Before you Migrate your DSL Projects


The migration tool modifies Visual Studio project files (.csproj) and solution files (.sln).
To prepare projects for migration.
Make sure the .csproj and .sln files can be written. If they are under source control, make sure that they are
checked out.
Make a copy of the folders you intend to migrate.

Migrating a Collection of Projects


To Migrate DSL Projects and Solutions to Visual Studio 2010
1. Start the DSL Migration Tool.
You can double-click the tool in Windows Explorer (or File Explorer), or start the tool from a
command prompt. The tool is in this location:
%ProgramFiles%\Microsoft Visual Studio 2010
SDK\VisualStudioIntegration\Tools\DSLTools\DslProjectsMigrationTool.exe
2. Choose a folder that contains solutions and projects that you want to convert.
Enter the path in the box at the top of the tool, or click Browse.
The migration tool displays a tree of projects that define or use DSLs. The tree includes every project
that uses the Microsoft.VisualStudio.Modeling.Sdk or TextTemplating assemblies.
3. Review the tree of projects, and uncheck projects that you do not want to convert.
Select a project or solution to see a list of changes that the tool will make.

NOTE
The checkboxes that appear next to folder names have no effect. You must expand the folders to inspect the
projects and solutions.
4. Convert the projects.
a. Click Convert.
Before each project file is converted, a copy of project.csproj is saved as project.vs2008.csproj
A copy of each solution.sln is saved as solution.vs2008.sln
b. Investigate any failed conversions that are reported.
Failures are reported in the text window. In addition, the tree view shows a red flag on each node that
has failed to convert. You can click the node to get more information about that failure.
5. Transform All Templates in solutions containing successfully converted projects.
a. Open the solution.
b. Click the Transform All Templates button in the header of Solution Explorer.

NOTE
You can make this step unnecessary. For more information, see How to Automate Transform All Templates.

6. Update your custom code in the converted projects.


Attempt to build the projects, and investigate any failures.
Test your designer.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

See Also
Related blog posts
API Reference for Modeling SDK for Visual Studio
11/7/2017 1 min to read Edit Online

The Visual Studio Visualization and Modeling SDK provides the platform on which your domain-specific languages
(DSL) tools are built.
This section contains reference material for namespaces that have names that begin with
"Microsoft.VisualStudio.Modeling".

NAMESPACE CONTENT

Microsoft.VisualStudio.Modeling Classes such as ModelElement, which is the base class of all


domain classes that you define in a DSL.

Microsoft.VisualStudio.Modeling.Design Classes that form part of a DSL definition.

Microsoft.VisualStudio.Modeling.Diagnostics The model Store Viewer and performance measurement tools.

Microsoft.VisualStudio.Modeling.Diagrams Classes such as ShapeElement, which is the base class of all


shapes that you define in a DSL.

Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnableme Gesture and Selection methods.


nt

Microsoft.VisualStudio.Modeling.DslDefinition The API of the DSL Definition designer.

Microsoft.VisualStudio.Modeling.DslDefinition.Design Internal classes of the DSL Definition designer.

Microsoft.VisualStudio.Modeling.DslDefinition.ExtensionEnable Attributes that allow you to extend the DSL designer with
ment commands, gestures and validation.

Microsoft.VisualStudio.Modeling.Extensibility Extension methods for ModelElement that implement DSL


Extensibility.

Microsoft.VisualStudio.Modeling.ExtensionEnablement Extensibility attributes

Microsoft.VisualStudio.Modeling.Immutability Lets you make parts of a model read-only.

Microsoft.VisualStudio.Modeling.Integration The Modelbus API, which helps you integrate different models.

Microsoft.VisualStudio.Modeling.Integration.Picker The dialog box that lets users navigate to models and
elements to create Modelbus references.

Microsoft.VisualStudio.Modeling.Integration.Picker.Hosting The Picker service.

Microsoft.VisualStudio.Modeling.Integration.Shell Modelbus adapter framework for Visual Studio.

Microsoft.VisualStudio.Modeling.Integration.Shell.Picker The Picker dialog box that lets users navigate to models and
elements to create Modelbus references.

Microsoft.VisualStudio.Modeling.Shell The interface between DSLs and Visual Studio.


NAMESPACE CONTENT

Microsoft.VisualStudio.Modeling.Shell.ExtensionEnablement Lets you define shortcut (context) menu commands.

Microsoft.VisualStudio.Modeling.Validation Lets you define validation constraints.

See Also
Customizing T4 Text Transformation
Code Generation and T4 Text Templates
10/18/2017 4 min to read Edit Online

In Visual Studio, a T4 text template is a mixture of text blocks and control logic that can generate a text file. The
control logic is written as fragments of program code in Visual C# or Visual Basic. In Visual Studio 2015 Update 2
and later, you can use C# version 6.0 features in T4 templates directives. The generated file can be text of any kind,
such as a Web page, or a resource file, or program source code in any language.
There are two kinds of T4 text templates:
Run time T4 text templates ('preprocessed' templates) are executed in your application to produce text strings,
typically as part of its output.
For example, you could create a template to define an HTML page:

<html><body>
The date and time now is: <#= DateTime.Now #>
</body></html>

Notice that the template resembles the generated output. The similarity of the template to the resulting output
helps you avoid mistakes when you want to change it.
In addition, the template contains fragments of program code. You can use these fragments to repeat sections of
text, to make conditional sections, and to show data from your application.
To generate the output, your application calls a function that is generated by the template. For example:

string webResponseText = new MyTemplate().TransformText();

Your application can run on a computer that does not have Visual Studio installed.
To create a run-time template, add a Preprocessed text template file to your project. Alternatively, you can add
a plain text file and set its Custom Tool property to TextTemplatingFilePreprocessor.
For more information, see Run-Time Text Generation with T4 Text Templates. For more information about the
syntax of templates, see Writing a T4 Text Template.
Design-time T4 text templates are executed in Visual Studio to define part of the source code and other
resources of your application.
Typically you would use several templates that read the data in a single input file or database, and generate some
of your .cs , .vb , or other source files. Each template generates one file. They are executed within Visual Studio
or MSBuild.
For example, your input data could be an XML file of configuration data. Whenever you edit the XML file during
development, the text templates would regenerate part of the application code. One of the templates could
resemble the following example:
<#@ output extension=".txt" #>
<#@ assembly name="System.Xml" #>
<#
System.Xml.XmlDocument configurationData = ...; // Read a data file here.
#>
namespace Fabrikam.<#= configurationData.SelectSingleNode("jobName").Value #>
{
... // More code here.
}

Dependent on the values in the XML file, the generated .cs file would resemble the following:

namespace Fabrikam.FirstJob
{
... // More code here.
}

As another example, the input could be a diagram of workflow in a business activity. When the users change their
business workflow, or when you start work with new users who have a different workflow, it is easy to regenerate
the code to fit the new model.
Design-time templates make it quicker and more reliable to change the configuration when the requirements
change. Typically the input is defined in terms of business requirements, as in the workflow example. This makes
it easier to discuss the changes with your users. Design-time templates are therefore a useful tool in an agile
development process.
To create a design-time template, add a Text Template file to your project. Alternatively, you can add a plain text
file and set its Custom Tool property to TextTemplatingFileGenerator.
For more information, see Design-Time Code Generation by using T4 Text Templates. For more information about
the syntax of templates, see Writing a T4 Text Template.

NOTE
The term model is sometimes used to describe data read by one or more templates. The model can be in any format, in any
kind of file or database. It does not have to be a UML model or a Domain-Specific Language model. 'Model' just indicates
that the data can be defined in terms of the business concepts, rather than resembling the code.

The text template transformation feature is named T4.

In This Section
Run-Time Text Generation with T4 Text Templates
In any application that generates text files, precompiled text templates are an easy and reliable method of defining
the text. However, this method cannot be used for text templates that change at run time.
Design-Time Code Generation by using T4 Text Templates
Generating code and other resources from a model lets you update your application by updating the model.
Code Generation in a Build Process
If you have installed Visual Studio Visualization and Modeling SDK, you can ensure the generated software keeps
up to date with changes in the model.
Writing a T4 Text Template
The syntax of a text template file.
Walkthrough: Generating Code by using Text Templates
A demonstration of one way to use code generation.
Debugging a T4 Text Template
How to debug text templates, and some common text template errors.
Generating Files with the TextTransform Utility
The command-line tool that you can use to run text template transformations.
Customizing T4 Text Transformation
How to write directive processors and custom templating hosts for your own data sources.

See Also
Generating Code from a Domain-Specific Language
Design-Time Code Generation by using T4 Text
Templates
11/22/2017 11 min to read Edit Online

Design-time T4 text templates let you generate program code and other files in your Visual Studio project.
Typically, you write the templates so that they vary the code that they generate according to data from a model.
A model is a file or database that contains key information about your application's requirements.
For example, you could have a model that defines a workflow, either as a table or a diagram. From the model,
you can generate the software that executes the workflow. When your users' requirements change, it is easy to
discuss the new workflow with the users. Regenerating the code from the workflow is more reliable than
updating the code by hand.

NOTE
A model is a data source that describes a particular aspect of an application. It can be any form, in any kind of file or
database. It does not have to be in any particular form, such as a UML model or Domain-Specific Language model.
Typical models are in the form of tables or XML files.

You are probably already familiar with code generation. When you define resources in a .resx file in your Visual
Studio solution, a set of classes and methods is generated automatically. The resources file makes it much easier
and more reliable to edit the resources than it would be if you had to edit the classes and methods. With text
templates, you can generate code in the same manner from a source of your own design.
A text template contains a mixture of the text that you want to generate, and program code that generates
variable parts of the text. The program code and allows you to repeat or conditionally omit parts of the
generated text. The generated text can itself be program code that will form part of your application.

Creating a Design-Time T4 Text Template


To create a design-time T4 template in Visual Studio
1. Create a Visual Studio project, or open an existing one.
For example, on the File menu, choose New, Project.
2. Add a text template file to your project and give it a name that has the extension .tt.
To do this, in Solution Explorer, on the shortcut menu of your project, choose Add, New Item. In the
Add New Item dialog box select Text Template from the middle pane.
Notice that the Custom Tool property of the file is TextTemplatingFileGenerator.
3. Open the file. It will already contain the following directives:

<#@ template hostspecific="false" language="C#" #>


<#@ output extension=".txt" #>

If you added the template to a Visual Basic project, the language attribute will be " VB ".
4. Add some text at the end of the file. For example:
Hello, world!

5. Save the file.


You might see a Security Warning message box that asks you to confirm that you want to run the
template. Click OK.
6. In Solution Explorer, expand the template file node and you will find a file that has the extension .txt.
The file contains the text generated from the template.

NOTE
If your project is a Visual Basic project, you must click Show All Files in order to see the output file.

Regenerating the code


A template will be executed, generating the subsidiary file, in any of the following cases:
Edit the template and then change focus to a different Visual Studio window.
Save the template.
Click Transform All Templates in the Build menu. This will transform all the templates in the Visual
Studio solution.
In Solution Explorer, on the shortcut menu of any file, choose Run Custom Tool. Use this method to
transform a selected subset of templates.
You can also set up a Visual Studio project so that the templates are executed when the data files that
they read have changed. For more information, see Regenerating the code automatically.

Generating Variable Text


Text templates let you use program code to vary the content of the generated file.
To generate text by using program code
1. Change the content of the .tt file:

<#@ template hostspecific="false" language="C#" #>


<#@ output extension=".txt" #>
<#int top = 10;

for (int i = 0; i<=top; i++)


{ #>
The square of <#= i #> is <#= i*i #>
<# } #>

<#@ template hostspecific="false" language="VB" #>


<#@ output extension=".txt" #>
<#Dim top As Integer = 10

For i As Integer = 0 To top


#>
The square of <#= i #> is <#= i*i #>
<#
Next
#>
2. Save the .tt file, and inspect the generated .txt file again. It lists the squares of the numbers from 0 to 10.
Notice that statements are enclosed within <#...#> , and single expressions within <#=...#> . For more
information, see Writing a T4 Text Template.
If you write the generating code in Visual Basic, the template directive should contain language="VB" .
"C#" is the default.

Debugging a Design-Time T4 Text Template


To debug a text template:
Insert debug="true" into the template directive. For example:
<#@ template debug="true" hostspecific="false" language="C#" #>

Set breakpoints in the template, in the same way that you would for ordinary code.
Choose Debug T4 Template from the shortcut menu of the text template file in Solution Explorer.
The template will run and stop at the breakpoints. You can examine variables and step through the code
in the usual way.

TIP
debug="true" makes the generated code map more accurately to the text template, by inserting more line numbering
directives into the generated code. If you leave it out, breakpoints might stop the run in the wrong state.
But you can leave the clause in the template directive even when you are not debugging. This causes only a very small
drop in performance.

Generating Code or Resources for Your Solution


You can generate program files that vary, depending on a model. A model is an input such as a database,
configuration file, UML model, DSL model, or other source. You usually generate several program files are from
the same model. To achieve this, you create a template file for each generated program file, and have all the
templates read the same model.
To generate program code or resources
1. Change the output directive to generate a file of the appropriate type, such as .cs, .vb, .resx, or .xml.
2. Insert code that will generate the solution code that you require. For example, if you want to generate
three integer field declarations in a class:

<#@ template debug="false" hostspecific="false" language="C#" #>


<#@ output extension=".cs" #>
<# var properties = new string [] {"P1", "P2", "P3"}; #>
// This is generated code:
class MyGeneratedClass {
<# // This code runs in the text template:
foreach (string propertyName in properties) { #>
// Generated code:
private int <#= propertyName #> = 0;
<# } #>
}
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".cs" #>
<# Dim properties = {"P1", "P2", "P3"} #>
class MyGeneratedClass {
<#
For Each propertyName As String In properties
#>
private int <#= propertyName #> = 0;
<#
Next
#>
}

3. Save the file and inspect the generated file, which now contains the following code:

class MyGeneratedClass {
private int P1 = 0;
private int P2 = 0;
private int P3 = 0;
}

Generating Code and Generated Text


When you generate program code, it is most important to avoid confusing the generating code that executes in
your template, and the resulting generated code that becomes part of your solution. The two languages do not
have to be the same.
The previous example has two versions. In one version, the generating code is in C#. In the other version, the
generating code is Visual Basic. But the text generated by both of them is the same, and it is a C# class.
In the same way, you could use a Visual C# template to generate code in any language. The generated text does
not have to be in any particular language, and it does not have to be program code.
Structuring text templates
As a matter of good practice, we tend to separate the template code into two parts:
A configuration or data-gathering part, which sets values in variables, but does not contain text blocks. In
the previous example, this part is the initialization of properties .
This is sometimes called the "model" section, because it constructs an in-store model, and typically reads
a model file.
The text-generation part ( foreach(...){...} in the example), which uses the values of the variables.
This is not a necessary separation, but it is a style which makes it easier to read the template by reducing
the complexity of the part that includes text.

Reading files or other sources


To access a model file or database, your template code can use assemblies such as System.XML. To gain access
to these assemblies, you must insert directives such as these:

<#@ assembly name="System.Xml.dll" #>


<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>

The assembly directive makes the specified assembly available to your template code, in the same manner as
the References section of a Visual Studio project. You do not need to include a reference to System.dll, which is
referenced automatically. The import directive lets you use types without using their fully qualified names, in
the same manner as the using directive in an ordinary program file.
For example, after importing System.IO, you could write:

<# var properties = File.ReadLines("C:\\propertyList.txt");#>


...
<# foreach (string propertyName in properties) { #>
...

<# For Each propertyName As String In


File.ReadLines("C:\\propertyList.txt")
#>

Opening a file with a relative pathname


To load a file from a location relative to the text template, you can use this.Host.ResolvePath() . To use
this.Host, you must set hostspecific="true" in the template :

<#@ template debug="false" hostspecific="true" language="C#" #>

Then you can write, for example:

<# string fileName = this.Host.ResolvePath("filename.txt");


string [] properties = File.ReadLines(filename);
#>
...
<# foreach (string propertyName in properties { #>
...

<# Dim fileName = Me.Host.ResolvePath("propertyList.txt")


Dim properties = File.ReadLines(filename)
#>
...
<# For Each propertyName As String In properties
...
#>

You can also use this.Host.TemplateFile , which identifies the name of the current template file.
The type of this.Host (in VB, Me.Host ) is Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost .
Getting data from Visual Studio
To use services provided in Visual Studio, set the hostSpecific attribute and load the EnvDTE assembly. You
can then use IServiceProvider.GetCOMService() to access DTE and other services. For example:

<#@ template hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetCOMService(typeof(EnvDTE.DTE));
#>

Number of projects in this VS solution: <#= dte.Solution.Projects.Count #>


TIP
A text template runs in its own app domain, and services are accessed by marshaling. In this circumstance,
GetCOMService() is more reliable than GetService().

Regenerating the code automatically


Typically, several files in a Visual Studio solution are generated with one input model. Each file is generated
from its own template, but the templates all refer to the same model.
If the source model changes, you should re-run all the templates in the solution. To do this manually, choose
Transform All Templates on the Build menu.
If you have installed Visual Studio Visualization and Modeling SDK, you can have all the templates transformed
automatically whenever you perform a build. To do this, edit your project file (.csproj or .vbproj) in a text editor
and add the following lines near the end of the file, after any other <import> statements:

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK
are installed automatically when you install specific features of Visual Studio. For more details, see this blog
post.

<Import
Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\TextTemplating\Microsoft.TextTemplating.targ
ets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<!-- Other properties can be inserted here -->
</PropertyGroup>

For more information, see Code Generation in a Build Process.

Error reporting
To place error and warning messages in the Visual Studio error window, you can use these methods:

Error("An error message");


Warning("A warning message");

Converting an existing file to a template


A useful feature of templates is that they look very much like the files that they generate, together with some
inserted program code. This suggests a useful method of creating a template. First create an ordinary file as a
prototype, such as a Visual C# file, and then gradually introduce generation code that varies the resulting file.
To convert an existing file to a design-time template
1. To your Visual Studio project, add a file of the type that you want to generate, such as a .cs , .vb , or
.resx file.

2. Test the new file to make sure that it works.


3. In Solution Explorer, change the file name extension to .tt.
4. Verify the following properties of the .tt file:
Custom Tool = TextTemplatingFileGenerator

Build Action = None

5. Insert the following lines at the beginning of the file:

<#@ template debug="false" hostspecific="false" language="C#" #>


<#@ output extension=".cs" #>

If you want to write the generating code of your template in Visual Basic, set the language attribute to
"VB" instead of "C#" .

Set the extension attribute to the file name extension for the type of file that you want to generate, for
example .cs , .resx , or .xml .
6. Save the file.
A subsidiary file is created, with the specified extension. Its properties are correct for the type of file. For
example, the Build Action property of a .cs file would be Compile.
Verify that the generated file contains the same content as the original file.
7. Identify a part of the file that you want to vary. For example, a part that appears only under certain
conditions, or a part that is repeated, or where the specific values vary. Insert generating code. Save the
file and verify that the subsidiary file is correctly generated. Repeat this step.

Guidelines for Code Generation


Please see Guidelines for Writing T4 Text Templates.

Next steps
NEXT STEP TOPIC

Write and debug a more advanced text template, with code Writing a T4 Text Template
that uses auxiliary functions, included files, and external data.

Generate documents from templates at run time. Run-Time Text Generation with T4 Text Templates

Run text generation outside Visual Studio. Generating Files with the TextTransform Utility

Transform your data in the form of a domain-specific Generating Code from a Domain-Specific Language
language.

Write directive processors to transform your own data Customizing T4 Text Transformation
sources.

See Also
Guidelines for Writing T4 Text Templates
Walkthrough: Generating Code by using Text
Templates
10/18/2017 13 min to read Edit Online

Code generation allows you to produce program code that is strongly typed, and yet can be easily changed when
the source model changes. Contrast this with the alternative technique of writing a completely generic program
that accepts a configuration file, which is more flexible, but results in code that is neither so easy to read and
change, nor has such good performance. This walkthrough demonstrates this benefit.

Typed code for reading XML


The System.Xml namespace provides comprehensive tools for loading an XML document and then navigating it
freely in memory. Unfortunately, all the nodes have the same type, XmlNode. It is therefore very easy to make
programming mistakes such as expecting the wrong type of child node, or the wrong attributes.
In this example project, a template reads a sample XML file, and generates classes that correspond to each type of
node. In the hand-written code, you can use these classes to navigate the XML file. You can also run your
application on any other files that use the same node types. The purpose of the sample XML file is to provide
examples of all the node types that you want your application to deal with.

NOTE
The application xsd.exe, which is included with Visual Studio, can generate strongly-typed classes from XML files. The template
shown here is provided as an example.

Here is the sample file:

<?xml version="1.0" encoding="utf-8" ?>


<catalog>
<artist id ="Mike%20Nash" name="Mike Nash Quartet">
<song id ="MikeNashJazzBeforeTeatime">Jazz Before Teatime</song>
<song id ="MikeNashJazzAfterBreakfast">Jazz After Breakfast</song>
</artist>
<artist id ="Euan%20Garden" name="Euan Garden">
<song id ="GardenScottishCountry">Scottish Country Garden</song>
</artist>
</catalog>

In the project that this walkthrough constructs, you can write code such as the following, and IntelliSense prompts
you with the correct attribute and child names as you type:

Catalog catalog = new Catalog(xmlDocument);


foreach (Artist artist in catalog.Artist)
{
Console.WriteLine(artist.name);
foreach (Song song in artist.Song)
{
Console.WriteLine(" " + song.Text);
}
}
Contrast this with the untyped code that you might write without the template:

XmlNode catalog = xmlDocument.SelectSingleNode("catalog");


foreach (XmlNode artist in catalog.SelectNodes("artist"))
{
Console.WriteLine(artist.Attributes["name"].Value);
foreach (XmlNode song in artist.SelectNodes("song"))
{
Console.WriteLine(" " + song.InnerText);
}
}

In the strongly typed version, a change to the XML schema will result in changes to the classes. The compiler will
highlight the parts of the application code that must be changed. In the untyped version that uses generic XML
code, there is no such support.
In this project, a single template file is used to generate the classes that make the typed version possible.

Setting up the Project


Create or open a C# project
You can apply this technique to any code project. This walkthrough uses a C# project, and for the purposes of
testing we use a console application.
To c r e a t e t h e p r o j e c t

1. On the File menu click New and then click Project.


2. Click the Visual C# node, and then in the Templates pane, click Console Application.
Add a prototype XML file to the project
The purpose of this file is to provide samples of the XML node types that you want your application to be able to
read. It could be a file that will be used for testing your application. The template will produce a C# class for each
node type in this file.
The file should be part of the project so that the template can read it, but it will not be built into the compiled
application.
To a d d a n X M L fi l e

1. In Solution Explorer, right-click the project, click Add and then Click New Item.
2. In the Add New Item dialog box, select XML File from the Templates pane.
3. Add your sample content to the file.
4. For this walkthrough, name the file exampleXml.xml . Set the content of the file to be the XML shown in the
previous section.
..
Add a test code file
Add a C# file to your project and write in it a sample of the code that you want to be able to write. For example:
using System;
namespace MyProject
{
class CodeGeneratorTest
{
public void TestMethod()
{
Catalog catalog = new Catalog(@"..\..\exampleXml.xml");
foreach (Artist artist in catalog.Artist)
{
Console.WriteLine(artist.name);
foreach (Song song in artist.Song)
{
Console.WriteLine(" " + song.Text);
} } } } }

At this stage, this code will fail to compile. As you write the template, you will generate classes that allow it to
succeed.
A more comprehensive test could check the output of this test function against the known content of the example
XML file. But in this walkthrough, we will be satisfied when the test method compiles.
Add a text template file
Add a text template file, and set the output extension to ".cs".
To a d d a t e x t t e m p l a t e fi l e t o y o u r p r o j e c t

1. In Solution Explorer, right-click the project, click Add, and then click New Item.
2. In the Add New Item dialog box select Text Template from the Templates pane.

NOTE
Make sure that you add a Text Template, and not a Preprocessed Text Template.

3. In the file, in the template directive, change the hostspecific attribute to true .
This change will enable the template code to gain access to the Visual Studio services.
4. In the output directive, change the extension attribute to ".cs", so that the template generates a C# file. In a
Visual Basic project, you would change it to ".vb".
5. Save the file. At this stage, the text template file should contain these lines:

<#@ template debug="false" hostspecific="true" language="C#" #>


<#@ output extension=".cs" #>

.
Notice that a .cs file appears in Solution Explorer as a subsidiary of the template file. You can see it by
clicking [+] next to the name of the template file. This file is generated from the template file whenever you
save or move the focus away from the template file. The generated file will be compiled as part of your
project.
For convenience while you develop the template file, arrange the windows of the template file and the
generated file so that you can see them next to each other. This lets you see immediately the output of your
template. You will also notice that when your template generates invalid C# code, errors will appear in the
error message window.
Any edits you perform directly in the generated file will be lost whenever you save the template file. You
should therefore either avoid editing the generated file, or edit it only for short experiments. It is sometimes
useful to try a short fragment of code in the generated file, where IntelliSense is in operation, and then copy
it to the template file.

Developing the Text Template


Following the best advice on agile development, we will develop the template in small steps, clearing some of the
errors at each increment, until the test code compiles and runs correctly.
Prototype the code to be generated
The test code requires a class for each node in the file. Therefore, some of the compilation errors will go away if you
append these lines to the template, and then save it:

class Catalog {}
class Artist {}
class Song {}

This helps you see what is required, but the declarations should be generated from the node types in the sample
XML file. Delete these experimental lines from the template.
Generate application code from the model XML file
To read the XML file and generate class declarations, replace the template content with the following template code:

<#@ template debug="false" hostspecific="true" language="C#" #>


<#@ output extension=".cs" #>
<#@ assembly name="System.Xml"#>
<#@ import namespace="System.Xml" #>
<#
XmlDocument doc = new XmlDocument();
// Replace this file path with yours:
doc.Load(@"C:\MySolution\MyProject\exampleXml.xml");
foreach (XmlNode node in doc.SelectNodes("//*"))
{
#>
public partial class <#= node.Name #> {}
<#
}
#>

Replace the file path with the correct path for your project.
Notice the code block delimiters <#...#> . These delimiters bracket a fragment of the program code that generates
the text. The expression block delimiters <#=...#> bracket an expression that can be evaluated to a string.
When you are writing a template that generates source code for your application, you are dealing with two
separate program texts. The program inside the code block delimiters runs every time that you save the template or
move the focus to another window. The text that it generates, which appears outside the delimiters, is copied to the
generated file and becomes part of your application code.
The <#@assembly#> directive behaves like a reference, making the assembly available to the template code. The list
of assemblies seen by the template is separate from the list of References in the application project.
The <#@import#> directive acts like a using statement, allowing you to use the short names of classes in the
imported namespace.
Unfortunately, although this template generates code, it produces a class declaration for every node in the example
XML file, so that if there are several instances of the <song> node, several declarations of the class song will appear.
Read the model file, then generate the code
Many text templates follow a pattern in which the first part of the template reads the source file, and the second
part generates the template. We need to read all of the example file to summarize the node types that it contains,
and then generate the class declarations. Another <#@import#> is needed so that we can use Dictionary<>:

<#@ template debug="false" hostspecific="true" language="C#" #>


<#@ output extension=".cs" #>
<#@ assembly name="System.Xml"#>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#
// Read the model file
XmlDocument doc = new XmlDocument();
doc.Load(@"C:\MySolution\MyProject\exampleXml.xml");
Dictionary <string, string> nodeTypes =
new Dictionary<string, string>();
foreach (XmlNode node in doc.SelectNodes("//*"))
{
nodeTypes[node.Name] = "";
}
// Generate the code
foreach (string nodeName in nodeTypes.Keys)
{
#>
public partial class <#= nodeName #> {}
<#
}
#>

Add an auxiliary method


A class feature control block is a block in which you can define auxiliary methods. The block is delimited by
<#+...#> and it must appear as the last block in the file.

If you prefer class names to begin with an uppercase letter, you can replace the last part of the template with the
following template code:

// Generate the code


foreach (string nodeName in nodeTypes.Keys)
{
#>
public partial class <#= UpperInitial(nodeName) #> {}
<#
}
#>
<#+
private string UpperInitial(string name)
{ return name[0].ToString().ToUpperInvariant() + name.Substring(1); }
#>

At this stage, the generated .cs file contains the following declarations:

public partial class Catalog {}


public partial class Artist {}
public partial class Song {}

More details such as properties for the child nodes, attributes, and inner text can be added using the same
approach.
Accessing the Visual Studio API
Setting the hostspecific attribute of the <#@template#> directive allows the template to obtain access to the Visual
Studio API. The template can use this to obtain the location of the project files, to avoid using an absolute file path
in the template code.

<#@ template debug="false" hostspecific="true" language="C#" #>


...
<#@ assembly name="EnvDTE" #>
...
EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
.GetService(typeof(EnvDTE.DTE));
// Open the prototype document.
XmlDocument doc = new XmlDocument();
doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml"));

Completing the Text Template


The following template content generates code that allows the test code to compile and run.

<#@ template debug="false" hostspecific="true" language="C#" #>


<#@ output extension=".cs" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
using System;using System.Collections.Generic;using System.Linq;using System.Xml;namespace MyProject{
<#
// Map node name --> child name --> child node type
Dictionary<string, Dictionary<string, XmlNodeType>> nodeTypes = new Dictionary<string, Dictionary<string,
XmlNodeType>>();

// The Visual Studio host, to get the local file path.


EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
.GetService(typeof(EnvDTE.DTE));
// Open the prototype document.
XmlDocument doc = new XmlDocument();
doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml"));
// Inspect all the nodes in the document.
// The example might contain many nodes of the same type,
// so make a dictionary of node types and their children.
foreach (XmlNode node in doc.SelectNodes("//*"))
{
Dictionary<string, XmlNodeType> subs = null;
if (!nodeTypes.TryGetValue(node.Name, out subs))
{
subs = new Dictionary<string, XmlNodeType>();
nodeTypes.Add(node.Name, subs);
}
foreach (XmlNode child in node.ChildNodes)
{
subs[child.Name] = child.NodeType;
}
foreach (XmlNode child in node.Attributes)
{
subs[child.Name] = child.NodeType;
}
}
// Generate a class for each node type.
foreach (string className in nodeTypes.Keys)
{
// Capitalize the first character of the name.
#>
partial class <#= UpperInitial(className) #>
{ private XmlNode thisNode; public <#= UpperInitial(className) #>(XmlNode node) { thisNode
= node; }
<#
// Generate a property for each child.
foreach (string childName in nodeTypes[className].Keys)
{
// Allow for different types of child.
switch (nodeTypes[className][childName])
{
// Child nodes:
case XmlNodeType.Element:
#>
public IEnumerable<<#=UpperInitial(childName)#>><#=UpperInitial(childName) #> { get
{ foreach (XmlNode node in thisNode.SelectNodes("<#=childName#>")) yield
return new <#=UpperInitial(childName)#>(node); } }
<#
break;
// Child attributes:
case XmlNodeType.Attribute:
#>
public string <#=childName #> { get { return thisNode.Attributes["<#=childName#>"].Value; } }
<#
break;
// Plain text:
case XmlNodeType.Text:
#>
public string Text { get { return thisNode.InnerText; } }
<#
break;
} // switch
} // foreach class child
// End of the generated class:
#>
}
<#
} // foreach class

// Add a constructor for the root class


// that accepts an XML filename.
string rootClassName = doc.SelectSingleNode("*").Name;
#>
partial class <#= UpperInitial(rootClassName) #> { public <#= UpperInitial(rootClassName) #>(string
fileName){ XmlDocument doc = new XmlDocument(); doc.Load(fileName); thisNode =
doc.SelectSingleNode("<#=rootClassName#>");} }}
<#+
private string UpperInitial(string name)
{
return name[0].ToString().ToUpperInvariant() + name.Substring(1);
}
#>

Running the test program


In the main of the console application, the following lines will execute the test method. Press F5 to run the program
in debug mode:

using System;
namespace MyProject
{ class Program
{ static void Main(string[] args)
{ new CodeGeneratorTest().TestMethod();
// Allow user to see the output:
Console.ReadLine();
} } }

Writing and updating the application


The application can now be written in strongly-typed style, using the generated classes instead of using generic
XML code.
When the XML schema changes, new classes can easily be generated. The compiler will tell the developer where the
application code must be updated.
To regenerate the classes when the example XML file is changed, click Transform All Templates in the Solution
Explorer toolbar.

Conclusion
This walkthrough demonstrates several techniques and benefits of code generation:
Code generation is the creation of part of the source code of your application from a model. The model
contains information in a form suited to the application domain, and may change over the lifetime of the
application.
Strong typing is one benefit of code generation. While the model represents information in a form more
suitable to the user, the generated code allows other parts of the application to deal with the information
using a set of types.
IntelliSense and the compiler help you create code that adheres to the schema of the model, both when you
write new code and when the schema is updated.
The addition of a single uncomplicated template file to a project can provide these benefits.
A text template can be developed and tested rapidly and incrementally.
In this walkthrough, the program code is actually generated from an instance of the model, a representative
example of the XML files that the application will process. In a more formal approach, the XML schema
would be the input to the template, in the form of an .xsd file or a domain-specific language definition. That
approach would make it easier for the template to determine characteristics such as the multiplicity of a
relationship.

Troubleshooting the Text Template


If you have seen template transformation or compilation errors in the Error List, or if the output file was not
generated correctly, you can troubleshoot the text template with the techniques described in Generating Files with
the TextTransform Utility.

See Also
Design-Time Code Generation by using T4 Text Templates
Writing a T4 Text Template
Code Generation in a Build Process
10/18/2017 6 min to read Edit Online

Text transformation can be invoked as part of the build process of a Visual Studio solution. There are build tasks
that are specialized for text transformation. The T4 build tasks run design-time text templates, and they also
compile run-time (preprocessed) text templates.
There are some differences in what the build tasks can do, depending on which build engine you use. When you
build the solution in Visual Studio, a text template can access the Visual Studio API (EnvDTE) if the
hostspecific="true" attribute is set. But that isn't true when you build the solution from the command line or when
you initiate a server build through Visual Studio. In those cases, the build is performed by MSBuild and a different
T4 host is used.
This means that you can't access things like project file names in the same way when you build a text template in
MSBuild. However, you can pass environment information into text templates and directive processors by using
build parameters.

Configure your machines


To enable build tasks on your development computer, install Modeling SDK for Visual Studio.

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.

If your build server runs on a computer on which Visual Studio is not installed, copy the following files to the build
computer from your development machine. Substitute the most recent version numbers for '*'.
$(ProgramFiles)\MSBuild\Microsoft\VisualStudio\v*.0\TextTemplating
Microsoft.VisualStudio.TextTemplating.Sdk.Host.*.0.dll
Microsoft.TextTemplating.Build.Tasks.dll
Microsoft.TextTemplating.targets
$(ProgramFiles)\Microsoft Visual Studio *.0\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0
Microsoft.VisualStudio.TextTemplating.*.0.dll
Microsoft.VisualStudio.TextTemplating.Interfaces.*.0.dll (several files)
Microsoft.VisualStudio.TextTemplating.VSHost.*.0.dll
$(ProgramFiles)\Microsoft Visual Studio *.0\Common7\IDE\PublicAssemblies\
Microsoft.VisualStudio.TextTemplating.Modeling.*.0.dll

To edit the project file


You'll have to edit your project file to configure some of the features in MSBuild.
In solution explorer, choose Unload from the context menu of your project. That allows you to edit the .csproj or
.vbproj file in the XML editor.
When you've finished editing, choose Reload.
Import the Text Transformation Targets
In the .vbproj or .csproj file, find a line like this:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

- or -
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

After that line, insert the Text Templating import:

<!-- Optionally make the import portable across VS versions -->


<PropertyGroup>
<!-- Get the Visual Studio version - defaults to 10: -->
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<!-- Keep the next element all on one line: -->
<VSToolsPath Condition="'$(VSToolsPath)' ==
''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<!-- This is the important line: -->


<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

Transforming templates in a build


There are some properties that you can insert into your project file to control the transformation task:
Run the Transform task at the start of every build:

<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>

Overwrite files that are read-only, for example because they are not checked out:

<PropertyGroup>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
</PropertyGroup>

Transform every template every time:

<PropertyGroup>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

By default, the T4 MSBuild task regenerates an output file if it is older than its template file, or any files that
are included, or any files that have previously been read by the template or by a directive processor that it
uses. Notice that this is a much more powerful dependency test than is used by the Transform All Templates
command in Visual Studio, which only compares the dates of the template and output file.
To perform just the text transformations in your project, invoke the TransformAll task:
msbuild myProject.csproj /t:TransformAll

To transform a specific text template:


msbuild myProject.csproj /t:Transform /p:TransformFile="Template1.tt"
You can use wildcards in TransformFile:
msbuild dsl.csproj /t:Transform /p:TransformFile="GeneratedCode\**\*.tt"

Source Control
There is no specific built-in integration with a source control system. However, you can add your own extensions,
for example to check out and check in a generated file.By default, the text transform task avoids overwriting a file
that is marked as read- only, and when such a file is encountered, an error is logged in the Visual Studio error list,
and the task fails.
To specify that read-only files should be overwritten, insert this property:
<OverwriteReadOnlyOuputFiles>true</OverwriteReadOnlyOuputFiles>

Unless you customize the postprocessing step, a warning will be logged in the error list when a file is overwritten.

Customizing the build process


Text transformation happens before other tasks in the build process. You can define tasks that are invoked before
and after transformation, by setting the properties $(BeforeTransform) and $(AfterTransform) :

<PropertyGroup>
<BeforeTransform>CustomPreTransform</BeforeTransform>
<AfterTransform>CustomPostTransform</AfterTransform>
</PropertyGroup>
<Target Name="CustomPreTransform">
<Message Text="In CustomPreTransform..." Importance="High" />
</Target>
<Target Name="CustomPostTransform">
<Message Text="In CustomPostTransform..." Importance="High" />
</Target>

In AfterTransform , you can reference lists of files:


GeneratedFiles - a list of files written by the process. For those files that overwrote existing read-only files,
%(GeneratedFiles.ReadOnlyFileOverwritten) will be true. These files can be checked out of source control.
NonGeneratedFiles - a list of read-only files that were not overwritten.
For example, you define a task to check out GeneratedFiles.

OutputFilePath and OutputFileName


These properties are used only by MSBuild. They do not affect code generation in Visual Studio. They redirect the
generated output file to a different folder or file. The target folder must already exist.

<ItemGroup>
<None Include="MyTemplate.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<OutputFilePath>MyFolder</OutputFilePath>
<LastGenOutput>MyTemplate.cs</LastGenOutput>
</None>
</ItemGroup>

A useful folder to redirect to is $(IntermediateOutputPath).

If you specify and output filename, it will take precedence over the extension specified in the output directive in the
templates.
<ItemGroup>
<None Include="MyTemplate.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<OutputFileName>MyOutputFileName.cs</OutputFileName>
<LastGenOutput>MyTemplate.cs</LastGenOutput>
</None>
</ItemGroup>

Specifying an OutputFileName or OutputFilePath isn't recommended if you are also transforming templates inside
VS using Transform All, or running the single file generator. You will end up with different file paths depending on
how you triggered the transformation. This can be very confusing.

Adding reference and include paths


The host has a default set of paths where it searches for assemblies referenced in templates. To add to this set:

<ItemGroup>
<T4ReferencePath Include="$(VsIdePath)PublicAssemblies\" />
<!-- Add more T4ReferencePath items here -->
</ItemGroup>

To set the folders that will be searched for include files, provide a semicolon-separated list. Usually you add to the
existing folder list.

<PropertyGroup>
<IncludeFolders>
$(IncludeFolders);$(MSBuildProjectDirectory)\Include;AnotherFolder;And\Another</IncludeFolders>
</PropertyGroup>

Pass build context data into the templates


You can set parameter values in the project file. For example, you can pass build properties and environment
variables:

<ItemGroup>
<T4ParameterValues Include="ProjectFolder">
<Value>$(ProjectDir)</Value>
<Visible>false</Visible>
</T4ParameterValues>
</ItemGroup>

In a text template, set hostspecific in the template directive. Use the parameter directive to get values:

<#@template language="c#" hostspecific="true"#>


<#@ parameter type="System.String" name="ProjectFolder" #>
The project folder is: <#= ProjectFolder #>

In a directive processor, you can call ResolveParameterValue:

string value = Host.ResolveParameterValue("-", "-", "parameterName");

Dim value = Host.ResolveParameterValue("-", "-", "parameterName")


NOTE
ResolveParameterValue gets data from T4ParameterValues only when you use MSBuild. When you transform the
template using Visual Studio, the parameters will have default values.

Using project properties in assembly and include directives


Visual Studio macros like $(SolutionDir) don't work in MSBuild. You can use project properties instead.
Edit your .csproj or .vbproj file to define a project property. This example defines a property named myLibFolder :

<!-- Define a project property, myLibFolder: -->


<PropertyGroup>
<myLibFolder>$(MSBuildProjectDirectory)\..\libs</myLibFolder>
</PropertyGroup>

<!-- Tell the MSBuild T4 task to make the property available: -->
<ItemGroup>
<T4ParameterValues Include="myLibFolder">
<Value>$(myLibFolder)</Value>
</T4ParameterValues>
</ItemGroup>

Now you can use your project property in assembly and include directives:

<#@ assembly name="$(myLibFolder)\MyLib.dll" #>


<#@ include file="$(myLibFolder)\MyIncludeFile.t4" #>

These directives get values from T4parameterValues both in MSBuild and in Visual Studio hosts.

Q&A
Why would I want to transform templates in the build server? I already transformed templates in Visual
Studio before I checked in my code.
If you update an included file, or another file read by the template, Visual Studio doesn't transform the file
automatically. Transforming templates as part of the build makes sure that everything's up to date.
What other options are there for transforming text templates?
The TextTransform utility can be used in command scripts. In most cases, it's easier to use MSBuild.
Invoking Text Transformation in a VS Extension
Design-time text templates are transformed by Visual Studio.
Run time text templates are transformed at run time in your application.

Read more
There is good guidance in the T4 MSbuild template,
$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets
Writing a T4 Text Template
Oleg Sych: Understanding T4:MSBuild Integration

In this release of Visual Studio, the Text Template Transformation SDK and the Visual Studio Modeling SDK are
installed automatically when you install specific features of Visual Studio. For more details, see this blog post.
Security of Text Templates
10/18/2017 1 min to read Edit Online

Text templates have the following security concerns:


Text templates are vulnerable to arbitrary code insertions.
If the mechanism that the host uses to find a directive processor is not secure, a malicious directive processor
could be run.

Arbitrary Code
When you write a template, you can put any code within the <# #> tags. This allows arbitrary code to be executed
from within a text template.
Be sure you obtain templates from trusted sources. Make sure to warn the end-users of your application not to
execute templates that do not come from trusted sources.

Malicious Directive Processor


The text template engine interacts with a transformation host and one or more directive processors to transform
the template text to an output file. For more information, see The Text Template Transformation Process.
If the mechanism that the host uses to find a directive processor is not secure, it runs the risk of running a malicious
directive processor. The malicious directive processor could provide code that is run in FullTrust mode when the
template is run. If you create a custom text template transformation host, you must use a secure mechanism, such
as the registry, for the engine to locate directive processors.
Generating Files with the TextTransform Utility
10/31/2017 2 min to read Edit Online

TextTransform.exe is a command-line tool that you can use to transform a text template. When you call
TextTransform.exe, you specify the name of a text template file as an argument. TextTransform.exe calls the text
transformation engine and processes the text template. TextTransform.exe is usually called from scripts. However,
it is not usually required, because you can perform text transformation either in Visual Studio or in the build
process.

NOTE
If you want to perform text transformation as part of a build process, consider using the MSBuild text transformation task.
For more information, see Code Generation in a Build Process. In a machine on which Visual Studio is installed, you can also
write an application or Visual Studio Extension that can transform text templates. For more information, see Processing Text
Templates by using a Custom Host.

TextTransform.exe is located in the following directory:


\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE
for the Professional edition, or
\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE
for the Enterprise edition.
In previous versions of Visual Studio, the file is found in the following location:
\Program Files (x86)\Common Files\Microsoft Shared\TextTemplating{version}
where {version} depends on which previous version is installed.

Syntax
TextTransform [<options>] <templateName>

Parameters

ARGUMENT DESCRIPTION

templateName Identifies the name of the template file that you want to
transform.

OPTION DESCRIPTION

-out <filename> The file to which the output of the transform is written.

-r <assembly> An assembly used for compiling and running the text


template.

-u <namespace> A namespace that is used for compiling the template.


OPTION DESCRIPTION

-I <includedirectory> A directory that contains the text templates included in the


specified text template.

-P <referencepath> A directory to search for assemblies specified within the text


template or for using the -r option.

For example, to include assemblies used for the Visual Studio


API, use

-P "%VSSHELLFOLDER%\Common7\IDE\PublicAssemblies"

-dp <processorName>!<className>! The name, full type name, and assembly of a directive
<assemblyName|codeBase> processor that can be used to process custom directives
within the text template.

-a [processorName]![directiveName]!<parameterName>! Specify a parameter value for a directive processor. If you


<parameterValue> specify just the parameter name and value, the parameter will
be available to all directive processors. If you specify a
directive processor, the parameter is available only to the
specified processor. If you specify a directive name, the
parameter is available only when the specified directive is
being processed.

To access the parameter values from a directive processor or


text template, use ResolveParameterValue. In a text template,
include hostspecific in the template directive and invoke
the message on this.Host . For example:

<#@template language="c#" hostspecific="true"#> [<#=


this.Host.ResolveParameterValue("", "",
"parameterName") #>]
.

Always type the '!' marks, even if you omit the optional
processor and directive names. For example:

-a !!param!value

-h Provides help.

Related topics
TASK TOPIC

Generate files in a Visual Studio solution. Design-Time Code Generation by using T4 Text Templates

Write directive processors to transform your own data Customizing T4 Text Transformation
sources.

Write a text templating host that allows you to invoke text Processing Text Templates by using a Custom Host
templates from your own application.
Run-Time Text Generation with T4 Text Templates
10/18/2017 10 min to read Edit Online

You can generate text strings in your application at run time by using Visual Studio runtime text templates. The
computer where the application executes does not have to have Visual Studio. Runtime templates are sometimes
called "preprocessed text templates" because at compile time, the template generates code that is executed at
run time.
Each template is a mixture of the text as it will appear in the generated string, and fragments of program code.
The program fragments supply values for the variable parts of the string, and also control conditional and
repeated parts.
For example, the following template could be used in an application that creates an HTML report.

<#@ template language="C#" #>


<html><body>
<h1>Sales for Previous Month</h2>
<table>
<# for (int i = 1; i <= 10; i++)
{ #>
<tr><td>Test name <#= i #> </td>
<td>Test value <#= i * i #> </td> </tr>
<# } #>
</table>
This report is Company Confidential.
</body></html>

Notice that the template is an HTML page in which the variable parts have been replaced with program code.
You could begin the design of such a page by writing a static prototype of the HTML page. You could then
replace the table and other variable parts with program code that generates the content that varies from one
occasion to the next.
Using a template in your application makes it is easier to see the final form of the output than you could in, for
example, a long series of write statements. Making changes to the form of the output is easier and more reliable.

Creating a Run-Time Text Template in any Application


To create a run-time text template
1. In Solution Explorer, on the shortcut menu of your project, choose Add, New Item.
2. In the Add New Item dialog box, select Runtime Text Template. (In Visual Basic look under Common
Items\General.)
3. Type a name for your template file.

NOTE
The template file name will be used as a class name in the generated code. Therefore, it should not have spaces or
punctuation.

4. Choose Add.
A new file is created that has extension .tt. Its Custom Tool property is set to
TextTemplatingFilePreprocessor. It contains the following lines:
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>

Converting an Existing File to a Run-Time Template


A good way to create a template is to convert an existing example of the output. For example, if your application
will generate HTML files, you can start by creating a plain HTML file. Make sure that it works correctly and that its
appearance is correct. Then include it into your Visual Studio project and convert it to a template.
To convert an existing text file to a run-time template
1. Include the file into your Visual Studio project. In Solution Explorer, on the shortcut menu of the project,
choose Add, Existing Item.
2. Set the file's Custom Tools property to TextTemplatingFilePreprocessor. In Solution Explorer, on the
shortcut menu of the file, choose Properties.

NOTE
If the property is already set, make sure that it is TextTemplatingFilePreprocessor and not
TextTemplatingFileGenerator. This can happen if you include a file that already has the extension .tt.

3. Change the file name extension to .tt. Although this step is optional, it helps you avoid opening the file in
an incorrect editor.
4. Remove any spaces or punctuation from the main part of the file name. For example "My Web Page.tt"
would be incorrect, but "MyWebPage.tt" is correct. The file name will be used as a class name in the
generated code.
5. Insert the following line at the beginning of the file. If you are working in a Visual Basic project, replace
"C#" with "VB".
<#@ template language="C#" #>

The Content of the Run-Time Template


Template directive
Keep the first line of the template as it was when you created the file:
<#@ template language="C#" #>

The language parameter will depend on the language of your project.


Plain content
Edit the .tt file to contain the text that you want your application to generate. For example:

<html><body>
<h1>Sales for January</h2>
<!-- table to be inserted here -->
This report is Company Confidential.
</body></html>

Embedded program code


You can insert program code between <# and #> . For example:

<table>
<# for (int i = 1; i <= 10; i++)
{ #>
<tr><td>Test name <#= i #> </td>
<td>Test value <#= i * i #> </td> </tr>
<# } #>
</table>

<table>
<#
For i As Integer = 1 To 10
#>
<tr><td>Test name <#= i #> </td>
<td>Test value <#= i*i #> </td></tr>
<#
Next
#>
</table>

Notice that statements are inserted between <# ... #> and expressions are inserted between <#= ... #> . For
more information, see Writing a T4 Text Template.

Using the Template


The code built from the template
Whenever you save the .tt file, a subsidiary .cs or .vb file will be generated. To see this file in Solution Explorer,
expand the .tt file node. In a Visual Basic project, you will be able to expand the node after you click Show All
Files in the Solution Explorer toolbar.
Notice that this subsidiary file contains a partial class that contains a method called TransformText() . You can
call this method from your application.
Generating text at run time
In your application code, you can generate the content of your template using a call like this:

MyWebPage page = new MyWebPage();


String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Dim page = New My.Templates.MyWebPage


Dim pageContent = page.TransformText()
System.IO.File.WriteAllText("outputPage.html", pageContent)

To place the generated class in a particular namespace, set the Custom Tool Namespace property of the text
template file.
Debugging Runtime Text Templates
Debug and test runtime text templates in the same way as ordinary code.
You can set a breakpoint in a text template. If you start the application in debugging mode from Visual Studio,
you can step through the code and evaluate watch expressions in the usual way.
Passing parameters in the constructor
Usually a template must import some data from other parts of the application. To make this easy, the code built
by the template is a partial class. You can create another part of the same class in another file in your project.
That file can include a constructor with parameters, properties and functions that can accessed both by the code
that is embedded in the template, and by the rest of the application.
For example, you could create a separate file MyWebPageCode.cs:

partial class MyWebPage


{
private MyData m_data;
public MyWebPage(MyData data) { this.m_data = data; }}

In your template file MyWebPage.tt, you could write:

<h2>Sales figures</h2>
<table>
<# foreach (MyDataItem item in m_data.Items)
// m_data is declared in MyWebPageCode.cs
{ #>
<tr><td> <#= item.Name #> </td>
<td> <#= item.Value #> </td></tr>
<# } // end of foreach
#>
</table>

To use this template in the application:

MyData data = ...;


MyWebPage page = new MyWebPage(data);
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Constructor parameters in Visual Basic


In Visual Basic, the separate file MyWebPageCode.vb contains:

Namespace My.Templates
Partial Public Class MyWebPage
Private m_data As MyData
Public Sub New(ByVal data As MyData)
m_data = data
End Sub
End Class
End Namespace

The template file could contain:


<#@ template language="VB" #>
<html><body>
<h1>Sales for January</h2>
<table>
<#
For Each item In m_data.Items
#>
<tr><td>Test name <#= item.Name #> </td>
<td>Test value <#= item.Value #> </td></tr>
<#
Next
#>
</table>
This report is Company Confidential.
</body></html>

And the template would be invoked by passing the parameter in the constructor:

Dim data = New My.Templates.MyData


' Add data values here ....
Dim page = New My.Templates.MyWebPage(data)
Dim pageContent = page.TransformText()
System.IO.File.WriteAllText("outputPage.html", pageContent)

Passing data in template properties


An alternative method of passing data to the template is to add public properties to the template class in a partial
class definition. Your application can set the properties before invoking TransformText() .
You can also add fields to your template class in a partial definition. This would enable you to pass data between
successive executions of the template.
Use partial classes for code
Many developers prefer to avoid writing large bodies of code in templates. Instead, define methods in a partial
class that has the same name as the template file. Call those methods from the template. In this way, the
template shows you more clearly what the target output string will look like. Discussions about the appearance
of the result can be separated from the logic of creating the data that it displays.
Assemblies and references
If you want your template code to reference a .NET or other assembly such as System.Xml.dll, you should add it
to your project's References in the usual manner.
If you want to import a namespace in the same way as a using statement, you can do this with the import
directive:

<#@ import namespace="System.Xml" #>

These directives must be placed at the beginning of the file, immediately after the <#@template directive.
Shared content
If you have text that is shared between several templates, you can place it in a separate file and include it in each
file in which it should appear:

<#@include file="CommonHeader.txt" #>

The included content can contain any mixture of program code and plain text, and it can contain other include
directives and other directives.
The include directive can be used anywhere within the text of a template file or an included file.
Inheritance between Run-Time Text Templates
You can share content between run-time templates by writing a base class template, which can be abstract. Use
the inherits parameter of the <@#template#> directive to reference another runtime template class.
Inheritance pattern: Fragments in Base Methods
In the pattern used in the example that follows, notice the following points:
The base class SharedFragments defines methods within class feature blocks <#+ ... #> .
The base class contains no free text. Instead, all its text blocks occur inside the class feature methods.
The derived class invokes the methods defined in SharedFragments .
The application calls the TextTransform() method of the derived class, but does not transform the base
class SharedFragments .
Both the base and derived classes are runtime text templates: that is, the Custom Tool property is set to
TextTemplatingFilePreprocessor.
SharedFragments.tt:

<#@ template language="C#" #>


<#+
protected void SharedText(int n)
{
#>
Shared Text <#= n #>
<#+
}
// Insert more methods here if required.
#>

MyTextTemplate1.tt:

<#@ template language="C#" inherits="SharedFragments" #>


begin 1
<# SharedText(2); #>
end 1

MyProgram.cs:

...
MyTextTemplate1 t1 = new MyTextTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

The resulting output:

begin 1
Shared Text 2
end 1

Inheritance Pattern: Text in Base Body


In this alternative approach to using template inheritance, the bulk of the text is defined in the base template. The
derived templates provide data and text fragments that fit into the base content.
AbstractBaseTemplate1.tt:

<#@ template language="C#" #>

Here is the description for this derived template:


<#= this.Description #>

Here is the fragment specific to this derived template:


<#
this.PushIndent(" ");
SpecificFragment(42);
this.PopIndent();
#>
End of common template.
<#+
// State set by derived class before calling TextTransform:
protected string Description = "";
// 'abstract' method to be defined in derived classes:
protected virtual void SpecificFragment(int n) { }
#>

DerivedTemplate1.tt:

<#@ template language="C#" inherits="AbstractBaseTemplate1" #>


<#
// Set the base template properties:
base.Description = "Description for this derived class";

// Run the base template:


base.TransformText();

#>
End material for DerivedTemplate1.

<#+
// Provide a fragment specific to this derived template:

protected override void SpecificFragment(int n)


{
#>
Specific to DerivedTemplate1 : <#= n #>
<#+
}
#>

Application code:

...
DerivedTemplate1 t1 = new DerivedTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Resulting output:

Here is the description for this derived template:


Description for this derived class

Here is the fragment specific to this derived template:


Specific to DerivedTemplate1 : 42
End of common template.
End material for DerivedTemplate1.
Related Topics
Design Time Templates: If you want to use a template to generate code that becomes part of your application,
see Design-Time Code Generation by using T4 Text Templates.
Runtime templates can be used in any application where the templates and their content are determined at
compile time. But if you want to write a Visual Studio extension that generates text from templates that change at
run time, see Invoking Text Transformation in a VS Extension.

See Also
Code Generation and T4 Text Templates
Writing a T4 Text Template
Understanding T4: Preprocessed Text Templates by Oleg Sych
Writing a T4 Text Template
10/18/2017 8 min to read Edit Online

A text template contains the text that will be generated from it. For example, a template that creates a web page
will contain "<html>..." and all the other standard parts of an HTML page. Inserted into the template are control
blocks, which are fragments of program code. Control blocks provide varying values and allow parts of the text
to be conditional and repeated.
This structure makes a template easy to develop, because you can start with a prototype of the generated file,
and incrementally insert control blocks that vary the result.
Text templates are composed of the following parts:
Directives - elements that control how the template is processed.
Text blocks - content that is copied directly to the output.
Control blocks - program code that inserts variable values into the text, and controls conditional or
repeated parts of the text.
To try the examples in this topic, copy them into a template file as described in Design-Time Code
Generation by using T4 Text Templates. After editing the template file, save it, and then inspect the output
.txt file.

Directives
Text template directives provide general instructions to the text templating engine about how to generate the
transformation code and the output file.
For example, the following directive specifies that the output file should have a .txt extension:

<#@ output extension=".txt" #>

For more information about directives, see T4 Text Template Directives.

Text blocks
A text block inserts text directly into the output file. There is no special formatting for text blocks. For example,
the following text template will produce a text file that contains the word "Hello":

<#@ output extension=".txt" #>


Hello

Control blocks
Control blocks are sections of program code that are used to transform the templates. The default language is
C#, but to use Visual Basic, you can write this directive at the beginning of the file:

<#@ template language="VB" #>


The language in which you write the code in the control blocks is unrelated to the language of the text that is
generated.
Standard control blocks
A standard control block is a section of program code that generates part of the output file.
You can mix any number of text blocks and standard control blocks in a template file. However, you cannot
place one control block inside another. Each standard control block is delimited by the symbols <# ... #> .
For example, the following control block and text block cause the output file to contain the line "0, 1, 2, 3, 4
Hello!":

<#
for(int i = 0; i < 4; i++)
{
Write(i + ", ");
}
Write("4");
#> Hello!

Instead of using explicit Write() statements, you can interleave text and code. The following example prints
"Hello!" four times:

<#
for(int i = 0; i < 4; i++)
{
#>
Hello!
<#
}
#>

You can insert a text block wherever a Write(); statement would be allowed in the code.

NOTE
When you embed a text block within a compound statement such as a loop or conditional, always use braces {...} to
contain the text block.

Expression control blocks


An expression control block evaluates an expression and converts it to a string. This is inserted into the output
file.
Expression control blocks are delimited by the symbols <#= ... #>

For example, the following control block causes the output file to contain "5":

<#= 2 + 3 #>

Notice that the opening symbol has three characters "<#=".


The expression can include any variable that is in scope. For example, this block prints lines with numbers:
<#@ output extension=".txt" #>
<#
for(int i = 0; i < 4; i++)
{
#>
This is hello number <#= i+1 #>: Hello!
<#
}
#>

Class feature control blocks


A class feature control block defines properties, methods, or any other code that should not be included in the
main transform. Class feature blocks are frequently used for helper functions. Typically, class feature blocks are
placed in separate files so that they can be included by more than one text template.
Class feature control blocks are delimited by the symbols <#+ ... #>

For example, the following template file declares and uses a method:

<#@ output extension=".txt" #>


Squares:
<#
for(int i = 0; i < 4; i++)
{
#>
The square of <#= i #> is <#= Square(i+1) #>.
<#
}
#>
That is the end of the list.
<#+ // Start of class feature block
private int Square(int i)
{
return i*i;
}
#>

Class features must be placed at the end of the file in which they are written. However, you can <#@include#> a
file that contains a class feature, even if the include directive is followed by standard blocks and text.
For more information about control blocks, see Text Template Control Blocks.
Class feature blocks can contain text blocks
You can write a method that generates text. For example:

List of Squares:
<#
for(int i = 0; i < 4; i++)
{ WriteSquareLine(i); }
#>
End of list.
<#+ // Class feature block
private void WriteSquareLine(int i)
{
#>
The square of <#= i #> is <#= i*i #>.
<#+
}
#>
It is particularly useful to place a method that generates text in a separate file that can be included by more than
one template.

Using external definitions


Assemblies
The code blocks of your template can use types that are defined the most frequently used .NET assemblies such
as System.dll. In addition, you can reference other .NET assemblies or your own assemblies. You can provide a
pathname, or the strong name of an assembly:

<#@ assembly name="System.Xml" #>

You should use absolute path names, or use standard macro names in the path name. For example:

<#@ assembly name="$(SolutionDir)library\MyAssembly.dll" #>

The assembly directive has no effect in a preprocessed text template.


For more information, see T4 Assembly Directive.
Namespaces
The import directive is the same as the using clause in C# or the imports clause in Visual Basic. It allows you
to refer to types in your code without using a fully qualified name:

<#@ import namespace="System.Xml" #>

You can use as many assembly and import directives as you want. You must place them before text and
control blocks.
For more information, see T4 Import Directive.
Including code and text
The include directive inserts text from another template file. For example, this directive inserts the content of
test.txt .

<#@ include file="c:\test.txt" #>

The included content is processed almost as if it were part of the including text template. However, you can
include a file that contains a class feature block <#+...#> even if the include directive is followed by ordinary
text and standard control blocks.
For more information, see T4 Include Directive.
Utility methods
There are several methods such as Write() that are always available to you in a control block. They include
methods for helping you indent the output, and for reporting errors.
You can also write your own set of utility methods.
For more information, see Text Template Utility Methods.

Transforming Data and Models


The most useful application for a text template is to generate material based on the content of a source such as
a model, database, or data file. Your template extracts and reformats the data. A collection of templates can
transform such a source into multiple files.
There are several approaches to reading the source file.
Read a file in the text template. This is simplest way to get data into the template:

<#@ import namespace="System.IO" #>


<# string fileContent = File.ReadAllText(@"C:\myData.txt"); ...

Load a file as a navigable model. A more powerful method is to read the data as a model, which your text
template code can navigate. For example, you can load an XML file and navigate it with XPath expressions. You
could also use xsd.exe to create a set of classes with which you can read the XML data.
Edit the model file in a diagram or form. Domain-Specific Language Tools provides tools that let you edit a
model as a diagram or Windows form. This makes it easier to discuss the model with users of the generated
application. Domain-Specific Language Tools also creates a set of strongly-typed classes that reflect the
structure of the model. For more information, see Generating Code from a Domain-Specific Language.
Relative file paths in design-time templates
In a design-time text template, if you want to reference a file in a location relative to the text template, use
this.Host.ResolvePath() . You must also set hostspecific="true" in the template directive:

<#@ template hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ import namespace="System.IO" #>
<#
// Find a path within the same project as the text template:
string myFile = File.ReadAllText(this.Host.ResolvePath("MyFile.txt"));
#>
Content of MyFile.txt is:
<#= myFile #>

You can also obtain other services that are provided by the host. For more information, see Accessing Visual
Studio or other Hosts from a Template.
Design-time Text Templates run in a separate AppDomain
You should be aware that a design-time text template runs in an AppDomain that is separate from the main
application. In most cases this is not important, but you might discover restrictions in certain complex cases. For
example, if you want to pass data in or out of the template from a separate service, then the service must
provide a serializable API.
(This isn't true of a run-time text template, which provides code that is compiled along with the rest of your
code.)

Editing Templates
Specialized text template editors can be downloaded from the Extension Manager Online Gallery. On the Tools
menu, click Extension Manager. Click Online Gallery, and then use the search tool.

Related topics
TASK TOPIC

Writing a template. Guidelines for Writing T4 Text Templates


TASK TOPIC

Generate text by using program code. Text Template Structure

Generate files in a Visual Studio solution. Design-Time Code Generation by using T4 Text Templates

Run text generation outside Visual Studio. Generating Files with the TextTransform Utility

Transform your data in the form of a domain-specific Generating Code from a Domain-Specific Language
language.

Write directive processors to transform your own data Customizing T4 Text Transformation
sources.
T4 Text Template Directives
10/18/2017 1 min to read Edit Online

Directives provide instructions to the text template transformation engine.


The syntax of directives is as follows:

<#@ DirectiveName [AttributeName = "AttributeValue"] ... #>

All attribute values must be surrounded by double quotation marks. If the value itself contains quotation marks,
they must be escaped with the \ character.
Directives are typically the first elements in a template file or an included file. You should not place them inside a
code block <#...#> , nor after a class feature block <#+...#> .
T4 Template Directive

<#@ template [language="VB"] [hostspecific="true|TrueFromBase"] [debug="true"] [inherits="templateBaseClass"]


[culture="code"] [compilerOptions="options"] [visibility="internal"] [linePragmas="false"] #>

T4 Parameter Directive

<#@ parameter type="Full.TypeName" name="ParameterName" #>

T4 Output Directive

<#@ output extension=".fileNameExtension" [encoding="encoding"] #>

T4 Assembly Directive

<#@ assembly name="[assembly strong name|assembly file name]" #>

T4 Import Directive

<#@ import namespace="namespace" #>

T4 Include Directive

<#@ include file="filePath" #>

T4 CleanUpBehavior directive

<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

In addition, you can create your own directives. For more information, see Creating Custom T4 Text Template
Directive Processors. If you use the Visualization and Modeling SDK to create a domain-specific language (DSL), a
directive processor will be generated as part of your DSL.
T4 Template Directive
10/18/2017 6 min to read Edit Online

A Visual Studio T4 text template usually starts with a template directive, which specifies how the template should
be processed. There should be no more than one template directive in a text template and any files that it includes.
For a general overview of writing text templates, see Writing a T4 Text Template.

Using the Template Directive


<#@ template [language="VB"] [compilerOptions="options"] [culture="code"] [debug="true"] [hostspecific="true"]
[inherits="templateBaseClass"] [visibility="internal"] [linePragmas="false"] #>

The template directive has several attributes that allow you to specify different aspects of the transformation. All
the attributes are optional.

compilerOptions attribute
Example:
compilerOptions="optimize+"

Valid values:
Any valid compiler options.
Ignored for run-time (preprocessed) templates.
These options are applied when the template has been converted into Visual C# or Visual Basic, and the resulting
code is compiled.

culture attribute
Example:
culture="de-CH"

Valid values:
"", the invariant culture, which is the default.
A culture expressed as a string in the form xx-XX. For example, en-US, ja-JP, de-CH, de-DE. For more information,
see System.Globalization.CultureInfo.
The culture attribute specifies the culture to use when an expression block is converted to text.

debug attribute
Example:

debug="true"

Valid values:
true, false . False is the default.
If the debug attribute is true , the intermediate code file will contain information that enables the debugger to
identify more accurately the position in your template where a break or exception occurred.
For design-time templates the intermediate code file will be written to your %TEMP% directory.
To run a design-time template in the debugger, save the text template, then open the shortcut menu of the text
template in Solution Explorer, and choose Debug T4 Template.

hostspecific attribute
Example:

hostspecific="true"

Valid values:
true, false, trueFromBase . False is the default.
If you set the value of this attribute to true , a property named Host is added to the class generated by your text
template. The property is a reference to the host of the transformation engine, and is declared as
ITextTemplatingEngineHost. If you have defined a custom host, you can cast it to the custom host type.
Because the type of this property depends on the type of host, it is only useful if you are writing a text template
that works only with a specific host. It's applicable to design-time templates, but not run-time templates.
When hostspecific is true and you are using Visual Studio, you can cast this.Host to IServiceProvider to
access Visual Studio features. You can also use Host.ResolvePath(filename) to obtain the absolute path of a file in
the project. For example:

<#@ template debug="false" hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.IO" #>
<# // Get the Visual Studio API as a service:
DTE dte = ((IServiceProvider)this.Host).GetCOMService(typeof(DTE)) as DTE;
#>
Number of projects in this solution: <#= dte.Solution.Projects.Count #>

<#
// Find a path within the current project:
string myFile = File.ReadAllText(this.Host.ResolvePath("MyFile.txt"));
#>
Content of myFile is:
<#= myFile #>

If you use the inherits and hostspecific attributes together, specify host="trueFromBase" in the derived class
and host="true" in the base class. This avoids a double definition of the Host property in the generated code.

language attribute
Example:
language="VB"

Valid values:
C# (default)

VB
The language attribute specifies the language (Visual Basic or Visual C#) to use for the source code in statement
and expression blocks. The intermediate code file from which the output is generated will use this language. This
language is not related to the language that your template generates, which can be any kind of text.
For example:

<#@ template language="VB" #>


<#@ output extension=".txt" #>
Squares of numbers:
<#
Dim number As Integer
For number = 1 To 4
#>
Square of <#= number #> is <#= number * number #>
<#
Next number
#>

inherits attribute
You can specify that the program code of your template can inherit from another class, which can also be
generated from a text template.
Inheritance in a run-time (preprocessed) text template
You can use inheritance between run-time text templates to create a basic template that has several derived
variants. Run-time templates are those that have the Custom Tool property set to
TextTemplatingFilePreprocessor. A run-time template generates code that you can call in your application to
create the text defined in the template. For more information, see Run-Time Text Generation with T4 Text
Templates.
If you do not specify an inherits attribute, a base class and a derived class are generated from your text template.
When you specify an inherits attribute, only the derived class is generated. You can write a base class by hand,
but it must provide the methods that are used by the derived class.
More typically, you specify another preprocessed template as the base class. The base template provides common
blocks of text, which can be interleaved with text from the derived templates. You can use class feature blocks
<#+ ... #> to define methods that contain text fragments. For example, you can place the framework of the
output text in the base template, providing virtual methods that can be overridden in derived templates:
Run-time (preprocessed) text template BaseTemplate.tt:

This is the common header.


<#
SpecificFragment1();
#>
A common central text.
<#
SpecificFragment2();
#>
This is the common footer.
<#+
// Declare abstract methods
protected virtual void SpecificFragment1() { }
protected virtual void SpecificFragment2() { }
#>

Run-time (preprocessed) text template DerivedTemplate1.tt:


<#@ template language="C#" inherits="BaseTemplate" #>
<#
// Run the base template:
base.TransformText();
#>
<#+
// Provide fragments specific to this derived template:
protected override void SpecificFragment1()
{
#>
Fragment 1 for DerivedTemplate1
<#+
}
protected override void SpecificFragment2()
{
#>
Fragment 2 for DerivedTemplate1
<#+
}
#>

Application code to invoke DerivedTemplate1:

Console.WriteLine(new DerivedTemplate().TransformText());

Resulting output:

This is the common header.


Fragment 1 for DerivedTemplate1
A common central text.
Fragment 2 for DerivedTemplate1
This is the common footer.

You can build the base and derived classes in different projects. Remember to add the base project or assembly to
the derived project's references.
You can also use an ordinary hand-written class as the base class. The base class must provide the methods used
by the derived class.

WARNING
If you use the inherits and hostspecific attributes together, specify hostspecific="trueFromBase" in the derived class
and host="true" in the base class. This avoids a double definition of the Host property in the generated code.

Inheritance in a design-time text template


A design-time text template is a file for which Custom Tool is set to TextTemplatingFileGenerator. The
template generates an output file of code or text, which forms part of your Visual Studio project. To generate the
output file, the template is first translated into an intermediate program code file, which you do not usually see.
The inherits attribute specifies the base class for this intermediate code.
For a design-time text template, you can specify any base class that is derived from
Microsoft.VisualStudio.TextTemplating.TextTransformation. Use the <#@assembly#> directive to load the assembly
or project that contains the base class.
For more information, see "Inheritance in Text Templates" in Gareth Jones' Blog.
LinePragmas attribute
Example:
linePragmas="false"

Valid values:
true (default)

false

Setting this attribute to false removes the tags that identify your line numbers within the generated code. This
means that the compiler will report any errors by using line numbers of the generated code.This gives you more
debugging options, as you can choose to debug either the text template or the generated code.
This attribute can also help if you're finding the absolute filenames in pragmas are causing distracting merges
under source code control.

Visibility attribute
Example:
visibility="internal"

Valid values:
public (default)

internal

In a runtime text template, this sets the visibility attribute of the generated class. By default, the class is part of the
public API of your code, but by setting visibility="internal" you can make sure that only your code can use the
text-generating class.
T4 Parameter Directive
10/18/2017 2 min to read Edit Online

In a Visual Studio text template, the parameter directive declares properties in your template code that are
initialized from values passed in from the external context. You can set these values if you write code that invokes
text transformation.

Using the Parameter Directive


<#@ parameter type="Full.TypeName" name="ParameterName" #>

The parameter directive declares properties in your template code that are initialized from values passed in from
the external context. You can set these values if you write code that invokes text transformation. The values can be
passed either in the Session dictionary, or in CallContext.
You can declare parameters of any remotable type. That is, the type must be declared with SerializableAttribute, or
it must derive from MarshalByRefObject. This allows parameter values to be passed into the AppDomain in which
the template is processed.
For example, you could write a text template with the following content:

<#@ template language="C#" #>

<#@ parameter type="System.Int32" name="TimesToRepeat" #>

<# for (int i = 0; i < TimesToRepeat; i++) { #>


Line <#= i #>
<# } #>

Passing parameter values to a template


If you are writing a Visual Studio Extension such as a menu command or an event handler, you can process a
template by using the text templating service:

// Get a service provider - how you do this depends on the context:


IServiceProvider serviceProvider = dte; // or dslDiagram.Store, for example
// Get the text template service:
ITextTemplating t4 = serviceProvider.GetService(typeof(STextTemplating)) as ITextTemplating;
ITextTemplatingSessionHost host = t4 as ITextTemplatingSessionHost;
// Create a Session in which to pass parameters:
host.Session = host.CreateSession();
// Add parameter values to the Session:
session["TimesToRepeat"] = 5;
// Process a text template:
string result = t4.ProcessTemplate("MyTemplateFile.t4",
System.IO.File.ReadAllText("MyTemplateFile.t4"));

Passing values in the Call Context


You can alternatively pass values as logical data in CallContext.
The following example passes values by using both methods:
ITextTemplating t4 = this.Store.GetService(typeof(STextTemplating)) as ITextTemplating;
ITextTemplatingSessionHost host = t4 as ITextTemplatingSessionHost;
host.Session = host.CreateSession();
// Pass a value in Session:
host.Session["p1"] = 32;
// Pass another value in CallContext:
System.Runtime.Remoting.Messaging.CallContext.LogicalSetData("p2", "test");

// Process a small template inline:


string result = t4.ProcessTemplate("",
"<#@parameter type=\"System.Int32\" name=\"p1\"#>"
+ "<#@parameter type=\"System.String\" name=\"p2\"#>"
+ "Test <#=p1#> <#=p2#>");

// Result value is:


// Test 32 test

Passing values to a Run-Time (Preprocessed) Text Template


It is not usually necessary to use the <#@parameter#> directive with run-time (preprocessed) text templates. Instead,
you can define an additional constructor or a settable property for the generated code, through which you pass
parameter values. For more information, see Run-Time Text Generation with T4 Text Templates.
However, if you want to use <#@parameter> in a run-time template, you can pass values to it by using the Session
dictionary. As an example, suppose you have created the file as a preprocessed template called PreTextTemplate1 .
You can invoke the template in your program by using the following code.

PreTextTemplate1 t = new PreTextTemplate1();


t.Session = new Microsoft.VisualStudio.TextTemplating.TextTemplatingSession();
t.Session["TimesToRepeat"] = 5;
// Add other parameter values to t.Session here.
t.Initialize(); // Must call this to transfer values.
string resultText = t.TransformText();

Obtaining arguments from TextTemplate.exe


IMPORTANT
The parameter directive does not retrieve values set in the -a parameter of the TextTransform.exe utility. To get those
values, set hostSpecific="true" in the template directive, and use
this.Host.ResolveParameterValue("","","argName") .
T4 Output Directive
10/18/2017 1 min to read Edit Online

In Visual Studio text templates, the output directive is used to define the file name extension and encoding of the
transformed file.
For example, if your Visual Studio project includes a template file named MyTemplate.tt which contains the
following directive:
<#@output extension=".cs"#>

then Visual Studio will generate a file named MyTemplate.cs


The output directive is not required in a run-time (preprocessed) text template. Instead, your application obtains
the generated string by calling TextTransform() . For more information, see Run-Time Text Generation with T4 Text
Templates.

Using the Output Directive


<#@ output extension=".fileNameExtension" [encoding="encoding"] #>

There should be no more than one output directive in each text template.

extension attribute
Specifies the file name extension of the generated text output file.
The default value is .cs
Examples:
<#@ output extension=".txt" #>

<#@ output extension=".htm" #>

<#@ output extension=".cs" #>

<#@ output extension=".vb" #>

Acceptable Values:
Any valid file name extension.

encoding attribute
Specifies the encoding to use when the output file is generated. For example:
<#@ output encoding="utf-8"#>

The default value is the encoding used by the text template file.
Acceptable Values:
us-ascii

utf-16BE
utf-16

utf-8

utf-7

utf-32

0 (System default)
In general, you can use the WebName string or the CodePage number of any of the encodings returned by
System.Text.Encoding.GetEncodings.
T4 Assembly Directive
10/18/2017 1 min to read Edit Online

In a Visual Studio design-time text template, the assembly directive loads an assembly so that your template code
can use its types. The effect is similar to adding an assembly reference in a Visual Studio project.
For a general overview of writing text templates, see Writing a T4 Text Template.

NOTE
You do not need the assembly directive in a run-time (preprocessed) text template. Instead, add the necessary assemblies
to the References of your Visual Studio project.

Using the Assembly Directive


The syntax of the directive is as follows:

<#@ assembly name="[assembly strong name|assembly file name]" #>

The assembly name should be one of the following:


The strong name of an assembly in the GAC, such as System.Xml.dll . You can also use the long form, such
as name="System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" . For more
information, see AssemblyName.
The absolute path of the assembly
You can use the $(variableName) syntax to reference Visual Studio variables such as $(SolutionDir) , and
%VariableName% to reference environment variables. For example:

<#@ assembly name="$(SolutionDir)\MyProject\bin\Debug\SomeLibrary.Dll" #>

The assembly directive has no effect in a preprocessed text template. Instead, include the necessary references in
the References section of your Visual Studio project. For more information, see Run-Time Text Generation with T4
Text Templates.

Standard Assemblies
The following assemblies are loaded automatically, so that you do not need to write assembly directives for them:
Microsoft.VisualStudio.TextTemplating.1*.dll

System.dll

WindowsBase.dll

If you use a custom directive, the directive processor might load additional assemblies. For example, if you
write templates for a domain-specific language (DSL), you do not need to write assembly directives for the
following assemblies:
Microsoft.VisualStudio.Modeling.Sdk.1*.dll
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.1*.dsl

Microsoft.VisualStudio.TextTemplating.Modeling.1*.dll

The assembly containing your DSL.

Using project properties in both MSBuild and Visual Studio


Visual Studio macros like $(SolutionDir) don't work in MSBuild. If you want to transform templates in your build
machine, you have to use project properties instead.
Edit your .csproj or .vbproj file to define a project property. This example defines a property named myLibFolder :

<!-- Define a project property, myLibFolder: -->


<PropertyGroup>
<myLibFolder>$(MSBuildProjectDirectory)\..\libs</myLibFolder>
</PropertyGroup>

<!-- Tell the MSBuild T4 task to make the property available: -->
<ItemGroup>
<T4ParameterValues Include="myLibFolder">
<Value>$(myLibFolder)</Value>
</T4ParameterValues>
</ItemGroup>

Now you can use your project property in text templates, which transform correctly in both Visual Studio and
MSBuild:

<#@ assembly name="$(myLibFolder)\MyLib.dll" #>

See Also
T4 Include Directive
T4 Import Directive
10/18/2017 1 min to read Edit Online

In the code blocks of a Visual Studio T4 text template, the import directive allows you to refer to elements in
another namespace without providing a fully-qualified name. It is the equivalent of using in C# or imports in
Visual Basic.
For a general overview of writing T4 text templates, see Writing a T4 Text Template.

Using the Import Directive


<#@ import namespace="namespace" #>

In this example, template code can omit an explicit namespace for members of System.IO:

<#@ import namespace="System.IO" #>


<#
string fileContent = File.ReadAllText("C:\x.txt"); // System.IO.File
#>
The file contains: <#= fileContent #>

Standard Imports
The following namespace is imported automatically, so that you do not need to write an import directive for it:
System

In addition, if you use a custom directive, the directive processor might import some namespaces
automatically.
For example, if you write templates for a domain-specific language (DSL), you do not need to write import
directives for the following namespaces:
Microsoft.VisualStudio.Modeling

Your DSL's namespace

See Also
T4 Assembly Directive
T4 Include Directive
10/18/2017 3 min to read Edit Online

In a text template in Visual Studio, you can include text from another file by using an <#@include#> directive. You
can place include directives anywhere in a text template before the first class feature block <#+ ... #> . The
included files can also contain include directives, and other directives. This allows you to share template code and
boilerplate text between templates.

Using Include Directives


<#@ include file="filePath" [once="true"] #>

filePath can be absolute, or relative to the current template file.


In addition, specific Visual Studio extensions can specify their own directories to search for include files. For
example, when you have installed the Visualization and Modeling SDK (DSL Tools), the following folder is
added to the include list:
Program Files\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\DSL SDK\DSL
Designer\11.0\TextTemplates
.
These additional include folders might depend on the file extension of the including file. For example, the
DSL Tools include folder is only accessible to including files that have the file extension .tt
filePath can include environment variables delimited with "%". For example:

<#@ include file="%HOMEPATH%\MyIncludeFile.t4" #>

The name of an included file does not have to use the extension ".tt" .
You might want to use another extension such as ".t4" for included files. This is because, when you add a
.tt file to a project, Visual Studio automatically sets its Custom Tool property to
TextTemplatingFileGenerator . You do not usually want included files to be transformed individually.

On the other hand, you should be aware that in some cases, the file extension affects which additional
folders will be searched for include files. This might be important when you have an included file that
includes other files.
The included content is processed almost as if it were part of the including text template. However, you can
include a file that contains a class feature block <#+...#> even if the include directive is followed by
ordinary text and standard control blocks.
Use once="true" to ensure that a template is included only once, even if it's invoked from more than one
other include file.
This feature makes it easy to build up a library of reusable T4 snippets that you can include at will without
worrying that some other snippet has already included them. For example, suppose you have a library of
very fine-grained snippets that deal with template processing and C# generation. In turn, these are used by
some more task-specific utilities such as generating exceptions, which you can then use from any more
application-specific template. If you draw the dependency graph, you see that some snippets would be
included several times. But the once parameter prevents the subsequent inclusions.
MyTextTemplate.tt:

<#@ output extension=".txt" #>


Output message 1 (from top template).
<#@ include file="TextFile1.t4"#>
Output message 5 (from top template).
<#
GenerateMessage(6); // defined in TextFile1.t4
AnotherGenerateMessage(7); // defined in TextFile2.t4
#>

TextFile1.t4:

Output Message 2 (from included file).


<#@include file="TextFile2.t4" #>
Output Message 4 (from included file).
<#+ // Start of class feature control block.
void GenerateMessage(int n)
{
#>
Output Message <#= n #> (from GenerateMessage method).
<#+
}
#>

TextFile2.t4:

Output Message 3 (from included file 2).


<#+ // Start of class feature control block.
void AnotherGenerateMessage(int n)
{
#>
Output Message <#= n #> (from AnotherGenerateMessage method).
<#+
}
#>

The resulting generated file, MyTextTemplate.txt:

Output message 1 (from top template).


Output Message 2 (from included file).
Output Message 3 (from included file 2).

Output Message 4 (from included file).

Output message 5 (from top template).


Output Message 6 (from GenerateMessage method).
Output Message 7 (from AnotherGenerateMessage method).

Using project properties in MSBuild and Visual Studio


Although you can use Visual Studio macros like $(SolutionDir) in an include directive, they don't work in MSBuild.
If you want to transform templates in your build machine, you have to use project properties instead.
Edit your .csproj or .vbproj file to define a project property. This example defines a property named
myIncludeFolder :
<!-- Define a project property, myIncludeFolder: -->
<PropertyGroup>
<myIncludeFolder>$(MSBuildProjectDirectory)\..\libs</myIncludeFolder>
</PropertyGroup>

<!-- Tell the MSBuild T4 task to make the property available: -->
<ItemGroup>
<T4ParameterValues Include="myIncludeFolder">
<Value>$(myIncludeFolder)</Value>
</T4ParameterValues>
</ItemGroup>

Now you can use your project property in text templates, which transform correctly in both Visual Studio and
MSBuild:

<#@ include file="$(myIncludeFolder)\defs.tt" #>


T4 CleanUpBehavior directive
10/18/2017 1 min to read Edit Online

To delete the appDomain after processing a text template, include the following line:

<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

Text templates are processed in an appDomain that is separate from the host process. In most cases, when one text
template has been processed, the appdomain is used again to process the next template. But if you specify
CleanupBehavior, the appDomain is deleted and the next template will be processed in a new appDomain.
This slows text processing, but can be useful to make sure that resources are disposed.
This directive works only in the Visual Studio host.
Text Template Control Blocks
10/18/2017 4 min to read Edit Online

Control blocks let you write code in your text template in order to vary the output. There are three kinds of control
blocks, which are distinguished by their opening brackets:
<# Standard control blocks #> can contain statements.
<#= Expression control blocks #> can contain expressions.
<#+ Class feature control blocks #> can contain methods, fields and properties.

Standard control block


Standard control blocks contain statements. For example, the following standard block gets the names of all the
attributes in the XML document:

<#@ assembly name="System.Xml.dll" #>


<#@ import namespace="System.Xml" #>

<#
List<string> allAttributes = new List<string>();
XmlDocument xDoc = new XmlDocument();
xDoc.Load(@"E:\CSharp\Overview.xml");
XmlAttributeCollection attributes = xDoc.Attributes;
if (attributes.Count > 0)
{
foreach (XmlAttribute attr in attributes)
{
allAtributes.Add(attr.Name);
}
}
#>

You can embed plain text inside a compound statement such as if or for . For example, this fragment generates
an output line in each loop iteration:

<#
foreach (XmlAttribute attr in attributes)
{
#>
Found another one!
<#
allAtributes.Add(attr.Name);
}
#>

WARNING
Always use {...} to delimit nested statements that contain embedded plain text. The following example might not work
properly:
<# if (ShouldPrint) #> Some text. -- WRONG

Instead, you should include {braces}, as follows:


<#
if (ShouldPrint)
{ // "{" REQUIRED
#>
Some text.
<#
}
#>

Expression control block


Expression control blocks are used for code that provides strings to be written to the output file. For example, with
the example above, you can print the names of the attributes to the output file by modifying the code block as
follows:

<#
XmlDocument xDoc = new XmlDocument();
xDoc.Load(@"E:\CSharp\Overview.xml");
XmlAttributeCollection attributes = xDoc.Attributes;
if (attributes != null)
{
foreach (XmlAttribute attr in attributes)
{
#><#= attr.Name #><#
}
}
#>

Class feature control block


You can use class feature control blocks to add methods, properties, fields, or even nested classes to your text
template. The most common use of class feature blocks is to provide helper functions for code in other parts of the
text template. For example, the following class feature block capitalizes the first letter of the attribute name (or, if
the name contains whitespace, it capitalizes the first letter of every word):

<#@ import namespace="System.Globalization" #>

<#+
private string FixAttributeName(string name)
{
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name);
}
#>

NOTE
A class feature control block must not be followed by standard control blocks in the same template file. However, this
restriction does not apply to the result of using <#@include#> directives. Each included file can have standard blocks
followed by class feature blocks.

You can create a function that generates output by embedding text and expression blocks inside a class feature
control block. For example:
<#+
private void OutputFixedAttributeName(string name)
{
#>
Attribute: <#= CultureInfo.CurrentCulture.TextInfo.ToTitleCase(name) #>
<#+ // <<< Notice that this is also a class feature block.
}
#>

You could call this function from a standard block or from another class feature block:

<# foreach (Attribute attribute in item.Attributes)


{
OutputFixedAttributeName(attribute.Name);
}
#>

How to use control blocks


All the code in all the standard and expression control blocks in a single template (including all the code in included
templates) is combined to form the TransformText() method of the generated code. (For more information about
including other text templates with the include directive, see T4 Text Template Directives.)
You should keep in mind the following considerations when you use control blocks:
Language. You can use either C# or Visual Basic code in a text template. The default language is C#, but you
can specify Visual Basic with the language parameter of the template directive. (For more information
about the template directive, see T4 Text Template Directives.)
The language you use in control blocks has nothing to do with the language or format of the text you
generate in a text template. You can generate C# by using Visual Basic code or vice versa.
You can use only one language in a given text template, including all the text templates you include with the
include directive.

Local variables. Since all the code in the standard and expression control blocks in a text template is
generated as a single method, you should make certain that there are no conflicts with the names of local
variables. If you are including other text templates, you must make sure that variable names are unique
across all included templates. One way to ensure this is to add a string to each local variable name
identifying the text template in which it was declared.
It is also a good idea to initialize your local variables to sensible values when you declare them, particularly
when you are including multiple text templates.
Nesting of control blocks. Control blocks may not be nested inside each other. You must always terminate
a given control block before you open another one. For example, the following shows how to print some text
in an expression block as part of a standard control block.

<#
int x = 10;
while (x-- > 0)
{
#>
<#= x #>
<# } #>

Refactoring. In order to keep your text templates short and easy to understand, it is strongly recommended
that you avoid repetitive code either by factoring the reusable code into helper functions in class feature
blocks or by creating your own text template class that inherits from the
Microsoft.VisualStudio.TextTemplating.TextTransformation class.
Text Template Utility Methods
10/18/2017 2 min to read Edit Online

There are several methods that are always available to you when you write code in a Visual Studio text template.
These methods are defined in TextTransformation.

TIP
You can also use other methods and services provided by the host environment in a regular (not preprocessed) text
template. For example, you can resolve file paths, log errors, and get services provided by Visual Studio and any loaded
packages. For more information, see Accessing Visual Studio from a Text Template.

Write methods
You can use the Write() and WriteLine() methods to append text inside a standard code block, instead of using
an expression code block. The following two code blocks are functionally equivalent.
C o d e b l o c k w i t h a n e x p r e ssi o n b l o c k

<#
int i = 10;
while (i-- > 0)
{ #>
<#= i #>
<# }
#>

C o d e b l o c k u si n g W r i t e L i n e ()

<#
int i = 10;
while (i-- > 0)
{
WriteLine((i.ToString()));
}
#>

You may find it helpful to use one of these utility methods instead of an expression block inside a long code block
with nested control structures.
The Write() and WriteLine() methods have two overloads, one that takes a single string parameter and one that
takes a composite format string plus an array of objects to include in the string (like the Console.WriteLine()
method). The following two uses of WriteLine() are functionally equivalent:

<#
string msg = "Say: {0}, {1}, {2}";
string s1 = "hello";
string s2 = "goodbye";
string s3 = "farewell";

WriteLine(msg, s1, s2, s3);


WriteLine("Say: hello, goodbye, farewell");
#>
Indentation methods
You can use indentation methods to format the output of your text template. The TextTransformation class has a
CurrentIndent string property that shows the current indentation in the text template and an indentLengths field
that is a list of the indentations that have been added. You can add an indentation with the PushIndent() method
and subtract an indentation with the PopIndent() method. If you want to remove all indentations, use the
ClearIndent() method. The following code block shows the use of these methods:

<#
WriteLine(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
ClearIndent();
WriteLine(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
#>

This code block produces the following output:

Hello
Hello
Hello
Hello
Hello

Error and warning methods


You can use error and warning utility methods to add messages to the Visual Studio Error List. For example, the
following code will add an error message to the Error List.

<#
try
{
string str = null;
Write(str.Length.ToString());
}
catch (Exception e)
{
Error(e.Message);
}
#>

Access to Host and Service Provider


The property this.Host can provide access to properties exposed by the host that is executing the template. To use
this.Host , you must set hostspecific attribute in the <@template#> directive:

<#@template ... hostspecific="true" #>

The type of this.Host depends on the type of host in which the template is executing. In a template that is running
in Visual Studio, you can cast this.Host to IServiceProvider to gain access to services such as the IDE. For
example:
EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
.GetService(typeof(EnvDTE.DTE));

Using a different set of utility methods


As part of the text generation process, your template file is transformed into a class, which is always named
GeneratedTextTransformation and inherits from TextTransformation. If you want to use a different set of methods
instead, you can write your own class and specify it in the template directive. Your class must inherit from
TextTransformation.

<#@ template inherits="MyUtilityClass" #>

Use the assembly directive to reference the assembly where the compiled class can be found.
Accessing Visual Studio or other Hosts from a Text
Template
10/18/2017 1 min to read Edit Online

In a text template, you can use methods and properties exposed by the host that executes the template, such as
Visual Studio.
This applies to regular text templates, not preprocessed text templates.

Obtaining access to the host


Set hostspecific="true" in the template directive. This lets you use this.Host , which has type
ITextTemplatingEngineHost. This type has members that you can use, for example, to resolve file names and to log
errors.
Resolving File Names
To find the full path of a file relative to the text template, use this.Host.ResolvePath().

<#@ template hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ import namespace="System.IO" #>
<#
// Find a path within the same project as the text template:
string myFile = File.ReadAllText(this.Host.ResolvePath("MyFile.txt"));
#>
Content of myFile is:
<#= myFile #>

Displaying Error Messages


This example logs messages when you transform the template. If the host is Visual Studio, they are added to the
error window.

<#@ template hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#
string message = "test message";
this.Host.LogErrors(new CompilerErrorCollection()
{ new CompilerError(
this.Host.TemplateFile, // Identify the source of the error.
0, 0, "0", // Line, column, error ID.
message) }); // Message displayed in error window.
#>

Using the Visual Studio API


If you are executing a text template in Visual Studio, you can use this.Host to access services provided by Visual
Studio and any packages or extensions that are loaded.
Set hostspecific="true" and cast this.Host to IServiceProvider.
This example gets the Visual Studio API, DTE, as a service:
<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
#>
Number of projects in this solution: <#= dte.Solution.Projects.Count #>

Using hostSpecific with template inheritance


Specify hostspecific="trueFromBase" if you also use the inherits attribute, and if you inherit from a template that
specifies hostspecific="true" . This avoids a compiler warning to the effect that the property Host has been
declared twice.
Using Escape Sequences in Text Templates
10/18/2017 1 min to read Edit Online

You can use escape sequences in text templates to generate text template tags and (in C# code only) to escape
control characters and quotation marks.
To print open and close tags for a standard code block to the output file, escape the tags as follows:

\<# ... \#>

You can do the same with other text template directive and code block tags.
If a text block includes strings used to escape text template tags, then you may use the following escape sequences:
If a text template tag is preceded by an even number of escape (\) characters the template parser will include
half of the escape characters and include the sequence as a text template tag. For example, if there are four
escape characters in the text template, there will be two "\" characters in the generated file.
If the text template tag is preceded by an odd number of escape (\) characters, the template parser will
include half of the "\" characters plus the tag itself (<# or #>). The tag is not considered to be a text template
tag.
If an escape (\) character appears anywhere else in any sequence other than where it escapes a control
character or a quote (in C# only), the character will be output directly.

See Also
How to: Generate Templates from Templates By Using Escape Sequences
How to: Generate Templates from Templates By Using
Escape Sequences
10/18/2017 1 min to read Edit Online

You can create a text template that creates another text template as its generated text output. To do this, you must
use escape sequences to delineate the text template tags. If you do not use escape sequences, your generated text
template will have a pre-defined meaning. For more information about using escape sequences in text templates,
see Using Escape Sequences in Text Templates.
To generate a text template from within a text template
Use the backslash (\) as an escape character to produce the necessary markup tags within the text template
for directives, statements, expressions, and class features in a separate text template file.

\<#@ directive \#>


\<# statement \#>
\<#= expression \#>
\<#+ classfeature \#>

Example
The following example uses escape characters to produce a text template from a text template. The output
directive sets the destination file type to the text template file type (.tt).

\<#@ output extension=".tt" \#>


\<#@ assembly name="System.Xml.dll" \#>
\<#@ import namespace="System.Xml" \#>
\<#
XmlDocument xDoc = new XmlDocument();
//System.Diagnostics.Debugger.Break();
xDoc.Load(@"E:\CSharp\Overview.xml");
XmlAttributeCollection attributes = xDoc.Attributes;
if (attributes != null)
{
foreach (XmlAttribute attr in attributes)
{\#>
\<#= attr.Name \#>
\<#}
}
\#>

The generated text output is a text template.


<#@ output extension=".tt" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#
XmlDocument xDoc = new XmlDocument();
//System.Diagnostics.Debugger.Break();
xDoc.Load(@"E:\CSharp\Overview.xml");
XmlAttributeCollection attributes = xDoc.Attributes;
if (attributes != null)
{
foreach (XmlAttribute attr in attributes)
{#>
<#= attr.Name #>
<#}
}
#>
Debugging a T4 Text Template
10/18/2017 11 min to read Edit Online

You can set breakpoints in text templates. To debug a design-time text template, save the text template file, and
then choose Debug T4 Template on the shortcut menu of the file in Solution Explorer. To debug a run-time text
template, simply debug the application to which it belongs.
To debug a text template, you should understand the steps of the template transformation process. Different kinds
of errors can occur within each step. The steps are as follows.

DESIGN-TIME TEMPLATE: WHEN IT


STEP HAPPENS RUN-TIME TEMPLATE: WHEN IT HAPPENS

Code is generated from the text When you save the template or invoke When you save the template or invoke
template. text transformation. text transformation.

Errors in directives, or mismatched or


disordered <#...#> tags.

Generated code is compiled. Immediately after the previous step. Along with your application code.

Compilation errors in your template


code.

Code runs. Immediately after the previous step. When your application runs and
invokes the template code.
Run-time errors in your template code.

In most cases, line numbers in the template code are given in the error report. When the error report refers to a
temporary filename, the usual cause is a mismatched bracket in the code of the text template.
You can set breakpoints in text templates and debug in the usual way.

Common Errors and Fixes


The following table lists the most common errors and their fixes.

ERROR MESSAGE DESCRIPTION SOLUTION

Failed to load base class '{0}' from which Occurs if you cannot find the base class Be sure the specified class exists, and
Transformation class inherits. specified in the inherits parameter that the assembly that it exists in is
in a template directive. The message specified in an assembly directive.
provides the line number of the
template directive.

Failed to resolve include text for file:{0} Occurs when you cannot find an Be sure that the file path is relative to
included template. The message the original template path, or that the
provides the name of the requested file is in a location that is registered with
include file. the host, or that there is a full path to
the file.
ERROR MESSAGE DESCRIPTION SOLUTION

Errors were generated when initializing Occurs when the 'Initialize()' of the The code in the Initialize() function
the transformation object. The transformation class failed or returned comes from the base transformation
transformation will not be run. false. class specified in the <#@template#>
directive and from directive processors.
The error that caused initialize to fail
probably is on the error list. Investigate
why it failed. You can look at the actual
generated code for Initialize() by
following the procedures to debug a
template.

The assembly '{0}' for directive Occurs when the system does not Be sure that you only use trusted
processor '{1}' was not granted the grant FullTrust permissions to an assemblies on the local machine.
FullTrust permission set. Only trusted assembly containing a directive
assemblies are allowed to provide processor. The message provides the
directive processors. This directive name of the assembly and the name of
processor will not be loaded. the directive processor.

The path '{0}' must be either local to Occurs when a directive or assembly Be sure that the directory where the
this computer or part of your trusted directive references a file that is not on directive or assembly directives are
zone. your local machine or on your located is in your trusted zone. You can
network's trusted zone. add a network directory to your trusted
zone through Internet Explorer.

Multiple syntax errors such as "Invalid Too many closing braces in your Check the number of closing braces and
token 'catch'" or "A namespace cannot template code. The compiler is brackets inside code delimiters.
directly contain members" confusing it with the standard
generation code.

Loops or conditionals not compiled or In C#, always use braces to surround Add braces:
executed correctly. For example: text blocks that are embedded in <#if (i>10) { #> Number is: <#= i
<#if (i>10)#> Number is: <#= i #> control statements. #><# } #>
. .

This code always outputs the value of i.


Only "Number is:" is conditional.

"Expression too complex" when Text block is too long. T4 converts text Break up the long text block with an
processing a design-time template or blocks to a string concatenation expression block such as:
compiling a runtime (preprocessed) expression, with one string literal for
template. each template line. Very long text <#= "" #>
blocks can overstep the compiler's size
Visual Studio stops working when limits.
attempting to inspect code generated
by a runtime template.

Warning Descriptions and Fixes


The following table lists the most common warnings together with fixes, if available.

WARNING MESSAGE DESCRIPTION SOLUTION

Loading the include file '{0}' returned a Occurs if an included text template file Either remove the include directive or
null or empty string. is blank. The message provides the file be sure the file has some content.
name of the included file.
WARNING MESSAGE DESCRIPTION SOLUTION

Compiling transformation: Prepends this string to all errors or If you have a problem finding the DLL,
warnings originating from the compiler you may need to provide either the full
when it compiles the transformation. path or a fully qualified strong name if
This string means that the compiler the DLL is in the GAC.
threw an error or warning.

The parameter '{0}' already exists in the Occurs when a parameter is specified Remove the duplicate parameter
directive. The duplicate parameter will more than once in a directive. The specification.
be ignored. message provides the name of the
parameter and the line number of the
directive.

There was an error loading the include Occurs when you cannot find a file Be sure the include file exists either in
file '{0}'. The include directive will be specified in an include directive. The the same directory as the original text
ignored. message provides the name of the file template file or in one of the include
and the line number of the directive. directories that are registered with the
host.

An invalid base class was specified for Occurs when the inherits parameter Specify a class that derives from
the Transformation class. The base class in a template directive specifies a class TextTransformation .
must derive from that does not inherit from
Microsoft.VisualStudio.TextTemplating.T TextTransformation . The message
extTransformation. provides the line number of the
template directive.

An invalid culture was specified in the Occurs when the culture parameter in a Change the culture parameter to a valid
'template' directive. The culture must be template directive is specified culture in the "xx-XX" format.
in the "xx-XX" format. The invariant incorrectly. The message provides the
culture will be used. line number of the template directive.

An invalid debug value '{0}' was Occurs when the debug parameter in Set the debug parameter to "true" or
specified in the template directive. The a template directive is specified "false".
debug value must be either "true" or incorrectly. The message provides the
"false". The default of "false" will be line number of the template directive.
used.

An invalid HostSpecific value '{0}' was Occurs when the host-specific Set the host-specific parameter to
specified in the template directive. The parameter in a template directive is "true" or "false".
HostSpecific value must be either "true" specified incorrectly. The message
or "false". The default of "false" will be provides the line number of the
used. template directive.

An invalid language '{0}' was specified in Occurs when an unsupported language Set the language parameter in the
the 'template' directive. The language is specified in the template directive. template directive to "C#" or"VB".
must be either "C#" or "VB". The default Only "C#" or "VB" are allowed (case
value of "C#" will be used. insensitive). The message provides the
line number of the template directive.

Multiple output directives were found Occurs when multiple output Remove duplicate output directives.
in the template. All but the first one will directives are specified in a template file.
be ignored. The message provides the line number
of the duplicate output directive.
WARNING MESSAGE DESCRIPTION SOLUTION

Multiple template directives were found Occurs if you specify multiple Aggregate the different template
in the template. All but the first one will template directives within a text directives into one template directive.
be ignored. Multiple parameters to the template file (including included files).
template directive should be specified The message provides the line number
within one template directive. of the duplicate template directive.

No processor was specified for a Occurs if you specify a custom Provide a processor attribute with
directive named '{0}'. The directive will directive, but do not provide a the name of the directive processor
be ignored. processor attribute. The message for the directive.
provides the name of the directive and
the line number.

A processor named '{0}' could not be Occurs when the system cannot find Set the processor attribute in the
found for the directive named '{1}'. The the directive processor you directive to the name of the directive
directive will be ignored. specified within a custom directive. processor.
The message provides the directive
name, processor name, and the line
number of the directive.

A required parameter '{0}' for the Occurs when the system does not Provide the missing parameter.
directive '{1}' was not found. The provide a required directive parameter.
directive will be ignored. The message provides the name of the
missing parameter, the directive name,
and the line number.

The processor named '{0}' does not Occurs when a directive processor does Correct the name of the directive.
support the directive named '{1}'. The not support a directive. The message
directive will be ignored. provides the name and line number of
the offending directive along with the
name of the directive processor.

The include directive for file '{0}' causes Displayed if circular include directives Do not specify circular include
an infinite loop. are specified (for example, file A includes directives.
file B, which includes file A).

Running transformation: Prepends this string to all errors or Not applicable.


warnings that are generated while
running the transform.

An unexpected start or end tag was Displayed when you have an Either remove the mismatched start or
found within a block. Make sure that unexpected <# or #>. That is, if you end tag, or use an escape character.
you did not mis-type a start or end tag, have a <# after another open tag that
and that you don't have any nested has not been closed, or you have a #>
blocks in the template. when there is no unclosed open tag
before it. The message provides the line
number of the mismatched tag.

A directive was specified in the wrong Displayed by the parser if a directive is Be sure all directives are in the form
format. The directive will be ignored. not specified in the correct format. The <#@ name
Please specify the directive in the message provides the line number of [parametername="parametervalue"]*
#>
format the incorrect directive.
<#@ name
. For more information, see T4 Text
[parametername="parametervalue"]* Template Directives.
#>
WARNING MESSAGE DESCRIPTION SOLUTION

Failed to load Assembly '{0}' for Occurs when a directive processor Be sure the directive processor is
registered directive processor '{1}' could not be loaded by the host. The registered correctly and that the
message identifies the assembly assembly exists.
{2} provided for the directive processor and
the name of the directive processor.

Failed to find type '{0}' in Assembly '{1}' Occurs when a directive processor type The vshost finds directive processor
for registered directive processor '{2}' could not be loaded from its assembly. information (name, assembly, and type)
The message provides the name of the in the registry. Be sure the directive
{3} type, assembly, and directive processor. processor is registered correctly, and
that the type exists in the assembly.

There was a problem loading the Occurs when there is a problem loading You can specify assemblies to be loaded
assembly '{0}' an assembly. The message provides the in <@#assembly#> directives, and by
name of the assembly. directive processors. The error message
that follows this string should provide
more data on why the assembly load
failed.

There was a problem creating and Occurs when the system could not Be sure you use the correct directive
initializing the processor for a directive create or initialize a directive processor. processor, and that the directive
named '{1}'. The type of the processor is The message provides the name and processor has a public default
{0}. The directive will be ignored. line number of the directive and the constructor. Otherwise, use the debug
type of the processor. options to find out why the Initialize()
method of the directive processor is
failing. For more information, see
Troubleshooting Text Templates.

An Exception was thrown while Occurs when a directive processor Be sure that the parameters to the
processing a directive named '{0}'. throws an exception when processing a directive processor are correct.
directive.

The host threw an exception while Occurs when the host throws an Assembly references come from
trying to resolve the assembly reference exception when it tries to resolve an <@#assembly#> directives and from
'{0}'. assembly reference. The message directive processors. Be sure that the
provides the assembly reference string. 'name' parameter provided in the
assembly parameter is correct.

Attempt to specify unsupported {1} Occurs by the Be sure that the names in the
value '{0}' for directive {2} RequiresProvidesDirectiveProcessor (all name='value' pairs provided in the
our generated directive processors requires and provides parameters are
derive from it), when you supply an correct.
unsupported requires or provides
argument.
Guidelines for Writing T4 Text Templates
10/18/2017 7 min to read Edit Online

These general guidelines might be helpful if you are generating program code or other application resources in
Visual Studio. They are not fixed rules.

Guidelines for Design-Time T4 Templates


Design-time T4 templates are templates that generate code in your Visual Studio project at design time. For more
information, see Design-Time Code Generation by using T4 Text Templates.
Generate variable aspects of the application.
Code generation is most useful for those aspects of the application that might change during the project, or will
change between different versions of the application. Separate these variable aspects from the more invariant
aspects so that you can more easily determine what has to be generated. For example, if your application provides
a Web site, separate the standard page serving functions from the logic that defines the navigation paths from one
page to another.
Encode the variable aspects in one or more source models.
A model is a file or database that each template reads to obtain specific values for variable parts of the code that is
to be generated. Models can be databases, XML files of your own design, diagrams, or domain-specific languages.
Typically, one model is used to generate many files in a Visual Studio project. Each file is generated from a
separate template.
You can use more than one model in a project. For example, you might define a model for navigation between
Web pages, and a separate model for the layout of the pages.
Focus the model on the users' needs and vocabulary, not on your implementation.
For example, in a Web site application, you would expect the model to refer to Web pages and hyperlinks.
Ideally, choose a form of presentation that suits the kind of information that the model represents. For example, a
model of navigation paths through a Web site could be a diagram of boxes and arrows.
Test the generated code.
Use manual or automated tests to verify that the resulting code works as the users require. Avoid generating tests
from the same model from which the code is generated.
In some cases, general tests can be performed on the model directly. For example, you could write a test that
ensures that every page in the Web site can be reached by navigation from any other.
Allow for custom code: generate partial classes.
Allow for code that you write by hand in addition to the generated code. It is unusual for a code generation
scheme to be able to account for all possible variations that might arise. Therefore, you should expect to add to or
override some of the generated code. Where the generated material is in a .NET language such as Visual C# or
Visual Basic, two strategies are especially useful:
The generated classes should be partial. This lets you to add content to the generated code.
Classes should be generated in pairs, one inheriting from the other. The base class should contain all the
generated methods and properties, and the derived class should contain only the constructors. This allows
your hand-written code to override any of the generated methods.
In other generated languages such as XML, use the <#@include#> directive to make simple combinations of
hand-written and generated content. In more complex cases, you might have to write a post-processing
step that combines the generated file with any hand-written files.
Move common material into include files or run-time templates
To avoid repeating similar blocks of text and code in multiple templates, use the <#@ include #> directive.
For more information, see T4 Include Directive.
You can also build run-time text templates in a separate project, and then call them from the design-time
template. To do this, use the <#@ assembly #> directive to access the separate project. For examples, see
"Inheritance in Text Templates" in Gareth Jones' Blog.
Consider moving large blocks of code into a separate assembly.
If you have large code blocks and class feature blocks, it might be useful to move some of this code into
methods that you compile in a separate project. You can use the <#@ assembly #> directive to access the
code in the template. For more information, see T4 Assembly Directive.
You can put the methods in an abstract class that the template can inherit. The abstract class must inherit
from Microsoft.VisualStudio.TextTemplating.TextTransformation. For more information, see T4 Template
Directive.
Generate code, not configuration files
One method of writing a variable application is to write generic program code that accepts a configuration
file. An application written in this manner is very flexible, and can be reconfigured when the business
requirements change, without rebuilding the application. However, a drawback of this approach is that the
application will perform less well than a more specific application. Also, its program code will be more
difficult to read and maintain, partly because it has always to deal with the most generic types.
By contrast, an application whose variable parts are generated before compilation can be strongly typed.
This makes it much easier and more reliable to write hand-written code and integrate it with the generated
parts of the software.
To obtain the full benefit of code generation, try to generate program code instead of configuration files.
Use a Generated Code folder
Place the templates and the generated files in a project folder named Generated Code, to make it clear that
these are not files that should be edited directly. If you create custom code to override or add to the
generated classes, place those classes in a folder that is named Custom Code. The structure of a typical
project looks like this:

MyProject
Custom Code
Class1.cs
Class2.cs
Generated Code
Class1.tt
Class1.cs
Class2.tt
Class2.cs
AnotherClass.cs

Guidelines for Run-Time (Preprocessed) T4 Templates


Move common material into inherited templates
You can use inheritance to share methods and text blocks between T4 text templates. For more information, see T4
Template Directive.
You can also use include files that have run-time templates.
Move large bodies of code into a partial class.
Each run-time template generates a partial class definition that has the same name as the template. You can write
a code file that contains another partial definition of the same class. You can add methods, fields, and constructors
to the class in this manner. These members can be called from the code blocks in the template.
An advantage of doing this is that the code is easier to write, because IntelliSense is available. Also, you can
achieve a better separation between the presentation and the underlying logic.
For example, in MyReportText.tt:
The total is: <#= ComputeTotal() #>

In MyReportText-Methods.cs:
private string ComputeTotal() { ... }

Allow for custom code: provide extension points


Consider generating virtual methods in <#+ class feature blocks #>. This allows a single template to be used in
many contexts without modification. Instead of modifying the template, you can construct a derived class which
supplies the minimum additional logic. The derived class can be either regular code, or it can be a run-time
template.
For example, in MyStandardRunTimeTemplate.tt:

This page is copyright <#= CompanyName() #>.


<#+ protected virtual string CompanyName() { return ""; } #>

In the code of an application:

class FabrikamTemplate : MyStandardRunTimeTemplate


{
protected override string CompanyName() { return "Fabrikam"; }
}
...
string PageToDisplay = new FabrikamTemplate().TextTransform();

Guidelines for All T4 Templates


Separate data-gathering from text generation
Try to avoid mixing computation and text blocks. In each text template, use the first <# code block #> to set
variables and perform complex computations. From the first text block down to the end of the template or the first
<#+ class feature block #>, avoid long expressions, and avoid loops and conditionals unless they contain text
blocks. This practice makes the template easier to read and maintain.
Don't use .tt for include files
Use a different file name extension such as .ttinclude for include files. Use .tt only for files that you want to be
processed either as run-time or design-time text templates. In some cases, Visual Studio recognizes .tt files and
automatically sets their properties for processing.
Start each template as a fixed prototype.
Write an example of the code or text that you want to generate, and make sure that it is correct. Then change its
extension to .tt and incrementally insert code that modifies the content by reading the model.
Consider using typed models.
Although you can create an XML or database schema for your models, it might be useful to create a domain
specific language (DSL). A DSL has the advantage that it generates a class to represent each node in the schema,
and properties to represent the attributes. This means that you can program in terms of the business model. For
example:

Team Members:
<# foreach (Person p in team.Members)
{ #>
<#= p.Name #>
<# } #>

Consider using diagrams for your models.


Many models are most effectively presented and managed simply as text tables, especially if they are very large.
However, for some kinds of business requirements, it is important to clarify complex sets of relationships and
work flows, and diagrams are the best suited medium. An advantage of a diagram is that it is easy to discuss with
users and other stakeholders. By generating code from a model at the level of business requirements, you make
your code more flexible when the requirements change.
You can also design your own type of diagram as a domain-specific language (DSL). Code can be generated from
both UML and DSLs. For more information, see Analyzing and Modeling Architecture.

See Also
Design-Time Code Generation by using T4 Text Templates
Run-Time Text Generation with T4 Text Templates
How to ... with Text Templates
10/18/2017 5 min to read Edit Online

Text templates in Visual Studio provide a useful way of generating text of any kind. You can use text templates to
generate text at run time as part of your application and at design time to generate some of your project code. This
topic summarizes the most frequently asked "How do I ...?" questions.
In this topic, multiple answers that are preceded by bullets are alternative suggestions.
For a general introduction to text templates, read Code Generation and T4 Text Templates.

How to ...
Generate part of my application code
I have a configuration or model in a file or a database. One or more parts of my code depend on that model.
Generate some of your code files from text templates. For more information, see Design-Time Code Generation
by using T4 Text Templates and What is the best way to start writing a template?.
Generate files at run time, passing data into the template
At run time, my application generates text files, such as reports, that contain a mixture of standard text and data. I
want to avoid writing hundreds of write statements.
Add a runtime text template to your project. This template creates a class in your code, which you can
instantiate and use to generate text. You can pass data to it in the constructor parameters. For more
information, see Run-Time Text Generation with T4 Text Templates.
If you want to generate from templates that are available only at run time, you can use standard text
templates. If you are writing a Visual Studio extension, you can invoke the text templating service. For more
information, see Invoking Text Transformation in a VS Extension. In other contexts, you can use the text
templating engine. For more information, see Microsoft.VisualStudio.TextTemplating.Engine.
Use the <#@parameter#> directive to pass parameters to these templates. For more information, see T4
Parameter Directive.
Read another project file from a template
To read a file from the same Visual Studio project as the template:
Insert hostSpecific="true" into the <#@template#> directive.
In your code, use this.Host.ResolvePath(filename) to obtain the full path of the file.
Invoke methods from a template
If the methods already exist, for example, in standard .NET Framework classes:
Use the <#@assembly#> directive to load the assembly, and use <#@import#> to set the namespace
context. For more information, see T4 Import Directive.
If you frequently use the same set of assembly and import directives, consider writing a directive processor.
In each template, you can invoke the directive processor, which can load the assemblies and the model files
and set the namespace context. For more information, see Creating Custom T4 Text Template Directive
Processors.
If you are writing the methods yourself:
If you are writing a runtime text template, write a partial class definition that has the same name as your
runtime text template. Add the additional methods into this class.
Write a class feature control block <#+ ... #> in which you can declare methods, properties, and private
classes. When the text template is compiled, it is transformed to a class. The standard control blocks <#...#>
and text are transformed to a single method, and class feature blocks are inserted as separate members. For
more information, see Text Template Control Blocks.
Methods defined as class features can also include embedded text blocks.
Consider placing class features in a separate file which you can <#@include#> into one or more template
files.
Write the methods in a separate assembly (class library) and call them from your template. Use the
<#@assembly#> directive to load the assembly, and <#@import#> to set the namespace context. Note that in
order to rebuild the assembly while you are debugging it, you might have to stop and restart Visual Studio.
For more information, see T4 Text Template Directives.
Generate many files from one model schema
If you often generate files from models that have the same XML or database schema:
Consider writing a directive processor. This enables you to replace multiple assembly statements and import
statements in each template with a single custom directive. The directive processor can also load and parse the
model file. For more information, see Creating Custom T4 Text Template Directive Processors.
Generate files from a complex model
Consider creating a domain-specific language (DSL) to represent the model. This makes it much easier to
write the templates, because you use types and properties that reflect the names of the elements in your
model. You do not have to parse the file or navigate XML nodes. For example:
foreach (Book book in this.Library) { ... }

For more information, see Getting Started with Domain-Specific Languages and Generating Code from a
Domain-Specific Language.
Get data from Visual Studio
To use services provided in Visual Studio, by set the hostSpecific attribute and load the EnvDTE assembly. For
example:

<#@ template hostspecific="true" language="C#" #>


<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetService(typeof(EnvDTE.DTE));
#>

Number of projects in this VS solution: <#= dte.Solution.Projects.Count #>

Execute text templates in the build process


For more information, see Code Generation in a Build Process.

More general questions


What is the best way to start writing a text template?
1. Write a specific example of the generated file.
2. Turn it into a text template by inserting the <#@template #> directive, and the directives and code that are
required to load the input file or model.
3. Progressively replace parts of the file with expression and code blocks.
What is a "model"?
The input read by your template. It could be in a file or in a database. It might be XML, or a Visio drawing, or
a domain-specific language (DSL), or a UML model, or it could be plain text. It could be spread across several
files. Typically more than one template reads one model.
The implication of the term "model" is that it represents some aspect of your business more directly than the
generated program code or other files. For example, it might represent the plan of a communications
network that your generated software will supervise.
What is the benefit of using text templates?
Typically, you generate multiple code or other files from one model. The model represents the requirements more
directly than the generated code. It omits implementation detail and is written in terms of the requirements, rather
than the code. When the requirements change - as they usually do - you can update the model more easily and
more reliably than the different parts of the program code.
Code generation is therefore a valuable tool from the perspective of agile development methods.
What "best practices" are there for text templates?
For more information, see Guidelines for Writing T4 Text Templates.
What is "T4"?
Another name for the Visual Studio text template capabilities described here. The previous version, which was
not published, was an abbreviation for "Text Template Transformation".
Customizing T4 Text Transformation
10/18/2017 1 min to read Edit Online

Text templates are a feature of Visual Studio that allow you to generate program code or other text files through a
transformation process. Using Visual Studio SDK, you can extend the default template transformation process by
customizing the text template directive processor or the text template host.

In This Section
The Text Template Transformation Process
Describes how text transformation works, and explains the role of the template host and the directive processors.
Creating Custom T4 Text Template Directive Processors
The directive processor deals with directives in your template, such as <#@template#>. It runs during the
compilation of the template, and can load assemblies and other resources. It can also insert code that will load
resources at runtime. By defining your own directive processor, you can reduce the complexity of your templates.
Invoking Text Transformation in a VS Extension
If you are writing a Visual Studio Extension such as a menu command or event handler, your extension can use
the Text Templating Service to transform any text template. You can pass parameter data into the template by
using the Session object, and get the values from within the template by using the <#@parameter#> directive.
Processing Text Templates by using a Custom Host
When the code of the text template executes, the host provides access to external files and the state of the
application. For example, the host that runs text transformations in Visual Studio can provide access to solution
explorer. It also displays errors in the error message window. If you want to run text transformations in a different
context, you can define your own host that provides access to the services available in that context.
If you are writing a Visual Studio Extension, consider using the existing text transformation service instead of
writing your own host. For more information, see Invoking Text Transformation in a VS Extension.

Reference
Writing a T4 Text Template
Provides the syntax of text template directives and control blocks.
The Text Template Transformation Process
10/18/2017 3 min to read Edit Online

The text template transformation process takes a text template file as the input and generates a new text file as the
output. For example, you can use text templates to generate Visual Basic or C# code, or you can generate an HTML
report.
Three components take part in this process: the engine, the host, and the directive processors. The engine controls
the process; it interacts with the host and the directive processor to produce the output file. The host provides any
interaction with the environment, such as locating files and assemblies. The directive processor adds functionality,
such as reading data from an XML file or a database.
The text template transformation process is performed in two steps. First, the engine creates a temporary class,
which is known as the generated transformation class. This class contains the code that is generated by the
directives and control blocks. After that, the engine compiles and executes the generated transformation class to
produce the output file.

Components
COMPONENT DESCRIPTION CUSTOMIZABLE (YES/NO)

Engine The engine component controls the No.


text template transformation process

Host The host is the interface between the Yes. You can write a custom host.
engine and the user environment.
Visual Studio is a host of the text
transformation process.

Directive Processors Directive processors are classes that Yes. You can write custom directive
handle directives in text templates. You processors
can use directives to provide data to a
text template from an input source.

The Engine
The engine receives the template as a string from the host, which handles all the files that are used in the
transformation process. The engine then asks the host to locate any custom directive processors and other aspects
of the environment. The engine then compiles and runs the generated transformation class. The engine returns the
generated text to the host, which normally saves the text to a file.

The Host
The host is responsible for anything that relates to the environment outside the transformation process, including
the following:
Locating text and binary files requested by the engine or a directive processor. The host can search
directories and the global assembly cache to locate assemblies. The host can locate custom directive
processor code for the engine. The host can also locate and read text files and return their contents as
strings.
Providing lists of standard assemblies and namespaces that are used by the engine to create the generated
transformation class.
Providing the application domain that is used when the engine compiles and executes the generated
transformation class. A separate application domain is used in order to protect the host application from
errors in the template code.
Writing the generated output file.
Setting the default extension for the generated output file.
Handling text template transformation errors. For example, the host can display the errors in the user
interface or write them to a file. (In Visual Studio, errors are displayed in the Error Message Window.)
Providing a required parameter value if a user has called a directive without providing a value. The directive
processor can specify the name of the directive and the parameter and ask the host to provide a default
value if it has one.

Directives and Directive Processors


A directive is a command in your text template. It provides parameters to the generation process. Typically
directives define the source and type of the model or other input, and the file name extension of the output file.
A directive processor can process one or more directives. When you transform a template, you must have installed
a directive processor that can deal with the directives in your template.
Directives work by adding code in the generated transformation class. You call directives from a text template, and
the engine processes all the directive calls when it creates the generated transformation class. After you call a
directive successfully, the rest of the code that you write in your text template can rely on the functionality that the
directive provides. For example, you can make the following call to the import directive in your template:
<#@ import namespace="System.Text" #>

The standard directive processor converts this to a using statement in the generated transformation class. You
can then use the StringBuilder class in the rest of your template code without qualifying it as
System.Text.StringBuilder .
Creating Custom T4 Text Template Directive
Processors
10/18/2017 3 min to read Edit Online

The text template transformation process takes a text template file as the input and produces a text file as the
output. The text template transformation engine controls the process, and the engine interacts with a text
template transformation host and one or more text template directive processors to complete the process. For
more information, see The Text Template Transformation Process.
To create a custom directive processor, you create a class that inherits from either DirectiveProcessor or
RequiresProvidesDirectiveProcessor.
The difference between these two is that DirectiveProcessor implements the minimum interface that is necessary
to get parameters from the user and to generate the code that produces the template output file.
RequiresProvidesDirectiveProcessor implements the requires/provides design pattern.
RequiresProvidesDirectiveProcessor handles two special parameters, requires and provides . For example, a
custom directive processor might accept a file name from the user, open and read the file, and then store the text
of the file in a variable that is named fileText . A subclass of the RequiresProvidesDirectiveProcessor class might
take a file name from the user as the value of the requires parameter, and the name of the variable in which to
store the text as the value of the provides parameter. This processor would open and read the file and then store
the text of the file in the specified variable.
Before you call a custom directive processor from a text template in Visual Studio, you must register it.
For more information about how to add the registry key, see Deploying a Custom Directive Processor.

Custom Directives
A custom directive looks like this:
<#@ MyDirective Processor="MyDirectiveProcessor" parameter1="value1" ... #>

You can use a custom directive processor when you want to access external data or resources from a text
template.
Different text templates can share the functionality that a single directive processor provides, so directive
processors provide a way to factor code for reuse. The built-in include directive is similar, because you can use it
to factor out code and share it among different text templates. The difference is that any functionality that the
include directive provides is fixed and does not accept parameters. If you want to provide common functionality
to a text template and allow the template to pass parameters, you must create a custom directive processor.
Some examples of custom directive processors could be:
A directive processor to return data from a database that accepts a user name and password as parameters.
A directive processor to open and read a file that accepts the name of the file as a parameter.
Principal parts of a custom directive processor
To develop a directive processor, you must create a class that inherits from either DirectiveProcessor or
RequiresProvidesDirectiveProcessor.
The most important DirectiveProcessor methods that you must implement are as follows.
bool IsDirectiveSupported(string directiveName) - Return true if your directive processor can deal with
the named directive.
void ProcessDirective (string directiveName, IDictionary<string, string> arguments) - The template
engine calls this method for each occurrence of a directive in the template. Your processor should save the
results.
After all calls to ProcessDirective() the templating engine will call these methods:
string[] GetReferencesForProcessingRun() - Return the names of assemblies that the template code
requires.
string[] GetImportsForProcessingRun() - Return the namespaces that can be used in the template code.
string GetClassCodeForProcessingRun() - Return the code of methods, properties, and other declarations
that the template code can use. The easiest way to do this is to build a string containing the C# or Visual
Basic code. To make your directive processor capable of being called from a template that uses any CLR
language, you can construct the statements as a CodeDom tree and then return the result of serializing the
tree in the language used by the template.
For more information, see Walkthrough: Creating a Custom Directive Processor.

In This Section
Deploying a Custom Directive Processor
Explains how to register a custom directive processor.
Walkthrough: Creating a Custom Directive Processor
Describes how to create a custom directive processor, how to register and test the directive processor, and how to
format the output file as HTML.
Deploying a Custom Directive Processor
10/18/2017 5 min to read Edit Online

To use a custom directive processor in Visual Studio on any computer, you must register it by one of the methods
described in this topic.
The alternative methods are:
Visual Studio Extension (VSIX). This provides a way to install and uninstall the directive processor both on
your own computer and on other computers. Typically, you might package other features in the same VSIX.
VSPackage. If you are defining a VSPackage that contains other features in addition to the directive
processor, there is a convenient method of registering the directive processor.
Set a registry key. In this method, you add a registry entry for the directive processor.
You need to use one of these methods only if you want to transform your text template in Visual Studio or
MSBuild. If you use a custom host in your own application, your custom host is responsible for finding the
directive processors for each directive.

Deploying a Directive Processor in a VSIX


You can add a custom directive processor to a Visual Studio Extension (VSIX).
You need to make sure that the following two items are contained in the .vsix file:
The assembly (.dll) that contains the custom directive processor class.
A .pkgdef file that registers the directive processor. The root name of the file must be the same as the
assembly. For example, your files could be named CDP.dll and CDP.pkgdef.
To inspect or change the content of a .vsix file, change its file name extension to .zip and then open it. After
editing the contents, change the filename back to .vsix.
There are several ways of creating a .vsix file. The following procedure describes one method.
To develop a custom directive processor in a VSIX project
1. Create a VSIX project in Visual Studio.
In the New Project dialog box, expand Visual Basic or Visual C#, then expand Extensibility. Click
VSIX Project.
2. In source.extension.vsixmanifest, set the content type and supported editions.
a. In the VSIX manifest editor, on the Assets tab, choose New and set the new item's properties:
Content Type = VSPackage
Source Project = <the current project>
b. Click Selected Editions and check the types of installation on which you want the directive
processor to be usable.
3. Add a .pkgdef file and set its properties to be included in the VSIX.
a. Create a text file and name it <assemblyName>.pkgdef.
<assemblyName> is usually the same as the name of the project.
b. Select it in Solution Explorer and set its properties as follows:
Build Action = Content
Copy to Output Directory = Copy Always
Include in VSIX = True
c. Set the name of the VSIX and make sure that the ID is unique.
4. Add the following text to the .pkgdef file.

[$RootKey$\TextTemplating]
[$RootKey$\TextTemplating\DirectiveProcessors]
[$RootKey$\TextTemplating\DirectiveProcessors\ CustomDirectiveProcessorName]
@="Custom Directive Processor description"
"Class"="NamespaceName.ClassName"
"CodeBase"="$PackageFolder$\AssemblyName.dll"

Replace the following names with your own names: CustomDirectiveProcessorName , NamespaceName ,
ClassName , AssemblyName .

5. Add the following references to the project:


Microsoft.VisualStudio.TextTemplating.*.0
Microsoft.VisualStudio.TextTemplating.Interfaces.*.0
Microsoft.VisualStudio.TextTemplating.VSHost.*.0
6. Add your custom directive processor class to the project.
This is a public class that should implement DirectiveProcessor or RequiresProvidesDirectiveProcessor.
To install the Custom Directive Processor
1. In Windows Explorer (File Explorer in Windows 8), open the build directory (usually bin\Debug or
bin\Release).
2. If you want to install the directive processor on another computer, copy the .vsix file to the other computer.
3. Double-click the .vsix file. The Visual Studio Extension Installer appears.
4. Restart Visual Studio. You will now be able to run text templates that contain directives that refer to the
custom directive processor. Each directive is of this form:
<#@ CustomDirective Processor="CustomDirectiveProcessorName" parameter1="value1" ... #>

To uninstall or temporarily disable the custom directive processor


1. In the Visual Studio Tools menu, click Extension Manager.
2. Select the VSIX that contains the directive processor, and then click Uninstall or Disable.
Troubleshooting a Directive Processor in a VSIX
If the directive processor does not work, the following suggestions might help:
The Processor name that you specify in the custom directive should match the
CustomDirectiveProcessorName that you specified in the .pkgdef file.

Your IsDirectiveSupported method must return true when it is passed the name of your CustomDirective .
If you cannot see the extension in Extension Manager, but the system will not allow you to install it, delete
the extension from %localappdata%\Microsoft\VisualStudio\*.0\Extensions\.
Open the .vsix file and inspect its contents. To open it, change the filename extension to .zip. Verify that it
contains the .dll, .pkgdef, and extension.vsixmanifest files. The extension.vsixmanifest file should contain the
appropriate list in the SupportedProducts node, and should also contain a VsPackage node under the
Content node:
<Content>

<VsPackage>CustomDirectiveProcessor.dll</VsPackage>

</Content>

Deploying a Directive Processor in a VSPackage


If your directive processor is part of a VSPackage that will be installed in the GAC, you can have the system
generate the .pkgdef file for you.
Place the following attribute on your package class:

[ProvideDirectiveProcessor(typeof(DirectiveProcessorClass), "DirectiveProcessorName", "Directive processor


description.")]

NOTE
This attribute is placed on the package class, not the directive processor class.

The .pkgdef file will be generated when you build the project. When you install the VSPackage, the .pkgdef file will
register the directive processor.
Verify that the .pkgdef file appears in the build folder, which is usually bin\Debug or bin\Release. If it does not
appear, open the .csproj file in a text editor, and remove the following node:
<GeneratePkgDefFile>false</GeneratePkgDefFile> .

For more information, see VSPackages.

Setting a Registry Key


This method of installing a custom directive processor is the least preferred. It does not provide a convenient way
enable and disable the directive processor, and does not provide a method of distributing the directive processor
to other users.
Cau t i on

Incorrectly editing the registry can severely damage your system. Before making changes to the registry, be sure to
back up any valued data on the computer.
To register a directive processor by setting a registry key
1. Run regedit .
2. In regedit, navigate to
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcess
ors
If you want to install the directive processor in the experimental version of Visual Studio, insert "Exp" after
"11.0".
3. Add a registry key that has the same name as the directive processor class.
In the registry tree, right-click the DirectiveProcessors node, point to New, and then click Key.
4. In the new node, add string values for Class and CodeBase or Assembly, according to the following tables.
a. Right-click the node that you created, point to New, and then click String Value.
b. Edit the name of the value.
c. Double-click the name and edit the data.
If the custom directive processor is not in the GAC, the registry subkeys should look like the following table:

NAME TYPE DATA

(Default) REG_SZ (value not set)

Class REG_SZ <Namespace Name>.<Class Name>

CodeBase REG_SZ <Your Path>\<Your Assembly


Name>

If the assembly is in the GAC, the registry subkeys should look like the following table:

NAME TYPE DATA

(Default) REG_SZ (value not set)

Class REG_SZ <Your Fully Qualified Class Name>

Assembly REG_SZ <Your Assembly Name in the GAC>

See Also
Creating Custom T4 Text Template Directive Processors
Walkthrough: Creating a Custom Directive Processor
10/18/2017 19 min to read Edit Online

Directive processors work by adding code to the generated transformation class. If you call a directive from a text
template, the rest of the code that you write in your text template can rely on the functionality that the directive
provides.
You can write your own custom directive processors. This enables you to customize your text templates. To create a
custom directive processor, you create a class that inherits from either DirectiveProcessor or
RequiresProvidesDirectiveProcessor.
Tasks that are illustrated in this walkthrough include the following:
Creating a custom directive processor
Registering the directive processor
Testing the directive processor

Prerequisites
To complete this walkthrough, you will need:
Visual Studio 2010
Visual Studio 2010 SDK

Creating a Custom Directive Processor


In this walkthrough, you create a custom directive processor. You add a custom directive that reads an XML file,
stores it in an XmlDocument variable, and exposes it through a property. In the section "Testing the Directive
Processor," you use this property in a text template to access the XML file.
The call to your custom directive looks like the following:
<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<Your Path>DocFile.xml" #>

The custom directive processor adds the variable and the property to the generated transformation class. The
directive that you write uses the System.CodeDom classes to create the code that the engine adds to the generated
transformation class. The System.CodeDom classes create code in either Visual C# or Visual Basic, depending on
the language specified in the language parameter of the template directive. The language of the directive
processor and the language of the text template that is accessing the directive processor do not have to match.
The code that the directive creates looks like the following:
private System.Xml.XmlDocument document0Value;

public virtual System.Xml.XmlDocument Document0


{
get
{
if ((this.document0Value == null))
{
this.document0Value = XmlReaderHelper.ReadXml(<FileNameParameterValue>);
}
return this.document0Value;
}
}

Private document0Value As System.Xml.XmlDocument

Public Overridable ReadOnly Property Document0() As System.Xml.XmlDocument


Get
If (Me.document0Value Is Nothing) Then
Me.document0Value = XmlReaderHelper.ReadXml(<FileNameParameterValue>)
End If
Return Me.document0Value
End Get
End Property

To create a custom directive processor


1. In Visual Studio, create a C# or a Visual Basic class library project named CustomDP.

NOTE
If you want to install the directive processor on more than one computer, it is better to use a Visual Studio Extension
(VSIX) project and include a .pkgdef file in the extension. For more information, see Deploying a Custom Directive
Processor.

2. Add references to these assemblies:


Microsoft.VisualStudio.TextTemplating.*.0
Microsoft.VisualStudio.TextTemplating.Interfaces.*.0
3. Replace the code in Class1 with the following code. This code defines a CustomDirectiveProcessor class that
inherits from the DirectiveProcessor class and implements the necessary methods.

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TextTemplating;

namespace CustomDP
{
public class CustomDirectiveProcessor : DirectiveProcessor
{
//this buffer stores the code that is added to the
//generated transformation class after all the processing is done
//---------------------------------------------------------------------
private StringBuilder codeBuffer;

//Using a Code Dom Provider creates code for the


//generated transformation class in either Visual Basic or C#.
//If you want your directive processor to support only one language, you
//can hard code the code you add to the generated transformation class.
//In that case, you do not need this field.
//--------------------------------------------------------------------------
private CodeDomProvider codeDomProvider;

//this stores the full contents of the text template that is being processed
//--------------------------------------------------------------------------
private String templateContents;

//These are the errors that occur during processing. The engine passes
//the errors to the host, and the host can decide how to display them,
//for example the the host can display the errors in the UI
//or write them to a file.
//---------------------------------------------------------------------
private CompilerErrorCollection errorsValue;
public new CompilerErrorCollection Errors
{
get { return errorsValue; }
}

//Each time this directive processor is called, it creates a new property.


//We count how many times we are called, and append "n" to each new
//property name. The property names are therefore unique.
//-----------------------------------------------------------------------------
private int directiveCount = 0;

public override void Initialize(ITextTemplatingEngineHost host)


{
//we do not need to do any initialization work
}

public override void StartProcessingRun(CodeDomProvider languageProvider, String


templateContents, CompilerErrorCollection errors)
{
//the engine has passed us the language of the text template
//we will use that language to generate code later
//----------------------------------------------------------
this.codeDomProvider = languageProvider;
this.templateContents = templateContents;
this.errorsValue = errors;

this.codeBuffer = new StringBuilder();


}

//Before calling the ProcessDirective method for a directive, the


//engine calls this function to see whether the directive is supported.
//Notice that one directive processor might support many directives.
//---------------------------------------------------------------------
public override bool IsDirectiveSupported(string directiveName)
{
if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0)
{
return true;
}
if (string.Compare(directiveName, "SuperCoolDirective", StringComparison.OrdinalIgnoreCase)
== 0)
{
return true;
}
return false;
}

public override void ProcessDirective(string directiveName, IDictionary<string, string>


arguments)
{
if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0)
{
string fileName;

if (!arguments.TryGetValue("FileName", out fileName))


{
throw new DirectiveProcessorException("Required argument 'FileName' not
specified.");
}

if (string.IsNullOrEmpty(fileName))
{
throw new DirectiveProcessorException("Argument 'FileName' is null or empty.");
}

//Now we add code to the generated transformation class.


//This directive supports either Visual Basic or C#, so we must use the
//System.CodeDom to create the code.
//If a directive supports only one language, you can hard code the code.
//--------------------------------------------------------------------------

CodeMemberField documentField = new CodeMemberField();

documentField.Name = "document" + directiveCount + "Value";


documentField.Type = new CodeTypeReference(typeof(XmlDocument));
documentField.Attributes = MemberAttributes.Private;

CodeMemberProperty documentProperty = new CodeMemberProperty();

documentProperty.Name = "Document" + directiveCount;


documentProperty.Type = new CodeTypeReference(typeof(XmlDocument));
documentProperty.Attributes = MemberAttributes.Public;
documentProperty.HasSet = false;
documentProperty.HasGet = true;

CodeExpression fieldName = new CodeFieldReferenceExpression(new


CodeThisReferenceExpression(), documentField.Name);
CodeExpression booleanTest = new CodeBinaryOperatorExpression(fieldName,
CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
CodeExpression rightSide = new CodeMethodInvokeExpression(new
CodeTypeReferenceExpression("XmlReaderHelper"), "ReadXml", new CodePrimitiveExpression(fileName));
CodeStatement[] thenSteps = new CodeStatement[] { new CodeAssignStatement(fieldName,
rightSide) };

CodeConditionStatement ifThen = new CodeConditionStatement(booleanTest, thenSteps);


documentProperty.GetStatements.Add(ifThen);

CodeStatement s = new CodeMethodReturnStatement(fieldName);


documentProperty.GetStatements.Add(s);

CodeGeneratorOptions options = new CodeGeneratorOptions();


options.BlankLinesBetweenMembers = true;
options.IndentString = " ";
options.VerbatimOrder = true;
options.BracingStyle = "C";

using (StringWriter writer = new StringWriter(codeBuffer, CultureInfo.InvariantCulture))


{
codeDomProvider.GenerateCodeFromMember(documentField, writer, options);
codeDomProvider.GenerateCodeFromMember(documentProperty, writer, options);
}

}//end CoolDirective

//One directive processor can contain many directives.


//If you want to support more directives, the code goes here...
//-----------------------------------------------------------------
if (string.Compare(directiveName, "supercooldirective", StringComparison.OrdinalIgnoreCase)
if (string.Compare(directiveName, "supercooldirective", StringComparison.OrdinalIgnoreCase)
== 0)
{
//code for SuperCoolDirective goes here...
}//end SuperCoolDirective

//Track how many times the processor has been called.


//-----------------------------------------------------------------
directiveCount++;

}//end ProcessDirective

public override void FinishProcessingRun()


{
this.codeDomProvider = null;

//important: do not do this:


//the get methods below are called after this method
//and the get methods can access this field
//-----------------------------------------------------------------
//this.codeBuffer = null;
}

public override string GetPreInitializationCodeForProcessingRun()


{
//Use this method to add code to the start of the
//Initialize() method of the generated transformation class.
//We do not need any pre-initialization, so we will just return "".
//-----------------------------------------------------------------
//GetPreInitializationCodeForProcessingRun runs before the
//Initialize() method of the base class.
//-----------------------------------------------------------------
return String.Empty;
}

public override string GetPostInitializationCodeForProcessingRun()


{
//Use this method to add code to the end of the
//Initialize() method of the generated transformation class.
//We do not need any post-initialization, so we will just return "".
//------------------------------------------------------------------
//GetPostInitializationCodeForProcessingRun runs after the
//Initialize() method of the base class.
//-----------------------------------------------------------------
return String.Empty;
}

public override string GetClassCodeForProcessingRun()


{
//Return the code to add to the generated transformation class.
//-----------------------------------------------------------------
return codeBuffer.ToString();
}

public override string[] GetReferencesForProcessingRun()


{
//This returns the references that we want to use when
//compiling the generated transformation class.
//-----------------------------------------------------------------
//We need a reference to this assembly to be able to call
//XmlReaderHelper.ReadXml from the generated transformation class.
//-----------------------------------------------------------------
return new string[]
{
"System.Xml",
this.GetType().Assembly.Location
};
}

public override string[] GetImportsForProcessingRun()


public override string[] GetImportsForProcessingRun()
{
//This returns the imports or using statements that we want to
//add to the generated transformation class.
//-----------------------------------------------------------------
//We need CustomDP to be able to call XmlReaderHelper.ReadXml
//from the generated transformation class.
//-----------------------------------------------------------------
return new string[]
{
"System.Xml",
"CustomDP"
};
}
}//end class CustomDirectiveProcessor

//-------------------------------------------------------------------------
// the code that we are adding to the generated transformation class
// will call this method
//-------------------------------------------------------------------------
public static class XmlReaderHelper
{
public static XmlDocument ReadXml(string fileName)
{
XmlDocument d = new XmlDocument();

using (XmlTextReader reader = new XmlTextReader(fileName))


{
try
{
d.Load(reader);
}
catch (System.Xml.XmlException e)
{
throw new DirectiveProcessorException("Unable to read the XML file.", e);
}
}
return d;
}
}//end class XmlReaderHelper
}//end namespace CustomDP

Imports System
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports System.Collections.Generic
Imports System.Globalization
Imports System.IO
Imports System.Text
Imports System.Xml
Imports System.Xml.Serialization
Imports Microsoft.VisualStudio.TextTemplating

Namespace CustomDP

Public Class CustomDirectiveProcessor


Inherits DirectiveProcessor

'this buffer stores the code that is added to the


'generated transformation class after all the processing is done
'---------------------------------------------------------------
Private codeBuffer As StringBuilder

'Using a Code Dom Provider creates code for the


'generated transformation class in either Visual Basic or C#.
'If you want your directive processor to support only one language, you
'can hard code the code you add to the generated transformation class.
'can hard code the code you add to the generated transformation class.
'In that case, you do not need this field.
'--------------------------------------------------------------------------
Private codeDomProvider As CodeDomProvider

'this stores the full contents of the text template that is being processed
'--------------------------------------------------------------------------
Private templateContents As String

'These are the errors that occur during processing. The engine passes
'the errors to the host, and the host can decide how to display them,
'for example the the host can display the errors in the UI
'or write them to a file.
'---------------------------------------------------------------------
Private errorsValue As CompilerErrorCollection
Public Shadows ReadOnly Property Errors() As CompilerErrorCollection
Get
Return errorsValue
End Get
End Property

'Each time this directive processor is called, it creates a new property.


'We count how many times we are called, and append "n" to each new
'property name. The property names are therefore unique.
'--------------------------------------------------------------------------
Private directiveCount As Integer = 0

Public Overrides Sub Initialize(ByVal host As ITextTemplatingEngineHost)

'we do not need to do any initialization work


End Sub

Public Overrides Sub StartProcessingRun(ByVal languageProvider As CodeDomProvider, ByVal


templateContents As String, ByVal errors As CompilerErrorCollection)

'the engine has passed us the language of the text template


'we will use that language to generate code later
'----------------------------------------------------------
Me.codeDomProvider = languageProvider
Me.templateContents = templateContents
Me.errorsValue = errors

Me.codeBuffer = New StringBuilder()


End Sub

'Before calling the ProcessDirective method for a directive, the


'engine calls this function to see whether the directive is supported.
'Notice that one directive processor might support many directives.
'---------------------------------------------------------------------
Public Overrides Function IsDirectiveSupported(ByVal directiveName As String) As Boolean

If String.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) = 0


Then
Return True
End If

If String.Compare(directiveName, "SuperCoolDirective", StringComparison.OrdinalIgnoreCase) =


0 Then
Return True
End If

Return False
End Function

Public Overrides Sub ProcessDirective(ByVal directiveName As String, ByVal arguments As


IDictionary(Of String, String))

If String.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) = 0


Then
Dim fileName As String

If Not (arguments.TryGetValue("FileName", fileName)) Then


Throw New DirectiveProcessorException("Required argument 'FileName' not specified.")
End If

If String.IsNullOrEmpty(fileName) Then
Throw New DirectiveProcessorException("Argument 'FileName' is null or empty.")
End If

'Now we add code to the generated transformation class.


'This directive supports either Visual Basic or C#, so we must use the
'System.CodeDom to create the code.
'If a directive supports only one language, you can hard code the code.
'--------------------------------------------------------------------------

Dim documentField As CodeMemberField = New CodeMemberField()

documentField.Name = "document" & directiveCount & "Value"


documentField.Type = New CodeTypeReference(GetType(XmlDocument))
documentField.Attributes = MemberAttributes.Private

Dim documentProperty As CodeMemberProperty = New CodeMemberProperty()

documentProperty.Name = "Document" & directiveCount


documentProperty.Type = New CodeTypeReference(GetType(XmlDocument))
documentProperty.Attributes = MemberAttributes.Public
documentProperty.HasSet = False
documentProperty.HasGet = True

Dim fieldName As CodeExpression = New CodeFieldReferenceExpression(New


CodeThisReferenceExpression(), documentField.Name)
Dim booleanTest As CodeExpression = New CodeBinaryOperatorExpression(fieldName,
CodeBinaryOperatorType.IdentityEquality, New CodePrimitiveExpression(Nothing))
Dim rightSide As CodeExpression = New CodeMethodInvokeExpression(New
CodeTypeReferenceExpression("XmlReaderHelper"), "ReadXml", New CodePrimitiveExpression(fileName))
Dim thenSteps As CodeStatement() = New CodeStatement() {New
CodeAssignStatement(fieldName, rightSide)}

Dim ifThen As CodeConditionStatement = New CodeConditionStatement(booleanTest,


thenSteps)
documentProperty.GetStatements.Add(ifThen)

Dim s As CodeStatement = New CodeMethodReturnStatement(fieldName)


documentProperty.GetStatements.Add(s)

Dim options As CodeGeneratorOptions = New CodeGeneratorOptions()


options.BlankLinesBetweenMembers = True
options.IndentString = " "
options.VerbatimOrder = True
options.BracingStyle = "VB"

Using writer As StringWriter = New StringWriter(codeBuffer,


CultureInfo.InvariantCulture)

codeDomProvider.GenerateCodeFromMember(documentField, writer, options)


codeDomProvider.GenerateCodeFromMember(documentProperty, writer, options)
End Using

End If 'CoolDirective

'One directive processor can contain many directives.


'If you want to support more directives, the code goes here...
'-----------------------------------------------------------------
If String.Compare(directiveName, "supercooldirective", StringComparison.OrdinalIgnoreCase) =
0 Then

'code for SuperCoolDirective goes here


End If 'SuperCoolDirective
End If 'SuperCoolDirective

'Track how many times the processor has been called.


'-----------------------------------------------------------------
directiveCount += 1
End Sub 'ProcessDirective

Public Overrides Sub FinishProcessingRun()

Me.codeDomProvider = Nothing

'important: do not do this:


'the get methods below are called after this method
'and the get methods can access this field
'-----------------------------------------------------------------
'Me.codeBuffer = Nothing
End Sub

Public Overrides Function GetPreInitializationCodeForProcessingRun() As String

'Use this method to add code to the start of the


'Initialize() method of the generated transformation class.
'We do not need any pre-initialization, so we will just return "".
'-----------------------------------------------------------------
'GetPreInitializationCodeForProcessingRun runs before the
'Initialize() method of the base class.
'-----------------------------------------------------------------
Return String.Empty
End Function

Public Overrides Function GetPostInitializationCodeForProcessingRun() As String

'Use this method to add code to the end of the


'Initialize() method of the generated transformation class.
'We do not need any post-initialization, so we will just return "".
'------------------------------------------------------------------
'GetPostInitializationCodeForProcessingRun runs after the
'Initialize() method of the base class.
'-----------------------------------------------------------------
Return String.Empty
End Function

Public Overrides Function GetClassCodeForProcessingRun() As String

'Return the code to add to the generated transformation class.


'-----------------------------------------------------------------
Return codeBuffer.ToString()
End Function

Public Overrides Function GetReferencesForProcessingRun() As String()

'This returns the references that we want to use when


'compiling the generated transformation class.
'-----------------------------------------------------------------
'We need a reference to this assembly to be able to call
'XmlReaderHelper.ReadXml from the generated transformation class.
'-----------------------------------------------------------------
Return New String() {"System.Xml", Me.GetType().Assembly.Location}
End Function

Public Overrides Function GetImportsForProcessingRun() As String()

'This returns the imports or using statements that we want to


'add to the generated transformation class.
'-----------------------------------------------------------------
'We need CustomDP to be able to call XmlReaderHelper.ReadXml
'from the generated transformation class.
'-----------------------------------------------------------------
Return New String() {"System.Xml", "CustomDP"}
End Function
End Function
End Class 'CustomDirectiveProcessor

'--------------------------------------------------------------------------
' the code that we are adding to the generated transformation class
' will call this method
'--------------------------------------------------------------------------
Public Class XmlReaderHelper

Public Shared Function ReadXml(ByVal fileName As String) As XmlDocument

Dim d As XmlDocument = New XmlDocument()

Using reader As XmlTextReader = New XmlTextReader(fileName)

Try
d.Load(reader)

Catch e As System.Xml.XmlException

Throw New DirectiveProcessorException("Unable to read the XML file.", e)


End Try
End Using

Return d
End Function
End Class 'XmlReaderHelper

End Namespace

4. For Visual Basic only, open the Project menu, and click CustomDP Properties. On the Application tab, in
Root namespace, delete the default value, CustomDP .
5. On the File menu, click Save All.
6. On the Build menu, click Build Solution.
Build the Project
Build the project. On the Build menu, click Build Solution.

Registering the Directive Processor


Before you can call a directive from a text template in Visual Studio, you must add a registry key for the directive
processor.

NOTE
If you want to install the directive processor on more than one computer, it is better to define a Visual Studio Extension
(VSIX) that includes a .pkgdef file along with your assembly. For more information, see Deploying a Custom Directive
Processor.

Keys for directive processors exist in the registry in the following location:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors

For 64-bit systems, the registry location is:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors
In this section, you add a key for your custom directive processor to the registry in the same location.
Cau t i on

Incorrectly editing the registry can severely damage your system. Before you make changes to the registry, back up
any valuable data that is on the computer.
To add a registry key for the directive processor
1. Run the regedit command by using the Start menu or the command line.
2. Browse to the location
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcess
ors, and click the node.
On 64-bit systems, use
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\
DirectiveProcessors
3. Add a new key named CustomDirectiveProcessor.

NOTE
This is the name that you will use in the Processor field of your custom directives. This name does not need to match
the name of the directive, the name of the directive processor class, or the directive processor namespace.

4. Add a new string value named Class that has a value CustomDP.CustomDirectiveProcessor for the name of
the new string.
5. Add a new string value named CodeBase that has a value equal to the path of the CustomDP.dll that you
created earlier in this walkthrough.
For example, the path might look like C:\UserFiles\CustomDP\bin\Debug\CustomDP.dll .
Your registry key should have the following values:

NAME TYPE DATA

(Default) REG_SZ (value not set)

Class REG_SZ CustomDP.CustomDirectiveProcessor

CodeBase REG_SZ <Path to Your


Solution>CustomDP\bin\Debug\Cus
tomDP.dll

If you have put the assembly in the GAC, the values should look like the following:

NAME TYPE DATA

(Default) REG_SZ (value not set)

Class REG_SZ CustomDP.CustomDirectiveProcessor

Assembly REG_SZ CustomDP.dll

6. Restart Visual Studio.


Testing the Directive Processor
To test the directive processor, you need to write a text template that calls it.
In this example, the text template calls the directive and passes in the name of an XML file that contains
documentation for a class file.
The text template then uses the XmlDocument property that the directive creates to navigate the XML and print the
documentation comments.
To create an XML file for use in testing the directive processor
1. Create a text file named DocFile.xml by using any text editor (for example, Notepad).

NOTE
You can create this file in any location (for example, C:\Test\DocFile.xml).

2. Add the following to the text file:

<?xml version="1.0"?>
<doc>
<assembly>
<name>xmlsample</name>
</assembly>
<members>
<member name="T:SomeClass">
<summary>Class level summary documentation goes here.</summary>
<remarks>Longer comments can be associated with a type or member through the remarks
tag</remarks>
</member>
<member name="F:SomeClass.m_Name">
<summary>Store for the name property</summary>
</member>
<member name="M:SomeClass.#ctor">
<summary>The class constructor.</summary>
</member>
<member name="M:SomeClass.SomeMethod(System.String)">
<summary>Description for SomeMethod.</summary>
<param name="s">Parameter description for s goes here</param>
<seealso cref="T:System.String">You can use the cref attribute on any tag to reference a
type or member and the compiler will check that the reference exists.</seealso>
</member>
<member name="M:SomeClass.SomeOtherMethod">
<summary>Some other method.</summary>
<returns>Return results are described through the returns tag.</returns>
<seealso cref="M:SomeClass.SomeMethod(System.String)">Notice the use of the cref attribute
to reference a specific method</seealso>
</member>
<member name="M:SomeClass.Main(System.String[])">
<summary>The entry point for the application.</summary>
<param name="args">A list of command line arguments</param>
</member>
<member name="P:SomeClass.Name">
<summary>Name property</summary>
<value>A value tag is used to describe the property value</value>
</member>
</members>
</doc>

3. Save and close the file.


To create a text template to test the directive processor
1. In Visual Studio, create a C# or Visual Basic class library project named TemplateTest.
2. Add a new text template file named TestDP.tt.
3. Make sure that the Custom Tool property of TestDP.tt is set to TextTemplatingFileGenerator .
4. Change the content of TestDP.tt to the following text.

NOTE
Make sure to replace the string < YOUR PATH> with the path to the DocFile.xml file.

The language of the text template does not have to match the language of the directive processor.

<#@ assembly name="System.Xml" #>


<#@ template debug="true" #>
<#@ output extension=".txt" #>

<# //This will call the custom directive processor. #>


<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>

<# //Uncomment this line if you want to see the generated transformation class. #>
<# //System.Diagnostics.Debugger.Break(); #>

<# //This will use the results of the directive processor. #>
<# //The directive processor has read the XML and stored it in Document0. #>
<#
XmlNode node = Document0.DocumentElement.SelectSingleNode("members");

foreach (XmlNode member in node.ChildNodes)


{
XmlNode name = member.Attributes.GetNamedItem("name");
WriteLine("{0,7}: {1}", "Name", name.Value);

foreach (XmlNode comment in member.ChildNodes)


{
WriteLine("{0,7}: {1}", comment.Name, comment.InnerText);
}
WriteLine("");
}
#>

<# //You can call the directive processor again and pass it a different file. #>
<# //@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\<Your Second File>" #>

<# //To use the results of the second directive call, use Document1. #>
<#
//XmlNode node2 = Document1.DocumentElement.SelectSingleNode("members");

//...
#>
<#@ assembly name="System.Xml" #>
<#@ template debug="true" language="vb" #>
<#@ output extension=".txt" #>

<# 'This will call the custom directive processor. #>


<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>

<# 'Uncomment this line if you want to see the generated transformation class. #>
<# 'System.Diagnostics.Debugger.Break() #>

<# 'This will use the results of the directive processor. #>
<# 'The directive processor has read the XML and stored it in Document0. #>
<#
Dim node as XmlNode = Document0.DocumentElement.SelectSingleNode("members")

Dim member As XmlNode


For Each member In node.ChildNodes

Dim name As XmlNode = member.Attributes.GetNamedItem("name")


WriteLine("{0,7}: {1}", "Name", name.Value)

Dim comment As XmlNode


For Each comment In member.ChildNodes
WriteLine("{0,7}: {1}", comment.Name, comment.InnerText)
Next

WriteLine("")
Next
#>

<# 'You can call the directive processor again and pass it a different file. #>
<# '@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFileTwo.xml" #>

<# 'To use the results of the second directive call, use Document1. #>
<#
'node = Document1.DocumentElement.SelectSingleNode("members")

'...
#>

NOTE
In this example, the value of the Processor parameter is CustomDirectiveProcessor . The value of the
Processor parameter must match the name of the processor's registry key.

5. On the File menu, click Save All.


To test the directive processor
1. In Solution Explorer, right-click TestDP.tt and then click Run Custom Tool.
For Visual Basic users, TestDP.txt might not appear in Solution Explorer by default. To display all files
assigned to the project, open the Project menu and click Show All Files.
2. In Solution Explorer, expand the TestDP.txt node, and then double-click TestDP.txt to open it in the editor.
The generated text output appears. The output should look like the following:
Name: T:SomeClass
summary: Class level summary documentation goes here.
remarks: Longer comments can be associated with a type or member through the remarks tag

Name: F:SomeClass.m_Name
summary: Store for the name property

Name: M:SomeClass.#ctor
summary: The class constructor.

Name: M:SomeClass.SomeMethod(System.String)
summary: Description for SomeMethod.
param: Parameter description for s goes here
seealso: You can use the cref attribute on any tag to reference a type or member and the compiler will
check that the reference exists.

Name: M:SomeClass.SomeOtherMethod
summary: Some other method.
returns: Return results are described through the returns tag.
seealso: Notice the use of the cref attribute to reference a specific method

Name: M:SomeClass.Main(System.String[])
summary: The entry point for the application.
param: A list of command line arguments

Name: P:SomeClass.Name
summary: Name property
value: A value tag is used to describe the property value

Adding HTML to Generated Text


After you test your custom directive processor, you might want to add some HTML to your generated text.
To add HTML to the generated text
1. Replace the code in TestDP.tt with the following. The HTML is highlighted. Make sure to replace the string
YOUR PATH with the path to the DocFile.xml file.

NOTE
Additional open <# and close #> tags separate the statement code from the HTML tags.
<#@ assembly name="System.Xml" #>
<#@ template debug="true" #>
<#@ output extension=".htm" #>

<# //this will call the custom directive processor #>


<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>

<# //uncomment this line if you want to see the generated transformation class #>
<# //System.Diagnostics.Debugger.Break(); #>

<html><body>

<# //this will use the results of the directive processor #>
<# //the directive processor has read the XML and stored it in Document0#>
<#
XmlNode node = Document0.DocumentElement.SelectSingleNode("members");

foreach (XmlNode member in node.ChildNodes)


{
#>
<h3>
<#
XmlNode name = member.Attributes.GetNamedItem("name");
WriteLine("{0,7}: {1}", "Name", name.Value);
#>
</h3>
<#
foreach (XmlNode comment in member.ChildNodes)
{
WriteLine("{0,7}: {1}", comment.Name, comment.InnerText);
#>
<br/>
<#
}
}
#>
</body></html>
<#@ assembly name="System.Xml" #>
<#@ template debug="true" language="vb" #>
<#@ output extension=".htm" #>

<# 'this will call the custom directive processor #>


<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>

<# 'uncomment this line if you want to see the generated transformation class #>
<# 'System.Diagnostics.Debugger.Break() #>

<html><body>

<# 'this will use the results of the directive processor #>
<# 'the directive processor has read the XML and stored it in Document0#>
<#
Dim node as XmlNode = Document0.DocumentElement.SelectSingleNode("members")

Dim member As XmlNode


For Each member In node.ChildNodes
#>
<h3>
<#
Dim name As XmlNode = member.Attributes.GetNamedItem("name")
WriteLine("{0,7}: {1}", "Name", name.Value)
#>
</h3>
<#
Dim comment As XmlNode
For Each comment In member.ChildNodes
WriteLine("{0,7}: {1}", comment.Name, comment.InnerText)
#>
<br/>
<#
Next
Next
#>
</body></html>

2. On the File menu, click Save TestDP.txt.


3. To view the output in a browser, in Solution Explorer, right-click TestDP.htm, and click View In Browser.
Your output should be the same as the original text except it should have the HTML format applied. Each
item name should appear in bold.
Invoking Text Transformation in a VS Extension
10/18/2017 3 min to read Edit Online

If you are writing a Visual Studio extension such as a menu command or domain-specific language, you can use
the text templating service to transform text templates. Get the STextTemplating service and cast it to
ITextTemplating.

Getting the text templating service


using Microsoft.VisualStudio.TextTemplating;
using Microsoft.VisualStudio.TextTemplating.VSHost;
...
// Get a service provider - how you do this depends on the context:
IServiceProvider serviceProvider = ...; // An instance of EnvDTE, for example

// Get the text template service:


ITextTemplating t4 = serviceProvider.GetService(typeof(STextTemplating)) as ITextTemplating;

// Process a text template:


string result = t4.ProcessTemplate(filePath, System.IO.File.ReadAllText(filePath));

Passing parameters to the template


You can pass parameters into the template. Inside the template, you can get the parameter values by using the
<#@parameter#> directive.

For the type of a parameter, you must use a type that is serializable or that can be marshaled. That is, the type
must be declared with SerializableAttribute, or it must be derived from MarshalByRefObject. This restriction is
necessary because the text template is executed in a separate AppDomain. All built-in types such as
System.String and System.Int32 are serializable.
To pass parameter values, the calling code can place values either in the Session dictionary, or in the CallContext.
The following example uses both methods to transform a short test template:
using Microsoft.VisualStudio.TextTemplating;
using Microsoft.VisualStudio.TextTemplating.VSHost;
...
// Get a service provider - how you do this depends on the context:
IServiceProvider serviceProvider = dte;

// Get the text template service:


ITextTemplating t4 = serviceProvider.GetService(typeof(STextTemplating)) as ITextTemplating;
ITextTemplatingSessionHost sessionHost = t4 as ITextTemplatingSessionHost;

// Create a Session in which to pass parameters:


sessionHost.Session = sessionHost.CreateSession();
sessionHost.Session["parameter1"] = "Hello";
sessionHost.Session["parameter2"] = DateTime.Now;

// Pass another value in CallContext:


System.Runtime.Remoting.Messaging.CallContext.LogicalSetData("parameter3", 42);

// Process a text template:


string result = t4.ProcessTemplate("",
// This is the test template:
"<#@parameter type=\"System.String\" name=\"parameter1\"#>"
+ "<#@parameter type=\"System.DateTime\" name=\"parameter2\"#>"
+ "<#@parameter type=\"System.Int32\" name=\"parameter3\"#>"
+ "Test: <#=parameter1#> <#=parameter2#> <#=parameter3#>");

// This test code yields a result similar to the following line:


// Test: Hello 07/06/2010 12:37:45 42

Error Reporting and the Output Directive


Any errors that arise during processing will be displayed in the Visual Studio error window. In addition, you can be
notified of errors by specifying a callback that implements ITextTemplatingCallback.
If you want to write the result string to a file, you might want to know what file extension and encoding have been
specified in the <#@output#> directive in the template. This information will also be passed to your callback. For
more information, see T4 Output Directive.
void ProcessMyTemplate(string MyTemplateFile)
{
string templateContent = File.ReadAllText(MyTemplateFile);
T4Callback cb = new T4Callback();
// Process a text template:
string result = t4.ProcessTemplate(MyTemplateFile, templateContent, cb);
// If there was an output directive in the MyTemplateFile,
// then cb.SetFileExtension() will have been called.
// Determine the output file name:
string resultFileName =
Path.Combine(Path.GetDirectoryName(MyTemplateFile),
Path.GetFileNameWithoutExtension(MyTemplateFile))
+ cb.fileExtension;
// Write the processed output to file:
File.WriteAllText(resultFileName, result, cb.outputEncoding);
// Append any error messages:
if (cb.errorMessages.Count > 0)
{
File.AppendAllLines(resultFileName, cb.errorMessages);
}
}

class T4Callback : ITextTemplatingCallback


{
public List<string> errorMessages = new List<string>();
public string fileExtension = ".txt";
public Encoding outputEncoding = Encoding.UTF8;

public void ErrorCallback(bool warning, string message, int line, int column)
{ errorMessages.Add(message); }

public void SetFileExtension(string extension)


{ fileExtension = extension; }

public void SetOutputEncoding(Encoding encoding, bool fromOutputDirective)


{ outputEncoding = encoding; }
}

The code can be tested with a template file similar to the following:

<#@output extension=".htm" encoding="ASCII"#>


<# int unused; // Compiler warning "unused variable"
#>
Sample text.

The compiler warning will appear in the Visual Studio error window, and it will also generate a call to
ErrorCallback .

Reference parameters
You can pass values out of a text template by using a parameter class that is derived from MarshalByRefObject.

Related Topics
To generate text from a preprocessed text template:
Call the TransformText() method of the generated class. For more information, see Run-Time Text Generation
with T4 Text Templates.
To generate text outside a Visual Studio extension:
Define a custom host. For more information, see Processing Text Templates by using a Custom Host.
To generate source code that can later be compiled and executed:
Call the t4.PreprocessTemplate() method of ITextTemplating.
Processing Text Templates by using a Custom Host
10/18/2017 2 min to read Edit Online

The text template transformation process takes a text template file as the input and produces a text file as the
output. You can call the text transformation engine from a Visual Studio extension, or from a standalone
application running on a machine on which Visual Studio is installed. However, you must provide a text templating
host. This class connects the template to the environment, finding resources such as assemblies and include files,
and dealing with the output and error messages.

TIP
If you are writing a package or extension that will run within Visual Studio, consider using the text templating service, instead
of writing your own host. For more information, see Invoking Text Transformation in a VS Extension.

NOTE
We do not recommend using text template transformations in server applications. We do not recommend using text
template transformations except in a single thread. This is because the text templating Engine re-uses a single AppDomain
to translate, compile, and execute templates. The translated code is not designed to be thread-safe. The Engine is designed
to process files serially, as they are in a Visual Studio project at design time.
For run-time applications, consider using preprocessed text templates: see Run-Time Text Generation with T4 Text
Templates.

If your application uses a set of templates that are fixed at compile time, it is easier to use Preprocessed Text
Templates. You can also use that approach if your application will run on a machine on which Visual Studio is not
installed. For more information, see Run-Time Text Generation with T4 Text Templates.

Executing a Text Template in Your Application


To execute a text template, you call the ProcessTemplate method of Microsoft.VisualStudio.TextTemplating.Engine:

using Microsoft.VisualStudio.TextTemplating;
...
Engine engine = new Engine();
string output = engine.ProcessTemplate(templateString, host);

Your application must find and provide the template, and must deal with the output.
In the host parameter, you must provide a class that implements ITextTemplatingEngineHost. This is called back
by the Engine.
The host must be able to log errors, resolve references to assembly and include files, provide an Application
Domain in which the template can execute, and call the appropriate processor for each directive.
Microsoft.VisualStudio.TextTemplating.Engine is defined in Microsoft.VisualStudio.TextTemplating.*.0.dll, and
ITextTemplatingEngineHost is defined in Microsoft.VisualStudio.TextTemplating.Interfaces.*.0.dll.

In This Section
Walkthrough: Creating a Custom Text Template Host
Shows you how to create a custom text template host that makes the text template functionality available outside
Visual Studio.

Reference
ITextTemplatingEngineHost

Related Sections
The Text Template Transformation Process
Describes how text transformation works, and which parts you can customize.
Creating Custom T4 Text Template Directive Processors
Provides an overview of text template directive processors.
Walkthrough: Creating a Custom Text Template Host
10/18/2017 20 min to read Edit Online

A text templatehost provides an environment that enables the text template transformation engine to run. The
host is responsible for managing the engine's interaction with the file system. The engine or directive processor
that needs a file or an assembly can request a resource from the host. The host can then search directories and the
global assembly cache to locate the requested resource. For more information, see The Text Template
Transformation Process.
You can write a custom host if you want to use the text template transformation functionality from outside Visual
Studio or if you want to integrate that functionality into custom tools. To create a custom host, you must create a
class that inherits from ITextTemplatingEngineHost. For the documentation of the individual methods, see
ITextTemplatingEngineHost.

WARNING
If you are writing a Visual Studio extension or package, consider using the text templating service instead of creating your
own host. For more information, see Invoking Text Transformation in a VS Extension.

Tasks illustrated in this walkthrough include the following:


Creating a custom text template host.
Testing the custom host.

Prerequisites
To complete this walkthrough, you must have the following:
Visual Studio 2010 or later
Visual Studio SDK

Creating a Custom Text Template Host


In this walkthrough, you create a custom host in an executable application that can be called from the command
line. The application accepts a text template file as an argument, reads the template, calls the engine to transform
the template, and displays any errors that occur in the command prompt window.
To create a custom host
1. In Visual Studio, create a new Visual Basic or a C# console application named CustomHost.
2. Add references to the following assemblies:
Microsoft.VisualStudio.TextTemplating.*.0
Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 and later versions
3. Replace the code in the Program.cs or Module1.vb file with the following code:

using System;
using System.IO;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;
using System.Text;
using Microsoft.VisualStudio.TextTemplating;

namespace CustomHost
{
//The text template transformation engine is responsible for running
//the transformation process.
//The host is responsible for all input and output, locating files,
//and anything else related to the external environment.
//-------------------------------------------------------------------------
class CustomCmdLineHost : ITextTemplatingEngineHost
{
//the path and file name of the text template that is being processed
//---------------------------------------------------------------------
internal string TemplateFileValue;
public string TemplateFile
{
get { return TemplateFileValue; }
}
//This will be the extension of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private string fileExtensionValue = ".txt";
public string FileExtension
{
get { return fileExtensionValue; }
}
//This will be the encoding of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private Encoding fileEncodingValue = Encoding.UTF8;
public Encoding FileEncoding
{
get { return fileEncodingValue; }
}
//These are the errors that occur when the engine processes a template.
//The engine passes the errors to the host when it is done processing,
//and the host can decide how to display them. For example, the host
//can display the errors in the UI or write them to a file.
//---------------------------------------------------------------------
private CompilerErrorCollection errorsValue;
public CompilerErrorCollection Errors
{
get { return errorsValue; }
}
//The host can provide standard assembly references.
//The engine will use these references when compiling and
//executing the generated transformation class.
//--------------------------------------------------------------
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
//If this host searches standard paths and the GAC,
//we can specify the assembly name like this.
//---------------------------------------------------------
//"System"

//Because this host only resolves assemblies from the


//fully qualified path and name of the assembly,
//this is a quick way to get the code to give us the
//fully qualified path and name of the System assembly.
//---------------------------------------------------------
typeof(System.Uri).Assembly.Location
typeof(System.Uri).Assembly.Location
};
}
}
//The host can provide standard imports or using statements.
//The engine will add these statements to the generated
//transformation class.
//--------------------------------------------------------------
public IList<string> StandardImports
{
get
{
return new string[]
{
"System"
};
}
}
//The engine calls this method based on the optional include directive
//if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
//The included text is returned in the context parameter.
//If the host searches the registry for the location of include files,
//or if the host searches multiple locations by default, the host can
//return the final path of the include file in the location parameter.
//---------------------------------------------------------------------
public bool LoadIncludeText(string requestFileName, out string content, out string location)
{
content = System.String.Empty;
location = System.String.Empty;

//If the argument is the fully qualified path of an existing file,


//then we are done.
//----------------------------------------------------------------
if (File.Exists(requestFileName))
{
content = File.ReadAllText(requestFileName);
return true;
}
//This can be customized to search specific paths for the file.
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
else
{
return false;
}
}
//Called by the Engine to enquire about
//the processing options you require.
//If you recognize that option, return an
//appropriate value.
//Otherwise, pass back NULL.
//--------------------------------------------------------------------
public object GetHostOption(string optionName)
{
object returnObject;
switch (optionName)
{
case "CacheAssemblies":
returnObject = true;
break;
default:
returnObject = null;
break;
}
return returnObject;
}
//The engine calls this method to resolve assembly references used in
//the generated transformation class project and for the optional
//assembly directive if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveAssemblyReference(string assemblyReference)
{
//If the argument is the fully qualified path of an existing file,
//then we are done. (This does not do any work.)
//----------------------------------------------------------------
if (File.Exists(assemblyReference))
{
return assemblyReference;
}
//Maybe the assembly is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile),
assemblyReference);
if (File.Exists(candidate))
{
return candidate;
}
//This can be customized to search specific paths for the file
//or to search the GAC.
//----------------------------------------------------------------
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
//If we cannot do better, return the original file name.
return "";
}
//The engine calls this method based on the directives the user has
//specified in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public Type ResolveDirectiveProcessor(string processorName)
{
//This host will not resolve any specific processors.
//Check the processor name, and if it is the name of a processor the
//host wants to support, return the type of the processor.
//---------------------------------------------------------------------
if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
{
//return typeof();
}
//This can be customized to search specific paths for the file
//or to search the GAC
//If the directive processor cannot be found, throw an error.
throw new Exception("Directive Processor not found");
}
//A directive processor can call this method if a file name does not
//have a path.
//The host can attempt to provide path information by searching
//specific paths for the file and returning the file and path if found.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolvePath(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("the file name cannot be null");
}
//If the argument is the fully qualified path of an existing file,
//then we are done
//----------------------------------------------------------------
if (File.Exists(fileName))
{
return fileName;
return fileName;
}
//Maybe the file is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
if (File.Exists(candidate))
{
return candidate;
}
//Look more places.
//----------------------------------------------------------------
//More code can go here...
//If we cannot do better, return the original file name.
return fileName;
}
//If a call to a directive in a text template does not provide a value
//for a required parameter, the directive processor can try to get it
//from the host by calling this method.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveParameterValue(string directiveId, string processorName, string
parameterName)
{
if (directiveId == null)
{
throw new ArgumentNullException("the directiveId cannot be null");
}
if (processorName == null)
{
throw new ArgumentNullException("the processorName cannot be null");
}
if (parameterName == null)
{
throw new ArgumentNullException("the parameterName cannot be null");
}
//Code to provide "hard-coded" parameter values goes here.
//This code depends on the directive processors this host will interact with.
//If we cannot do better, return the empty string.
return String.Empty;
}
//The engine calls this method to change the extension of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
public void SetFileExtension(string extension)
{
//The parameter extension has a '.' in front of it already.
//--------------------------------------------------------
fileExtensionValue = extension;
}
//The engine calls this method to change the encoding of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//----------------------------------------------------------------------
public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
{
fileEncodingValue = encoding;
}
//The engine calls this method when it is done processing a text
//template to pass any errors that occurred to the host.
//The host can decide how to display them.
//---------------------------------------------------------------------
public void LogErrors(CompilerErrorCollection errors)
{
errorsValue = errors;
}
//This is the application domain that is used to compile and run
//the generated transformation class to create the generated text output.
//----------------------------------------------------------------------
//----------------------------------------------------------------------
public AppDomain ProvideTemplatingAppDomain(string content)
{
//This host will provide a new application domain each time the
//engine processes a text template.
//-------------------------------------------------------------
return AppDomain.CreateDomain("Generation App Domain");
//This could be changed to return the current appdomain, but new
//assemblies are loaded into this AppDomain on a regular basis.
//If the AppDomain lasts too long, it will grow indefintely,
//which might be regarded as a leak.
//This could be customized to cache the application domain for
//a certain number of text template generations (for example, 10).
//This could be customized based on the contents of the text
//template, which are provided as a parameter for that purpose.
}
}
//This will accept the path of a text template as an argument.
//It will create an instance of the custom host and an instance of the
//text templating transformation engine, and will transform the
//template to create the generated text output file.
//-------------------------------------------------------------------------
class Program
{
static void Main(string[] args)
{
try
{
ProcessTemplate(args);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static void ProcessTemplate(string[] args)
{
string templateFileName = null;
if (args.Length == 0)
{
throw new System.Exception("you must provide a text template file path");
}
templateFileName = args[0];
if (templateFileName == null)
{
throw new ArgumentNullException("the file name cannot be null");
}
if (!File.Exists(templateFileName))
{
throw new FileNotFoundException("the file cannot be found");
}
CustomCmdLineHost host = new CustomCmdLineHost();
Engine engine = new Engine();
host.TemplateFileValue = templateFileName;
//Read the text template.
string input = File.ReadAllText(templateFileName);
//Transform the text template.
string output = engine.ProcessTemplate(input, host);
string outputFileName = Path.GetFileNameWithoutExtension(templateFileName);
outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName);
outputFileName = outputFileName + "1" + host.FileExtension;
File.WriteAllText(outputFileName, output, host.FileEncoding);

foreach (CompilerError error in host.Errors)


{
Console.WriteLine(error.ToString());
}
}
}
}
}

Imports System
Imports System.IO
Imports System.CodeDom.Compiler
Imports System.Collections.Generic
Imports System.Text
Imports Microsoft.VisualStudio.TextTemplating

Namespace CustomHost
'The text template transformation engine is responsible for running
'the transformation process.
'The host is responsible for all input and output, locating files,
'and anything else related to the external environment.
'-------------------------------------------------------------------------
Public Class CustomCmdLineHost
Implements ITextTemplatingEngineHost

'the path and file name of the text template that is being processed
'---------------------------------------------------------------------
Friend TemplateFileValue As String
Public ReadOnly Property TemplateFile() As String Implements
ITextTemplatingEngineHost.TemplateFile
Get
Return TemplateFileValue
End Get
End Property
'This will be the extension of the generated text output file.
'The host can provide a default by setting the value of the field here.
'The engine can change this based on the optional output directive
'if the user specifies it in the text template.
'---------------------------------------------------------------------
Private fileExtensionValue As String = ".txt"
Public ReadOnly Property FileExtension() As String
Get
Return fileExtensionValue
End Get
End Property
'This will be the encoding of the generated text output file.
'The host can provide a default by setting the value of the field here.
'The engine can change this value based on the optional output directive
'if the user specifies it in the text template.
'---------------------------------------------------------------------
Private fileEncodingValue As Encoding = Encoding.UTF8
Public ReadOnly Property fileEncoding() As Encoding
Get
Return fileEncodingValue
End Get
End Property
'These are the errors that occur when the engine processes a template.
'The engine passes the errors to the host when it is done processing,
'and the host can decide how to display them. For example, the host
'can display the errors in the UI or write them to a file.
'---------------------------------------------------------------------
Private errorsValue As CompilerErrorCollection
Public ReadOnly Property Errors() As CompilerErrorCollection
Get
Return errorsValue
End Get
End Property
'The host can provide standard assembly references.
'The engine will use these references when compiling and
'executing the generated transformation class.
'--------------------------------------------------------------
Public ReadOnly Property StandardAssemblyReferences() As IList(Of String) Implements
ITextTemplatingEngineHost.StandardAssemblyReferences
Get
'If this host searches standard paths and the GAC,
'If this host searches standard paths and the GAC,
'we can specify the assembly name like this.
'---------------------------------------------------------
'Return New String() {"System"}
'Because this host only resolves assemblies from the
'fully qualified path and name of the assembly,
'this is a quick way to get the code to give us the
'fully qualified path and name of the System assembly.
'---------------------------------------------------------
Return New String() {(New System.UriBuilder()).GetType().Assembly.Location}
End Get
End Property
'The host can provide standard imports or imports statements.
'The engine will add these statements to the generated
'transformation class.
'--------------------------------------------------------------
Public ReadOnly Property StandardImports() As IList(Of String) Implements
ITextTemplatingEngineHost.StandardImports
Get
Return New String() {"System"}
End Get
End Property
' Called by the Engine to enquire about
' the processing options you require.
' If you recognize that option, return an
' appropriate value.
' Otherwise, pass back NULL.
'--------------------------------------------------------------------
Public Function GetHostOption(ByVal optionName As String) As Object Implements
ITextTemplatingEngineHost.GetHostOption
Dim returnObject As Object
Select Case optionName
Case "CacheAssemblies"
returnObject = True
Case Else
returnObject = False
End Select
Return returnObject
End Function
'The engine calls this method based on the optional include directive
'if the user has specified it in the text template.
'This method can be called 0, 1, or more times.
'---------------------------------------------------------------------
'The included text is returned in the context parameter.
'If the host searches the registry for the location of include files
'or if the host searches multiple locations by default, the host can
'return the final path of the include file in the location parameter.
'---------------------------------------------------------------------
Public Function LoadIncludeText(ByVal requestFileName As String, ByRef content As String, ByRef
location As String) As Boolean Implements ITextTemplatingEngineHost.LoadIncludeText
content = System.String.Empty
location = System.String.Empty
'If the argument is the fully qualified path of an existing file,
'then we are done.
'----------------------------------------------------------------
If File.Exists(requestFileName) Then
content = File.ReadAllText(requestFileName)
Return True
'This can be customized to search specific paths for the file.
'This can be customized to accept paths to search as command line
'arguments.
'----------------------------------------------------------------
Else
Return False
End If
End Function
'The engine calls this method to resolve assembly references used in
'the generated transformation class project and for the optional
'assembly directive if the user has specified it in the text template.
'This method can be called 0, 1, or more times.
'This method can be called 0, 1, or more times.
'---------------------------------------------------------------------
Public Function ResolveAssemblyReference(ByVal assemblyReference As String) As String
Implements ITextTemplatingEngineHost.ResolveAssemblyReference
'If the argument is the fully qualified path of an existing file,
'then we are done. (This does not do any work.)
'----------------------------------------------------------------
If File.Exists(assemblyReference) Then
Return assemblyReference
End If
'Maybe the assembly is in the same folder as the text template that
'called the directive.
'----------------------------------------------------------------
Dim candidate As String = Path.Combine(Path.GetDirectoryName(Me.TemplateFile),
assemblyReference)
If File.Exists(candidate) Then
Return candidate
End If
'This can be customized to search specific paths for the file,
'or to search the GAC.
'----------------------------------------------------------------
'This can be customized to accept paths to search as command line
'arguments.
'----------------------------------------------------------------
'If we cannot do better, return the original file name.
Return ""
End Function
'The engine calls this method based on the directives the user has
'specified in the text template.
'This method can be called 0, 1, or more times.
'---------------------------------------------------------------------
Public Function ResolveDirectiveProcessor(ByVal processorName As String) As System.Type
Implements ITextTemplatingEngineHost.ResolveDirectiveProcessor
'This host will not resolve any specific processors.
'Check the processor name, and if it is the name of a processor the
'host wants to support, return the type of the processor.
'---------------------------------------------------------------------
If String.Compare(processorName, "XYZ", StringComparison.InvariantCultureIgnoreCase) = 0
Then
'return typeof()
End If
'This can be customized to search specific paths for the file,
'or to search the GAC.
'If the directive processor cannot be found, throw an error.
Throw New Exception("Directive Processor not found")
End Function
'A directive processor can call this method if a file name does not
'have a path.
'The host can attempt to provide path information by searching
'specific paths for the file and returning the file and path if found.
'This method can be called 0, 1, or more times.
'---------------------------------------------------------------------
Public Function ResolvePath(ByVal fileName As String) As String Implements
ITextTemplatingEngineHost.ResolvePath
If fileName Is Nothing Then
Throw New ArgumentNullException("the file name cannot be null")
End If
'If the argument is the fully qualified path of an existing file,
'then we are done.
'----------------------------------------------------------------
If File.Exists(fileName) Then
Return fileName
End If
'Maybe the file is in the same folder as the text template that
'called the directive.
'----------------------------------------------------------------
Dim candidate As String = Path.Combine(Path.GetDirectoryName(Me.TemplateFile), fileName)
If File.Exists(candidate) Then
Return candidate
End If
End If
'Look more places.
'----------------------------------------------------------------
'More code can go here...
'If we cannot do better, return the original file name
Return fileName
End Function
'If a call to a directive in a text template does not provide a value
'for a required parameter, the directive processor can try to get it
'from the host by calling this method.
'This method can be called 0, 1, or more times.
'---------------------------------------------------------------------
Public Function ResolveParameterValue(ByVal directiveId As String, ByVal processorName As
String, ByVal parameterName As String) As String Implements
ITextTemplatingEngineHost.ResolveParameterValue
If directiveId Is Nothing Then
Throw New ArgumentNullException("the directiveId cannot be null")
End If
If processorName Is Nothing Then
Throw New ArgumentNullException("the processorName cannot be null")
End If
If parameterName Is Nothing Then
Throw New ArgumentNullException("the parameterName cannot be null")
End If
'Code to provide "hard-coded" parameter values goes here.
'This code depends on the directive processors this host will interact with.
'If we cannot do better, return the empty string.
Return String.Empty
End Function
'The engine calls this method to change the extension of the
'generated text output file based on the optional output directive
'if the user specifies it in the text template.
'---------------------------------------------------------------------
Public Sub SetFileExtension(ByVal extension As String) Implements
ITextTemplatingEngineHost.SetFileExtension
'The parameter extension has a '.' in front of it already.
'--------------------------------------------------------
fileExtensionValue = extension
End Sub
'The engine calls this method to change the encoding of the
'generated text output file based on the optional output directive
'if the user specifies it in the text template.
'---------------------------------------------------------------------
Public Sub SetOutputEncoding(ByVal encoding As System.Text.Encoding, ByVal fromOutputDirective
As Boolean) Implements ITextTemplatingEngineHost.SetOutputEncoding
fileEncodingValue = encoding
End Sub
'The engine calls this method when it is done processing a text
'template to pass any errors that occurred to the host.
'The host can decide how to display them.
'---------------------------------------------------------------------
Public Sub LogErrors(ByVal errors As System.CodeDom.Compiler.CompilerErrorCollection)
Implements ITextTemplatingEngineHost.LogErrors
errorsValue = errors
End Sub
'This is the application domain that is used to compile and run
'the generated transformation class to create the generated text output.
'----------------------------------------------------------------------
Public Function ProvideTemplatingAppDomain(ByVal content As String) As System.AppDomain
Implements ITextTemplatingEngineHost.ProvideTemplatingAppDomain
'This host will provide a new application domain each time the
'engine processes a text template.
'-------------------------------------------------------------
Return AppDomain.CreateDomain("Generation App Domain")
'This could be changed to return the current appdomain, but new
'assemblies are loaded into this AppDomain on a regular basis.
'If the AppDomain lasts too long, it will grow indefintely,
'which might be regarded as a leak.
'This could be customized to cache the application domain for
'a certain number of text template generations (for example, 10).
'a certain number of text template generations (for example, 10).
'This could be customized based on the contents of the text
'template, which are provided as a parameter for that purpose.
End Function
End Class 'CustomCmdLineHost
'This will accept the path of a text template as an argument.
'It will create an instance of the custom host and an instance of the
'text templating transformation engine. It will also transform the
'template to create the generated text output file.
'-------------------------------------------------------------------------
Class Program
Shared Sub Main(ByVal args As String())
Try
ProcessTemplate(args)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Shared Sub ProcessTemplate(ByVal args As String())
Dim templateFileName As String = ""
If args.Length = 0 Then
Throw New System.Exception("you must provide a text template file path")
End If
templateFileName = args(0)
If templateFileName Is Nothing Then
Throw New ArgumentNullException("the file name cannot be null")
End If
If Not File.Exists(templateFileName) Then
Throw New FileNotFoundException("the file cannot be found")
End If
Dim host As CustomCmdLineHost = New CustomCmdLineHost()
Dim engine As Engine = New Engine()
host.TemplateFileValue = templateFileName
'Read the text template.
Dim input As String = File.ReadAllText(templateFileName)
'Transform the text template.
Dim output As String = engine.ProcessTemplate(input, host)
Dim outputFileName As String = Path.GetFileNameWithoutExtension(templateFileName)
outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName)
outputFileName = outputFileName & "1" & host.FileExtension
File.WriteAllText(outputFileName, output, host.fileEncoding)
Dim e As CompilerError
For Each e In host.Errors
Console.WriteLine(e.ToString())
Next
End Sub 'ProcessTemplate
End Class 'Program
End Namespace

4. For Visual Basic only, open the Project menu, and click CustomHost Properties. In the Startup object
list, click CustomHost.Program.
5. On the File menu, click Save All.
6. On the Build menu, click Build Solution.

Testing the Custom Host


To test the custom host, you write a text template, then you run the custom host, pass it the name of the text
template, and verify that the template is transformed.
To create a text template to test the custom host
1. Create a text file, and name it TestTemplate.tt .
You can use any text editor (for example, Notepad) to create the file.
2. Add the following to the file:

NOTE
The programming language of the text template does not have to match that of the custom host.

Text Template Host Test

<#@ template debug="true" #>

<# //Uncomment this line to test that the host allows the engine to set the extension. #>
<# //@ output extension=".htm" #>

<# //Uncomment this line if you want to debug the generated transformation class. #>
<# //System.Diagnostics.Debugger.Break(); #>

<# for (int i=0; i<3; i++)


{
WriteLine("This is a test");
}
#>

Text Template Host Test

<#@ template debug="true" language="VB"#>

<# 'Uncomment this line to test that the host allows the engine to set the extension. #>
<# '@ output extension=".htm" #>

<# 'Uncomment this line if you want to debug the generated transformation class. #>
<# 'System.Diagnostics.Debugger.Break() #>

<# Dim i As Integer


For i = 0 To 2

WriteLine("This is a test")
Next
#>

3. Save and close the file.


To test the custom host
1. Open the Command Prompt window.
2. Type the path of the executable file for the custom host, but do not press ENTER yet.
For example, type:
<YOUR PATH>CustomHost\bin\Debug\CustomHost.exe

NOTE
Instead of typing the address, you can browse to the file CustomHost.exe in Windows Explorer and then drag the
file into the Command Prompt window.

3. Type a space.
4. Type the path of the text template file, and then press ENTER.
For example, type:
C:\<YOUR PATH>TestTemplate.tt

NOTE
Instead of typing the address, you can browse to the file TestTemplate.tt in Windows Explorer and then drag the
file into the Command Prompt window.

The custom host application runs and completes the text template transformation process.
5. In Windows Explorer, browse to the folder that contains the file TestTemplate.tt.
That folder also contains the file TestTemplate1.txt.
6. Open this file to see the results of the text template transformation.
The generated text output appears and looks like this:

Text Template Host Test

This is a test
This is a test
This is a test

Next Steps
In this walkthrough, you created a text template transformation host that supports the basic transformation
functionality. You can expand your host to support text templates that call custom or generated directive
processors. For more information, see Walkthrough: Connecting a Host to a Generated Directive Processor.

See Also
ITextTemplatingEngineHost
API Reference for T4 Text Templates
10/18/2017 1 min to read Edit Online

The Text Templating API lets you invoke and customize the transformation of text templates.

Namespaces
NAMESPACE PURPOSE

Microsoft.VisualStudio.TextTemplating Contains classes for the text template transformation


functionality. The text template transformation engine is
integrated into Visual Studio, and transforms text template
files into generated text output files.

Microsoft.VisualStudio.TextTemplating.Modeling Provides text transformation facilities related to UML models


and domain-specific languages, such as access to Visual Studio
ModelBus.

Microsoft.VisualStudio.TextTemplating.VSHost Provides access to the text templating service in Visual Studio.

You might also like