DSL con diseñador WPF

 

En los últimos años la comunidad NetFxFactory ha venido desarrollando artículos muy influyentes relacionados con las DSL Tools de Microsoft. Han aportado excelentes soluciones en temas como el multidiagrama o el uso de patrones en modelos.

En Septiembre de 2009, publicaron “Provide an improved user experience to your DSL’s” en el que anticipaban una de las principales características  incorporadas en la versión de DSL Tools de 2010: los diseñadores basados en WPF; dando una primera aproximación para Visual Studio 2008. Una vez lanzado Visual Studio 2010 publicaron “Magnify your diagram”, artículo en el que migraban su prototipo de 2008 haciendo uso de todas las características lanzadas en la nueva versión de DSL Tools.  

Como bien dice Pascal Recchia, autor de estos artículos, a pesar de que entre estas nuevas características no se propone ninguna mejora directa sobre los mecanismos de layout de los diseñadores generados (que supone, sin lugar a dudas, un de lde la herramienta) se puede entrever que la estrategia que se quiere seguir para afrontarlos es precisamente el uso de WPF.

En el ejemplo que se propone en dichos artículos se desarrolla un DSL para modelar una red de aeropuertos y vuelos entre aeropuertos.

image

 

 

Un ejemplo bastante simple y “específico del dominio”. Indudablemente de eso se trata. No obstante, si lo que queremos es desarrollar un diseñador basado en WPF algo más completo y genérico, que nos sirva de referencia tanto para crear un diseñador de clases, como para un entidad-relación o un CUIP, la cosa se nos queda algo corta.

Tampoco nos sirve de mucho la plantilla de DSL con diseñador WPF que viene incluida en la instalación de las DSL Tools de VS2010.

 

image

 

En este caso, el diseñador que se genera desde nuestro metamodelo es, en efecto, un formulario WPF… pero bastante discreto:

 

image

 

 

 

Por tanto, para poder empezar a crear DSLs “con sustancia” usando esta prometedora característica, hay que hacerse con un buen ejemplo de diseñadores de diagramas en WPF.

En CodeProject hay una serie de cuatro artículos, con su correspondiente código, que desgranan paso a paso el desarrollo de un diseñador de diagramas basado en WPF. Si descargamos el código de ejemplo podemos ejecutar la aplicación y veremos algo como esto:

 

 

image

 

Nos decidimos a integrar estos ejemplos de CodeProject sobre un pequeño DSL de entidades con atributos generado con las DSL Tools.

A la hora de crearlo seleccionamos la plantilla “Minimal Language”.

 

image

 

 ¿Y por qué no usar la plantilla “Minimal WPF Designer” si lo que queremos es desarrollar un diseñador WPF? Pues porque con la plantilla WPF no se nos permite definir, y por tanto generar, desde el metamodelo, lo relativo al diseñador y al mapeo entre sus elementos y los correspondientes elementos del modelo. Esto merece una explicación un poco más en profundidad.

Como vemos en la siguiente imagen, un metamodelo en DSL Tools está representado por dos partes: Elementos del modelo (Classes and relationships) y Elementos del diagrama (Diagram Elements).

 

image

 

Mientras los primeros definen el dominio: entidades y atributos en nuestro ejemplo, aeropuertos y vuelos en el de NetFxFactory; los segundos definen la representación en un diseñador de cada una de las instancias de los elementos de ese dominio, tanto en forma visual, como en posición, tamaño o Zindex. Los pequeños conectores que relacionan ambas partes marcan el mapeo entre los elementos de las respectivas regiones, es decir, qué elemento de diseñador representa a cada elemento del dominio.

Con la plantilla de diseñador de WPF por defecto, que es la que se usa en el ejemplo de NetFxFactory, la parte de diseñador esta deshabilitada, y no se nos permite mapear elementos de diseñador a elementos de dominio. Esto implica que nuestro modelo sólo mantendrá información sobre los elementos del dominio y no de cómo se representan los mismos en el diseñador. Asimismo el mecanismo de persistencia de esta información de diseñador queda deshabilitada, es decir, no tendremos el archivo “.diagram” en nuestro modelo y, por tanto, no se guardarán, entre otras cosas, las posiciones de los elementos en el diseñador.

Por todas estas cosas utilizamos la plantilla “Minimal Language”  y creamos nuestro DSL de entidades (Podéis ver el metamodelo en la imagen anterior).

A la solución creada le agregamos el proyecto que comentamos de los artículos de CodeProject. En este caso usamos el código que acompaña al artículo 4. Previamente lo convertimos en biblioteca de clases y eliminamos el App.xaml que ya no necesitaremos.

 

image

 

 

En este punto, si ejecutamos la solución actual, obviamente vemos el diseñador generado por defecto, basado en WinForms:

 

 

image

 

Para usar nuestro nuevo diseñador, basado en WPF, tenemos que añadir algo de código “custom”. La responsabilidad del diseñador recae sobre tres objetos: EditorFactory, DocData y DocView (se encuentran en la carpeta GeneratedCode del proyecto DslPackage). El objeto DocView corresponde a la interfaz del diseñador, el DocData contiene la representación en memoria del documento y maneja el guardado y cargado del mismo, mientras el EditorFactory es el encargado de construir objetos de las dos anteriores clases.

En este caso creamos una clase parcial del DocView para directamente cambiar la interfaz del diseñador al Designer de WPF.

 

image

 

 

En la clase parcial básicamente sobrescribimos la carga de la vista y su contenido para albergar una instancia del WPF Designer.

        public override object Content

        {

            get

            {

                if (content == null)

                {

                    content = this.CreateControl();

                }

                return content;

            }

        }

 

        protected Wpf::ContentControl WpfViewControl

        {

            get

            {

                if (this.Content != null)

                {

                    return this.Content as Wpf::ContentControl;

                }

                else

                {

                    return null;

                }

            }

        }

            

        protected global::System.Windows.UIElement CreateControl()

        {

            return new DiagramDesigner.Designer();

        }

 

        protected override bool LoadView()

        {

            base.LoadView();

 

            global::System.Diagnostics.Debug.Assert(this.DocData.RootElement != null);

 

            bool returnValue = false;

 

            if (this.DocData.RootElement != null)

            {

                returnValue = true;

                WpfViewControl.DataContext = this.DocData.RootElement;

                SelectionPusher.Add(WpfViewControl);

            }

            return returnValue;

        }

 

 

 

 

Podemos comprobar el resultado si ejecutamos la instancia experimental y observamos el proyecto Debugging. Queda curioso, pero realmente aún no sirve para nada.

 

image

 

 

Para sacarle partido hay que meterse de lleno en el XAML de nuestro Designer y darle un par de vueltas. Pero por esto pasaré a muy alto nivel, que para enseñar XAML ya hay blogs mejores que éste. La base del diseñador la podemos ver en el siguiente esquema:

 

image

 

 

La parte azulada corresponde a la interfaz del diseñador, mientras la parte anaranjada corresponde a la infraestructura generada por las DSL Tools que lo sustenta.

El resultado es un DSL totalmente funcional con un diseñador en WPF y del que, por ejemplo, podemos generar código mediante plantillas T4:

 

 

image

image

 

 

El siguiente paso sería sacar factor común y obtener un conjunto de clases base más un conjunto de plantillas para que, dada una definición de un metamodelo cualquiera en un archivo DslDefinition, obtengamos el correspondiente diseñador basado en WPF automáticamente, tal y como se hace actualmente con los diseñadores basados en WinForms.

Este creo que es el camino en el que se están centrando desde NetFxFactory… estaremos atentos a sus resultados!

Un comentario en “DSL con diseñador WPF”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *