Professional Documents
Culture Documents
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
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)
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
To see which versions of Visual Studio support each feature, see Version support for architecture and modeling
tools
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:
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:
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:
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
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.
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.
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
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.
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:
DIAGRAMS DESCRIBES
DIAGRAM DESCRIBES
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
DIAGRAM DESCRIBES
DIAGRAM DESCRIPTION
See:
See:
DIAGRAM DESCRIPTION
See:
External Resources
CATEGORY LINKS
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
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.
Understand class structures: How to: Add Class Diagrams to Projects (Class Designer)
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
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.
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.
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.
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.
Show Base Types For a class, add the base class and the implemented
interfaces.
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 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.
Show Methods This Calls Add methods that this method calls.
Show Fields This References Add fields that this method references.
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.
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.
The program database (.pdb) file might Turn on the /DEBUG option in the
not be built. linker.
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.
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.
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 .
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.
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.
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 map will update automatically while you continue debugging. See Update the map with the next call
stack.
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:
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:
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.
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.
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.
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.
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.
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.
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 -
Undo an action in the map. Press CTRL + Z or use the Visual Studio Undo command.
Scan the map. Drag the map in any direction using the mouse.
- or -
- or -
- or -
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 -
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.
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:
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.
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.
See more information about a node or a link. Move the mouse pointer on top of the node or link until a
tooltip appears.
- 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-
-or-
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.
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.
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
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.
Show cross-group links at all times. Show All Cross-Group Links. This hides aggregated links
between groups.
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.
Change the layout inside a group. Select the group, open the shortcut menu, choose Layout,
and select the layout style you want.
- or -
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.
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 -
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.
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.
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.
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.
Compiled .NET assemblies (.dll or .exe) or binaries. Drag the assemblies or binaries from outside Visual Studio to
a map.
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
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 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.
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:
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:
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.
TO CHOOSE
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.
<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.
Background="ColorNameOrHexadecimalValue"
Border color
Stroke="StrokeValue"
For example:
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
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"
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
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
<Condition Expression="MyCategory"/>
or -
or -
<Condition Expression="HasCategory('MyCategory')"/>
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 .
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>
<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>
<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>
<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>
<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>
<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 .
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>
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:
<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:
<Links></Links>
This element contains the list of <Link> elements, which define links between nodes. For more information,
see the <Link/> element.
Example:
<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.
<Categories></Categories>
This element contains the list of <Category/> elements. For more information, see the <Category/>
element.
Example:
<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:
<Properties></Properties>
This element contains the list of <Property/> elements. For more information, see the <Property/>
element.
Example:
<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.
<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
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.
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.
Create a layer for a single artifact 1. Drag the item onto the dependency diagram from
these sources:
Solution Explorer
Create a single layer for all selected artifacts Drag all the artifacts to the dependency diagram at the same
time.
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.
Create a new unlinked layer In the Toolbox, expand the Dependency Diagram section,
and then drag a Layer to the dependency diagram.
- or -
- 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.
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.
Create new dependencies Use the Dependency and Bidirectional Dependency tools.
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.
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
Related Tasks
TOPIC TASK
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
External Resources
CATEGORY LINKS
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.
The following table describes the elements that you can use on a dependency diagram.
- 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.
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.
Supports Validation If True, then the layer validation process can verify that
the project conforms to dependencies to or from this
element.
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.
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.
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.
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
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.
Delete a dependency that should not exist Click the dependency, and then press DELETE.
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.
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.
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.
Related Topics
TITLE DESCRIPTION
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.
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.
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.
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.
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.
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.
External Resources
CATEGORY LINKS
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.
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.
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:
How to develop code that adheres to business rules Model your app's architecture
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.
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.
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
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. 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.
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.
Make sure your software meets the users' requirements: - Develop tests from a model
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
External Resources
CATEGORY LINKS
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.
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.
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
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.
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.
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:
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:
Suppress selected errors during validation Right-click the one or multiple selected errors, point to
Manage Validation Errors, and then click Suppress Errors.
Stop suppressing selected errors Right-click the selected suppressed error or errors, point to
Manage Validation Errors, and then click Stop
Suppressing Errors.
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.
<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
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.
SYNTAX DESCRIPTION
MySolution.MyProject.MyClass.MyMethod(Method)
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.
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.
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.
[Export(typeof(ICommandExtension))]
...
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;
[Import]
public ILinkedUndoContext LinkedUndoContext { get; set; }
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.
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.
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;
...
} }
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");
// 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.
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
<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
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)
{ }
/// <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:
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.
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.
NOTE
%LocalAppData% is typically DriveName:UsersUserNameAppDataLocal.
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.
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.
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
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:
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.
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.
This solution defines a domain specific language. For more information, see Overview of the Domain-
Specific Language Tools User Interface.
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:
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.
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
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.
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.
<#
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.
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 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.
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.
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.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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
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()
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.
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.
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
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
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).
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.
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.
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.
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.
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:
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;
...
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.
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);
/// <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);
}
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.
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
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:
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:
3. In the code behind the user control, insert methods to listen for elements added and removed:
public partial class UserControl1 : UserControl { ...
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
Editor
Toobox Tabs
MyDsl //a tab
Tools
ExampleElement // an element tool
ExampleRelationship // a connection tool
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.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
transistor.Base.Name = "base";
transistor.Collector.Name = "collector";
transistor.Emitter.Name = "emitter";
return elementGroup.CreatePrototype();
}
else
{
return base.CreateElementToolPrototype(store, domainClassId);
} } }
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.
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
/// <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 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.
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.
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.
// Custom actions:
ExampleElement mergingElement = sourceElement as ExampleElement;
if (mergingElement != null)
{
mergingElement.Name = DateTime.Now.ToLongTimeString();
}
}
}
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.
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.
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);
}
if (!docView.IsContextMenuShowing)
{
// User hit CTRL+V - just use base method.
// Utility class:
DesignSurfaceElementOperations op = diagram.ElementOperations;
The changes that you make will affect both the elements and the image that is copied.
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;
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.
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;
bitmap = this.CreateBitmapForClipboard(shapes);
if (bitmap != null)
{
dataObject.SetData(DataFormats.Bitmap, bitmap);
}
#endregion
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;
}
/// <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);
}
// Mark all the elements that are not embedded under other elements:
this.MarkRootElements(elementGroup, elements, closureType);
/// <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;
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.
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.
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.
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.
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:
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.
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.
this.Store.EventManagerDirectory.ElementDeleted.Add(commentRelationship,
new EventHandler<ElementDeletedEventArgs>(CommentLinkDeleted));
}
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.
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.
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
[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))]
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);
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).
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.
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
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:
Company Name The name of the current company name Current company name
in the system registry.
Package Guid The guid for the Visual Studio package <none>
generated for this DSL.
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.
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.
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.
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.
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.
Setter Access Modifier The access modifier for the setter. This public
controls the scope in which program
code can set the property.
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.
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.
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.
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.
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 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.
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.
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.
Display Name The name that is displayed in the The adjusted value of the Name
generated designer for the domain role. property.
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.
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.
Class Represented The root domain class that this diagram Current root class if applicable
represents.
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.
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.
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 Line Style The style of the separator line ( Solid , Dash
Dash , Dot , DashDot , DashDotDot ,
or Custom ).
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.
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.
Fixed Tooltip Text The text that is used for a fixed tooltip. <none>
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.
Fill Gradient Mode The fill gradient mode of this shape Horizontal
(Horizontal, Vertical, Forward Diagonal,
Backward Diagonal, or None).
Has Default Connection Points If True , the shape will use top, False
bottom, left, and right connection
points in the generated designer.
Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).
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
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
Fixed Tooltip Text The text that is used for a fixed tooltip. <none>
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.
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 Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).
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.
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
Fixed Tooltip Text The text that is used for a fixed tooltip. <none>
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.
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.
Is Single Compartment Header Visible If False , and the shape has a single True
compartment, the header of the
compartment is not visible.
Outline Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, Custom).
Text Color The color used for text decorators that Black
are associated with 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.
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
Fixed Tooltip Text The text that is used for a fixed tooltip. <none>
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.
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 Dash Style The outline dash style of this shape Solid
(Solid, Dash, Dot, DashDot,
DashDotDot, or Custom).
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
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
Fixed Tool Tip Text The text that is used for a fixed tooltip. <none>
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.
Icon Decorator
PROPERTY DESCRIPTION DEFAULT
TextDecorator
PROPERTY DESCRIPTION DEFAULT
See Also
Domain-Specific Language Tools Glossary
Properties of Connectors
10/18/2017 2 min to read Edit Online
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).
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.
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
Fixed Tooltip Text The text that is used for a fixed tooltip. <none>
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.
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"))
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)
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
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
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.
if (henry.Name.EndsWith("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) { ... }
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))
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);
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
There are also methods for finding elements, such as the following:
store.ElementDirectory.FindElements(Person.DomainClassId);
store.ElementDirectory.GetElement(elementId);
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();
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.
edward.Parents.Add(henry);
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 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.
edward.Parents.Remove(henry); // or:
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;
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.
In your DSL Definition, each element that you specify creates a class that is derived from one of the following
standard classes.
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;
This relationship also links the root of the model to the diagram:
FamilyTreeDiagram diagram =
PresentationViewsSubject.GetPresentation(familyTree)
.FirstOrDefault() as FamilyTreeDiagram;
diagram.ModelElement as FamilyTreeModel
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
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.
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:
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.
this.Store.TransactionManager.CurrentTransaction.Rollback();
Transaction Context
Each transaction has a dictionary in which you can store any information you want:
store.TransactionManager
.CurrentTransaction.TopLevelTransaction
.Context.Add(aKey, aValue);
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.
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.
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.
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.
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
If you want to be able to use modelReference later, you can store it in a domain property that has the External Type
ModelBusReference :
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.
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.
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"));
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:
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 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();
}
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";
[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.
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
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.
2. Create a new folder named MefExtension inside the Dsl project. Add the following files to it:
File name: DesignerExtensionMetaDataAttribute.tt
3. Add the following line to the existing file that is named DslPackage\Commands.vsct:
<Include href="MefExtension\CommandExtensionVSCT.vsct"/>
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;
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.
/// <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
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 .
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.
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.
None No restriction.
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.
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:
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):
/// <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
Prerequisites
Before you can start this walkthrough, you must first install these components:
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.
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.
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");
/// <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;
}
}
}
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);
DomainPropertyInfo trackingProperty =
source.Store.DomainDataDirectory.GetDomainProperty(
ExampleElement.IsNamespaceTrackingDomainPropertyId);
propertyDescriptors.Add(new TrackingPropertyDescriptor(
source, domainProperty, trackingProperty, attr));
}
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();
}
}
}
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";
if (!element.Store.InUndoRedoOrRollback)
{
element.OnDefaultNamespaceChanged(oldValue, newValue);
}
}
}
#endregion
}
}
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.
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;
}
return namespaceStorage;
}
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;
}
}
#endregion
}
}
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.
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.Modeling;
namespace CompanyName.ProductName.TrackingPropertyDSL
{
#region Helper classes for maintaining state while the store is serializing.
// 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);
}
#endregion
#endregion
}
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.
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.
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:
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:
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:
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:
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:
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:
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.
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:
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.)
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:
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":
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:
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:
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 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> ...
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.
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 :
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:
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:
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>
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.
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 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.
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();
}
/// <summary>
/// Return a User Control instead of the DSL window.
/// The user control will contain the DSL window.
/// </summary>
}
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:
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
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.
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:
CATEGORY EXECUTION
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.
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.
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) { ... }
}
The handlers are also called after Undo or Redo operations that affect the links or elements.
[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.
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.
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.
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.
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()
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.
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
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 */
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:
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
} } } }
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:
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;
}
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.
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 .
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.
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.
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:
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.
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.
// 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;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;
// 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).
/// <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];
/// <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.
NOTE
Set e.Handled to true , unless you want the event to be passed to the containing shape or diagram.
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.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;
// 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;
/// <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];
/// <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.
MEMBER DESCRIPTION
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.
// using Microsoft.VisualStudio.Modeling.Shell;
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;
this.diagram = diagram;
this.elementDirectory = diagram.Store.ElementDirectory;
}
return true;
}
}
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.
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.
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:
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.
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.
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.
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.
Standard (Default) A domain property that is saved in the store and serialized to
file.
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.
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.
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:
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.
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.
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.
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;
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;
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:
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:
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
{
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.
/*
// 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();
}
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.
}
ElementMoved A model element has been moved from one store partition
to another.
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:
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;
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 .
This method can be used both for domain properties and non-store features, such as the size of the shape.
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.
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.
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.
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.
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
...
#>
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);
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.
<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)]
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.
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.
b. Near the end of the file, insert the following additional attribute in front of class AdapterManager.
[Microsoft.VisualStudio.Modeling.Integration.HostSpecific(HostName)]
/// <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
{
}
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 #>
<#
}
}
#>
ExampleElement1
ExampleElement2
ExampleElement2 is linked to Task: Task2
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.
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.
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.
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.
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.
Prerequisites
To define a DSL, you must have installed the following components:
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.
IMPORTANT
This step generates the directive processor and adds the key for it in the registry.
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.
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");
}
NOTE
The programming language of the text template does not need to match that of the custom host.
<# //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);
WriteLine("");
}
#>
Text Template Host Test
<# '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("")
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:
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.
myDsl.myDsl
This model was saved and then re-opened in the XML text editor:
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:
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:
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:
However, if Albums are referenced by ID instead, then the monikers would be as follows:
<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.
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
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.
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.
Moniker Element Name Name of the xml element used for monikers that refer to
elements of this class.
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.
Property Description
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.
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.
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.
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.
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.
METHOD BENEFITS
VSX (Visual Studio Extension) Very easy to deploy: Copy and execute the .vsix file from the
DslPackage project.
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.
<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";
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>
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.
Namespace Company.FarmApp
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.
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 .
using System.ComponentModel;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
namespace Company.FarmApp
{
partial class FarmControl
{
public IContainer Components { get { return components; } }
2. In the DslPackage project, edit DslPackage\DocView.tt to update the following variable definition:
WARNING
You must use the shortcut menu on the Farm node, not the Animals node.
using Microsoft.VisualStudio.Modeling;
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:
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.
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.
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
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.
TERM DEFINITION
Domain Class Domain classes are the types of elements in your models.
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.
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.
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.
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.
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.
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
Prerequisites
Before you can start this procedure, you must first install these components:
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.
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.
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.
Copying elements
You can use copy, cut and paste on elements in the DSL definition diagram.
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.
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>();
/// <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");
}
}
}
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:
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
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.
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.DslDefinition.ExtensionEnable Attributes that allow you to extend the DSL designer with
ment commands, gestures and validation.
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.Shell.Picker The Picker dialog box that lets users navigate to models and
elements to create Modelbus references.
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:
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.
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.
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!
NOTE
If your project is a Visual Basic project, you must click Show All Files in order to see the output file.
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.
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;
}
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:
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:
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>
Error reporting
To place error and warning messages in the Visual Studio error window, you can use these methods:
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.
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.
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.
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:
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.
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:
.
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.
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:
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<>:
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:
At this stage, the generated .cs file contains the following declarations:
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.
using System;
namespace MyProject
{ class Program
{ static void Main(string[] args)
{ new CodeGeneratorTest().TestMethod();
// Allow user to see the output:
Console.ReadLine();
} } }
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.
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.
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
- or -
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>
Overwrite files that are read-only, for example because they are not checked out:
<PropertyGroup>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
</PropertyGroup>
<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
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.
<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>
<ItemGroup>
<None Include="MyTemplate.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<OutputFilePath>MyFolder</OutputFilePath>
<LastGenOutput>MyTemplate.cs</LastGenOutput>
</None>
</ItemGroup>
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.
<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>
<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:
<!-- 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:
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
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.
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.
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.
-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.
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.
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.
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" #>
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#" #>
<html><body>
<h1>Sales for January</h2>
<!-- table to be inserted here -->
This report is Company Confidential.
</body></html>
<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.
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:
<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>
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
And the template would be invoked by passing the parameter in the constructor:
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:
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:
MyTextTemplate1.tt:
MyProgram.cs:
...
MyTextTemplate1 t1 = new MyTextTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);
begin 1
Shared Text 2
end 1
DerivedTemplate1.tt:
#>
End material for DerivedTemplate1.
<#+
// Provide a fragment specific to this derived template:
Application code:
...
DerivedTemplate1 t1 = new DerivedTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);
Resulting output:
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:
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":
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:
<#
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.
For example, the following control block causes the output file to contain "5":
<#= 2 + 3 #>
For example, the following template file declares and uses a method:
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.
You should use absolute path names, or use standard macro names in the path name. For example:
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 .
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.
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:
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
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
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
T4 Parameter Directive
T4 Output Directive
T4 Assembly Directive
T4 Import Directive
T4 Include Directive
T4 CleanUpBehavior directive
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.
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:
<#
// 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:
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:
Console.WriteLine(new DerivedTemplate().TransformText());
Resulting output:
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.
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.
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:
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"#>
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" #>
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.
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
<!-- 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:
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.
In this example, template code can omit an explicit namespace for members of System.IO:
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
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.
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:
TextFile1.t4:
TextFile2.t4:
<!-- 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:
To delete the appDomain after processing a text template, include the following line:
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.
<#
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
<#
XmlDocument xDoc = new XmlDocument();
xDoc.Load(@"E:\CSharp\Overview.xml");
XmlAttributeCollection attributes = xDoc.Attributes;
if (attributes != null)
{
foreach (XmlAttribute attr in attributes)
{
#><#= attr.Name #><#
}
}
#>
<#+
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:
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(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
ClearIndent();
WriteLine(CurrentIndent + "Hello");
PushIndent(" ");
WriteLine(CurrentIndent + "Hello");
#>
Hello
Hello
Hello
Hello
Hello
<#
try
{
string str = null;
Write(str.Length.ToString());
}
catch (Exception e)
{
Error(e.Message);
}
#>
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));
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.
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.
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).
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.
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.
Generated code is compiled. Immediately after the previous step. Along with your application 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.
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. #><# } #>
. .
"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.
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).
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.
MyProject
Custom Code
Class1.cs
Class2.cs
Generated Code
Class1.tt
Class1.cs
Class2.tt
Class2.cs
AnotherClass.cs
In MyReportText-Methods.cs:
private string ComputeTotal() { ... }
Team Members:
<# foreach (Person p in team.Members)
{ #>
<#= p.Name #>
<# } #>
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:
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)
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.
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.
[$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 .
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>
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> .
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:
If the assembly is in the GAC, the registry subkeys should look like the following table:
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
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;
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.
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;
//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; }
}
if (string.IsNullOrEmpty(fileName))
{
throw new DirectiveProcessorException("Argument 'FileName' is null or empty.");
}
}//end CoolDirective
}//end ProcessDirective
//-------------------------------------------------------------------------
// 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();
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
'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
Return False
End Function
If String.IsNullOrEmpty(fileName) Then
Throw New DirectiveProcessorException("Argument 'FileName' is null or empty.")
End If
End If 'CoolDirective
Me.codeDomProvider = Nothing
'--------------------------------------------------------------------------
' the code that we are adding to the generated transformation class
' will call this method
'--------------------------------------------------------------------------
Public Class XmlReaderHelper
Try
d.Load(reader)
Catch e As System.Xml.XmlException
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.
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
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:
If you have put the assembly in the GAC, the values should look like the following:
NOTE
You can create this file in any location (for example, C:\Test\DocFile.xml).
<?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>
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.
<# //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");
<# //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" #>
<# '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")
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.
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
NOTE
Additional open <# and close #> tags separate the statement code from the HTML tags.
<#@ assembly name="System.Xml" #>
<#@ template debug="true" #>
<#@ output extension=".htm" #>
<# //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");
<# '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")
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.
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;
public void ErrorCallback(bool warning, string message, int line, int column)
{ errorMessages.Add(message); }
The code can be tested with a template file similar to the following:
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.
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.
Prerequisites
To complete this walkthrough, you must have the following:
Visual Studio 2010 or later
Visual Studio SDK
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"
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.
NOTE
The programming language of the text template does not have to match that of the custom host.
<# //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(); #>
<# '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() #>
WriteLine("This is a test")
Next
#>
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:
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