Buenas
en Avanade ha surgido un par de oportunidades más que interesantes y es por eso que desde hace un tiempo estoy tratando de refrescar mis conocimientos sobre las Domain Specific Language Tools para Visual Studio 2005. Me había contenido de postear sobre las mismas ya que no es tema muy popular, pero después de tener bastantes dolores de cabeza con las cosas más simples, comenzaré a soltar un poco de opinión sobre mis experiencias con las mismas.
Uno de los aspectos más importantes que proporciona Visual Studio 2005 cuando trabajamos con DSL, es la capacidad para la edición del mismo. La siguiente imagen muestra el editor por defecto que trae VS2005 para un DSL a partir de uno de los templates incluídos en VS:
Existen situaciones donde es más práctico crear otro tipo de editor para gestionar la información de nuestro modelo. Ambos editores trabajarán sobre el mismo modelo, pero sin embargo con diferentes interfaces de usuario para la edición de la información del DSL. El siguiente paso a paso demuestra como crear un DSL, y reemplazar el editor por defecto que crea Visual Studio por uno personalizado.
Creación de un proyecto DSL de ejemplo
1. En Visual Studio 2005 crear un nuevo proyecto de tipo Other Project Types // Extensibility // Domain-Specific Language Designer
2. Seleccionamos el template Minimal Language
|
3. Definimos la extensión con la que Visual Studio asociará a nuestros archivos para este dsl
4. Completamos el nombre del producto, la companía, el namespace por defecto y presionamos Finish
5. Nuestra solución de ejemplo está creada y dentro de la misma, encontraremos 2 proyectos: Dsl y DslPackage.
Nuestro proyecto principal posee el dsl con la definición que queremos crear. El template Minimal Language crea un modelo con una Domain Class principal: Example Model, relacionada con otra Domain Class llamada Example Element, que a su vez se puede relacionar con otros Example Element.

Nota: un ejemplo concreto de la potencia de las Domain Specific Language Tools; es que los proyecto de creación de DSLs aprovechan utilizan como templates plantillas DSL para la creación de las mismas, en otras palabras para crear DSLS se utilizan DSLs dentro de Visual Studio. La mejor forma de dar el ejemplo es utilizándolo @home.
6. Para probar nuestro proyecto, debemos compilar el mismo. Pero antes de presionar Ctrl + Shitf + B, debemos generar las plantillas de código a partir de nuestro diseño, para esto presionamos el botón Transform All Templates en el panel Solution Explorer y luego presionamos F5.
Nota: por defecto cuando creamos un proyecto se realiza la acción Transform All Templates, pero sin embargo es conveniente realizar esta acción cuando realizamos algún cambio en el DSL.
7. Nuestro proyecto de Debugging, posee una implementación del DSL con el que estamos trabajando. Y dentro del mismo podremos ver
- en el panel Toolbox, poseemos 2 herramientas para crear nuevos elementos y para crear relaciones entre los mismos
- el archivo Sample.dsl10 posee elementos de ejemplo creados dentro del mismo, para que veamos una representación de nuestro DSL
- el panel DSL10 Explorer nos muestra la jerarquía de los elementos con los que estamos trabajando
- el proyecto posee 2 archivos con la extension dsl10, que son modelos de ejemplo para nuestro proyecto
8. Si cerramos el archivo Sample.dsl10 y vemos el contenido del mismo en modo texto podremos ver que posee la siguiente información, que nos muestra los 2 elementos que posee el DSL y la relacion entre los mismos a partir del ExampleElement1 hacia el ExampleElement2.
<?xml version="1.0" encoding="utf-8"?>
<exampleModel dslVersion="1.0.0.0" Id="cbe83dd6-007a-48dd-9c21-b0859208c1a7" xmlns="http://schemas.microsoft.com/dsltools/DSL10">
<elements>
<exampleElement name="ExampleElement1">
<targets>
<exampleElementMoniker name="/cbe83dd6-007a-48dd-9c21-b0859208c1a7/ExampleElement2" />
</targets>
</exampleElement>
<exampleElement name="ExampleElement2" />
</elements>
</exampleModel>
Nota: para ver el contenido del archivo, cerramos el mismo y seleccionamos la opción Open With // Xml Editor.
9. Cerramos en proyecto Debugging y volvemos a la solucion original.
Creación del Custom Editor o visor personalizado
10. En este momento, sobreescribiremos el visor por defecto que brinda Visual Studio por un formulario que creemos. Ya que nuestro DSL es muy simple, simplemente compuesto por elementos, es posible pensar en una visualización del mismo en una lista y en la misma agregar una serie de botones que permitan agregar, modificar y eliminar elementos.
Para esto en el proyecto DslPackage agregamos un nuevo UserControl llamado DslCustomEditor y dentro del mismo agregamos un Listbox, un TextBox y un Button, como muestra la siguiente imagen:
Nota: nuestro UserControl se ajustará como una nueva ventana de Visual Studio por lo que he ajustado los anchors de los controles para que respeten el aspecto del mismo cuando se redimensiona el UserControl.
11. A continuación agregamos el código necesario para completar el ListBox con los elementos del DSL, y para agregar un nuevo elemento. Además es necesario que nuestro UserControl reciba una instancia del visor original para poder gestionar desde el mismo las colecciones de objetos que maneja nuestro DSL. Este es el código final de nuestro UserControl:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualStudio.Modeling;
namespace ElBruno.DSL10
{
public partial class DslCustomEditor : UserControl
{
private DSL10DocView dv;
/// <summary>
/// Initializes a new instance of the <see cref="DslCustomEditor"/> class.
/// </summary>
/// <param name="dv">The DocView that manages the DSL information</param>
internal DslCustomEditor(DSL10DocView dv)
{
InitializeComponent();
this.dv = dv;
}
private void btnAddElement_Click(object sender, EventArgs e)
{
// valida el contenido de la caja de texto
if (txtElement.Text.Length == 0) return;
// inicializa una transaccion y agrega el nuevo elemento
using (Transaction tx = this.dv.DocData.Store.TransactionManager.BeginTransaction("Agregar Elemento"))
{
ExampleElement newElem = new ExampleElement(this.dv.DocData.Store);
newElem.Name = txtElement.Text;
ExampleModel em = this.dv.DocData.RootElement as ExampleModel;
em.Elements.Add(newElem);
tx.Commit();
}
// refresca el contenido del listbox
RefreshElements();
}
private void RefreshElements()
{
this.SuspendLayout();
// limpia el listbox
lstElements.Items.Clear();
// obtiene y recorre todos los elementos del tipo <ExampleElement>
foreach (ExampleElement ee in this.dv.DocData.Store.ElementDirectory.FindElements<ExampleElement>())
{
lstElements.Items.Add(ee.Name);
}
this.ResumeLayout();
}
private void DslCustomEditor_Load(object sender, EventArgs e)
{
RefreshElements();
}
}
}
12. Una vez que hemos finalizado nuestro editor por defecto para nuestro DSL, necesitamos eliminar el Editor que se crea por defecto en el proyecto. Para esto, dentro del proyecto Dsl, en el panel Dsl Explorer seleccionamos el nodo Editor y eliminamos el editor actual.
13. Una vez eliminado si grabamos o queremos compilar veremos el siguiente error:
Error 2 Dsl DSL10 has no Editor. C:\Projects\Labs2005\DSL10\Dsl\DslDefinition.dsl 0 1 Dsl
Si queremos compilar y ejecutar, también veremos una serie de errores relacionados con el hecho que no hay un editor para nuestro DSL.
14. Para agregar un editor personalizado, en el panel DSL Explorer desplegamos el menú contextual y seleccionamos la opción Add New Custom Editor.
15. Una vez que hemos creado el nuevo Custom Editor, seleccionamos el nodo Editor (Custom Editor) y completamos las siguientes propiedades.
File Extension: dsl 10
Root Class: ExampleModel
16. Como hemos cambiado parte de la información relativa al DSL necesitamos regenerar el codigo a partir del mismo desde el panel Solution Explorer y compilar la solucion nuevamente. Luego de la compilación nos encontraremos con sl siguiente error:
Error 1 'ElBruno.DSL10.DSL10DocView' does not implement inherited abstract member 'Microsoft.VisualStudio.Shell.WindowPane.Window.get' C:\Projects\Labs2005\DSL10\DslPackage\GeneratedCode\DocView.cs 20 25 DslPackage
17. Para solucionarlo debemos editar el archivo DslPackage // GeneratedCode // DocView.tt // DocView.cs y agregar el siguiente código:
/// <summary>
/// Representa el visor personalizado para editar el contenido del DSL
/// </summary>
private DslCustomEditor customEditor;
/// <summary>
/// Gets the window associated with this window pane.
/// </summary>
/// <value></value>
/// <returns><see cref="T:System.Windows.Forms.IWin32Window"></see>.</returns>
public override System.Windows.Forms.IWin32Window Window
{
get
{
// verifica si existe una instancia del visor y la crea si es necesario
if (customEditor == null)
customEditor = new DslCustomEditor(this);
return customEditor;
}
}
Nota: el archivo DocView.cs se genera automáticamente cuando seleccionamos la opción para regenerar el código, por lo que este cambio deberemos realizarlo luego de cada generación o implementarlo en una clase parcial.
18. Si compilamos y lanzamos el proyecto de debug veremos que aparece nuestro UserControl como el editor para los dsls y que podremos agregar informacion dentro del mismo:
19. Si cerramos el documento, grabando los cambios del mismo; podremos ver que dentro de la información que posee el DSL se encuentra la información que hemos editado con el Custom Editor:
<?xml version="1.0" encoding="utf-8"?>
<exampleModel dslVersion="1.0.0.0" Id="cbe83dd6-007a-48dd-9c21-b0859208c1a7" xmlns="http://schemas.microsoft.com/dsltools/DSL10">
<elements>
<exampleElement name="ExampleElement1">
<targets>
<exampleElementMoniker name="/cbe83dd6-007a-48dd-9c21-b0859208c1a7/ExampleElement2" />
</targets>
</exampleElement>
<exampleElement name="ExampleElement2" />
<exampleElement name="Nuevo Elemento" />
<exampleElement name="Otro Elemento Mas" />
<exampleElement name="Mas Elementos" />
</elements>
</exampleModel>
Conclusión
Existe un proyecto en CodePlex "DSL Editor Powertoy" que nos permite agilizar muy poco el proceso de cambiar los editores por defecto para nuestros DSL. Pero creo que es interesante conocer un poco más en detalle como se realiza este tipo de trabajo ya que cuando los DSLs comienzan a madurar, es muy probable que necesitemos saltar de una interfaz visual o gráfica a una más potente basada en listas, treeviews, etc.
El ejemplo completo se puede bajar desde aquí (gracias Geeks.ms una vez más).
Por ahora es todo ... mañana un poco sobre como funciona el IDE en modo debugging
Saludos
El Bruno
Crossposting from
ElBruno.com