DSL
My eighteenth How do I is up.
Scenario
Let's say that we start from the Minimal Language DSL template and we have an ExampleElement domain class that has a Name domain property with the Is Element Name flag set to true.
Because it has the IsElementName flag, every time that we create a ExampleElement with the UI (toolbox or model explorer) the element is created with a default name. This default name is composed of the type name of the concept plus an index. Example : ElementName1, ElementName2, etc.
The focus of this How do I shows how to create the model element programmatically.
Interfaces and classes needed
Code snippet
Wrong way:
private void CreateElement(Store store)
{
using(Transaction tx = store.TransactionManager.BeginTransaction("Add ExampleElement"))
{
ExampleModel root = store.ElementDirectory.FindElements<ExampleModel>().First() as ExampleModel;
ExampleElement element =
this.Store.ElementFactory.CreateElement(ExampleElement.DomainClassId) as ExampleElement;
root.Elements.Add(element);
tx.Commit();
}
}
Right way:
private void CreateElement(Store store)
{
using(Transaction tx = store.TransactionManager.BeginTransaction("Add ExampleElement"))
{
ExampleModel root = store.ElementDirectory.FindElements<ExampleModel>().First() as ExampleModel;
ExampleElement element =
store.ElementFactory.CreateElement(ExampleElement.DomainClassId) as ExampleElement;
ElementOperations elementOperations =
new ElementOperations(store as IServiceProvider, store);
ElementGroup elementGroup = new ElementGroup(store);
elementGroup.Add(element);
elementGroup.MarkAsRoot(element);
elementOperations.MergeElementGroup(root, elementGroup);
tx.Commit();
}
}
Assemblies needed
- Microsoft.VisualStudio.Shell.Interop
- Microsoft.VisualStudio.Modeling.Sdk.Shell
- Microsoft.VisualStudio.Modeling.Sdk.Diagrams
Stay tuned,
Pablo
Here are some pictures for the VSX Developer Conference with me in the front. :)
The videos are going to posted at MSDN soon...

Pablo
As Victor announced it, after great efforts the T4 Editor v1.0 was finally out.
I don't need to tell you how much the user experience can drastically change when you use it, having syntax coloring, intellisense, code generation preview, support for multiple T4 hosts, etc etc.
Check it out here
Pablo
My fourteenth How do I is up.
Scenario
Let's say that we want to cancel an editing operation in a model element property if the new value meets a certain condition. The focus of this How do I tackles this scenario.
In the next sample we have a MyLanguage dsl and a MyElement domain class and we want to cancel the editing operation over the "Foo" property if the new value is empty.
Interfaces and classes needed
Code snippet
public partial class MyLanguageDomainModel
{
protected override Type[] GetCustomDomainModelTypes()
{
var types = new List<Type>();
types.AddRange(base.GetCustomDomainModelTypes());
types.Add(typeof(MyElementChangeRule));
return types.ToArray();
}
}
[RuleOn(typeof(MyElement), FireTime = TimeToFire.TopLevelCommit)]
public partial class MyElementChangeRule : ChangeRule
{
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e)
{
if(!e.ModelElement.Store.TransactionManager.CurrentTransaction.IsSerializing)
{
if(e.DomainProperty.Name.Equals("Foo"))
{
if(string.IsNullOrEmpty(e.NewValue.ToString()))
{
//The name cannot be empty, therefore we cancel the change
PackageUtility.ShowError(e.ModelElement.Store, "Value cannot be empty");
e.ModelElement.Store.TransactionManager.CurrentTransaction.Rollback();
}
}
}
}
}
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Stay tuned,
Pablo
My ninth How do I is up.
Scenario
Let's say that we have some "Add rules" in our model to perform some custom operation every time a model element is added. One common problem is that we want to perform this operation only when the user creates a model element on demand, this can be translated to the user drags a shape from the toolbox, uses the model explorer, etc. But the add rule is also fired when the store is being deserialized and somehow we need to distinguish both cases. The focus of this How do I tackles this scenario.
Interfaces and classes needed
Code snippet
var transaction = e.ModelElement.Store.TransactionManager.CurrentTransaction;
if(transaction != null && !transaction.IsSerializing)
{
//TODO: perform operation
}
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk
Stay tuned,
Pablo
My sixth How do I is up.
Scenario
The scenario here is really simple, we want to get the shape associated with a model element or vice versa. The focus of this How do I tackles this scenario.
Interfaces and classes needed
Code snippet
Getting the model element corresponding shape:
PresentationViewsSubject.GetPresentation(modelElement)
.ForEach(pel =>
{
ShapeElement shape = pel as ShapeElement;
//TODO: use shape
});
Getting the model element corresponding shape:
ModelElement modelElement = PresentationViewsSubject.GetSubject(shape);
//TODO: use model element
Assemblies needed
- Microsoft.VisualStudio.Modeling.Sdk.Diagrams
Stay tuned,
Pablo
TTxGen is a generic single file generator based on the text templating engine.
What is that???, Some background:
Single file generators or custom tools are COM components registered with Visual Studio that generate code.
To implement a custom tool you basically need to implement the IVsSingleFileGenerator interface. For more detail you can read kzu's post
A custom tool also needs to be registered and associated to file a extension.
The IVsSingleFileGenerator interface declares two methods:
int DefaultExtension (
out string pbstrDefaultExtension
)
This method is used to specify the extension of the generated file.
int Generate (
[InAttribute] string wszInputFilePath,
[InAttribute] string bstrInputFileContents,
[InAttribute] string wszDefaultNamespace,
[OutAttribute] IntPtr[] rgbOutputFileContents,
out uint pcbOutput,
[InAttribute] IVsGeneratorProgress pGenerateProgress
)
This method is used to generate the code that we want. It receives the name of the file associated with custom tool, the content of the file and it returns a Byte[] with the generated code.
One serious drawback is that we need to implement, register and deploy one custom tool for each representation of custom code that we want to generate.
Ok that's all for custom tools, now the funny part...
As you may know the text templating (t4) engine is now part of Visual Studio 2008. Based on this I created an implementation of a generic single file generator using t4.
The idea here is to have the same context as in custom tools. For that I have created a custom directive processor that injects the context needed on the tt:
<#@ output extension=".xml" #>
<#@ ParentFileInjector processor="TTxGenDirectiveProcessor" requires="fileName='Sample.xml'" #>
<#= this.ParentFileName #>
<#= this.ParentFileContent #>
If we want to access the parent file name we can use the ParentFileName variable, and for the content we can use the ParentFileContent.
TTxGen comes with some tooling to create a xGen template with the proper directives and context to start generating code:
The Create xGen Template command unfold a tt template, default the extension to the extension of the parent file and it automatically replace the requires argument.
You can download TTxGen from codegallery.
Pablo
What about adding any C# 3.5 feature (linq, extension methods, partial methods, etc) inside a text template file?
The procedure is really easy, just add the "C#v3.5" or "VBv3.5" to the language directive:
<#@ template language="C#v3.5"#>
<#@ Import Namespace="System.Linq" #>
<#@ Assembly Name="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" #>
Happy code generation...
Pablo
The first CTP of the SFT or Clarius Software Factory Toolkit was released yesterday.
It has a lot features that will help you to author a guidance package:
- VB Net support (Create a guidance package using VBNet)
- DSL integration (Integrate your recipes with DSLs)
- Eventing (Launch recipes based on VS events)
- Scripting (Use the script value provider to assign values to recipe arguments inline)
- Possibility to register a guidance package on the VS Exp Hive
- Fixes for GAX known issues:
- .gpstate and TFS issue fixed
- and much more...
Please download it now and tell us your story. You could win a free MSDN Team Suite subscription.
On the next posts I will cover in more detail some of these features.
Pablo
As part of the DSL and GAX posts regarding integration, I would like to recommend an article made by Victor and Mauro named Guidance Automation Toolkit and Domain-Specific Language Tools for Visual Studio 2005: Integration Scenarios.
It describes the process of integrating both technologies, I provided some tips of my previous posts: Integrating DSL RTM and GAX part one , Integrating DSL RTM and GAX part two , Integrating DSL RTM and GAX part three
A must read...
Pablo
On the part two, I talked about launching recipes from the DSL Designer.
Another posibility is to launch a recipe from the DSL Language Explorer. For that we need the command bar GUID and ID.
The good news is that the commandbar GUID is the same that the one for the DSL Designer
The ID for the Language Explorer will always be 65537.
Pablo
Once we have the guidance package and the DSL package living on the same visual studio hive one thing that we will need for sure is the ability to launch recipes from the DSL designer.
For that we need to find out the command bar GUID of the DSL designer.
Steps:
- Open the DSL solution
- Open the Constants.cs file located under the DslPackage.csproj
- Look for:
internal static partial class Constants
{
// Menu identifier
public const string Language1CommandSetId = "a00ac613-8df8-4537-b7c7-89d31a44651c";
}
- Update the HostData section of the recipe manifest
<
HostData
>
<
CommandBar
Guid
="a00ac613-8df8-4537-b7c7-89d31a44651c"ID="65536"/>
</
HostData
>
Notice that the ID for the DSL RTM version will always be 65536.
Pablo
A guidance package and a DSL package are registered by default in different visual studio hives. A guidance package lives on the main hive and a DSL package lives on the Experimental one.
So the first thing that we need to do is migrate one of the packages so the live on the same hive.
I am going to migrate the DSL package to the main hive, the procedure is very simple:
A DSL solution consists of 2 projects, the Dsl.csproj and DslPackage.csproj and we need to edit both:
Dsl.csproj:
Change the line:
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/rootsuffix Exp /DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
To
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
DslPackage.csproj:
Change the line:
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/rootsuffix Exp /DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
To
<PropertyGroup>
<StartProgram>$(DevEnvDir)\devenv.exe</StartProgram>
<StartAction>Program</StartAction>
<StartArguments>/DesignTimeRun "..\..\..\Debugging\Debugging.sln"</StartArguments>
</PropertyGroup>
And
<TargetRegistryRoot>Software\Microsoft\VisualStudio\8.0Exp</TargetRegistryRoot>
To
<TargetRegistryRoot>Software\Microsoft\VisualStudio\8.0</TargetRegistryRoot>
After this changes the guidance package and the DSL will live in the same visual studio hive, which means that one is going to be "visible" by the other and viceversa.
Pablo
To execute a recipe you need to find out which is the GUID for the DSL. This GUID is different for every DSL project that we create.
So, you need to open the GeneratedCmd.cs class file located at \Shell on the Designer project and search for the following constant:
public const string guidDSLNAMEMenuString = "GUID";
For example if we have a DSL named DSLFoo, then we need to search for the constant named guidDSLFooMenuString.
Once we have the GUID, we need the ID. For the DSL menu it will always be 256.
If we supose that we have a guidDSLFooMenuString constant with a value of “d309f791-903f-11d0-9efc-00a0c911004f“, then the CommandBar will be:
<CommandBar Guid="d309f791-903f-11d0-9efc-00a0c911004f" ID="256"/>
Pablo
Gaston Milano developed an addin fron Visual Studio 2005, to support intellisense and syntax coloring for T4 Templates.
It works really great.
Pablo
You can download it from here.
Pablo