Feb
28

Con el fin de poner en práctica y adquirir experiencia sobre las cosas que fui aprendiendo en mi entrenamiento, me sugirieron que reescriba mi aplicación AjGenesisStudio usando Smart Client Software Factory, lo cual me pareció una buena idea y una oportunidad para lanzar una nueva versión con algo más de futuro, con esto me refiero a que la aplicación será más fácil de mantener y de añadir nuevas funcionalidades, cualquier persona que tenga algún conocimiento de SCSF podrá escribir nuevos módulos fácilmente.

AjGenesisStudio es un IDE que escribí hace unos meses que sirve para trabajar con el generador de código de Angel Lopez: “AjGenesis“, se podría pensar como algo similar al generador “MyGeneration” pero con las ventajas que ofrece el generador AjGenesis (y sin IntelliSense por ahora).

Requerimientos

AjGenesisStudio es un IDE inspirado en Visual Studio por lo cual su apariencia y funciones serán muy similares.

  • Poder abrir y editar cualquier tipo de archivo de texto,
  • Debe permitir realizar todas las funciones de edición de texto conocidas (copiar, pegar, buscar, reemplazar, etc.),
  • Resaltado de sintaxis para la mayor cantidad posible de archivos (XML, VB, CS, HTML. etc.),
  • Ofrecer resaltado de sintaxis en las plantillas de AjGenesis tanto para el código AjBasic como para el texto de salida (similar a ASP.net).
  • Poder abrir carpetas de proyectos de generación y mostrar los archivos en un Explorador de Proyecto en forma de árbol (similar al Explorador de Soluciones de Visual Studio).
  • Ejecutar archivos .build de NAnt con un botón (Run).
  • Administración de herramientas externas (similar al de Visual Studio).
  • Poder crear nuevos archivos y proyectos a partir de plantillas (igual que VS).

Para el resaltado de sintaxis y la interfaz tipo “docking” se emplea el excelente componente open source “DotNetFireball“.

Diseño de la Shell

En esta etapa ya tengo diseñado y codificado la shell y los demás elementos y servicios de infraestructura.

El layout principal tiene un menú principal (MainMenu) el cuál debe ser extendido usando el UIExtensionSite “MenuExtension” (los nuevos menús se insertarán entre el menú View y Tools), la barra del menú principal ya posee ítems que son genéricos y que todos los módulos pueden extender (ver imagen). Luego posee una barra de herramientas (MainToolbar) y una barra de estado (MainStatus).

ajgsshell

El shell tiene únicamente un Workspace: MainWorkspace, el cual es una implementación de IComposableWorkspace basado en el componente DockContainer de DotNetFireball. Este workspace usa un SmartPartInfo (DockableWindowSmartPartInfo) cuyo propósito es configurar el estado de la ventana (anclado, flotante, auto-ocultable, abajo, arriba, izquierda, derecha, etc). Este workspace se encuentra en el assembly Infrastructure.UI y deberá ser referenciado por los módulos de negocio que deseen mostrar vistas.

El menú archivo (FileMenu) puede ser extendido en 4 lugares:filemenu

  • FileNewMenu: ítems para crear un nuevo elemento (ej. módulo Documento: “Nuevo Archivo”, módulo Proyecto: “Nuevo Proyecto”).
  • FileOpenMenu: ítems para abrir elementos (ej. Abrir Archivo, Abrir Proyecto).
  • FileAddMenu: ítems que se pueden añadir a un proyecto (ej. Nuevo Xml, Nuevo Template).
  • FileMenuExtension: los módulos deben añadir ítems al menu archivo a travez de este UIExtensionSite (en la imagen se ven ítems añadidos por el módulo Documento).
¿Por qué MenuExtension y FileMenuExtension?

¿Por qué para extender el menú principal y el menú File hay que extender los elementos MenuExtension y FileMenuExtension respectivamente en lugar de extender directamente MainMenu o FileMenu? Esto ocurre porque se desea que los nuevos elementos se agreguen no al final si no en una posición determinada (entre el menú View y Tools para MainMenu y antes del Exit para FileMenu) y para lograr esto hay que usar un pequeño truco que consiste en insertar al final (luego de agregar los ítems básicos) un elemento no visible al menú en la posición deseada para que luego, los siguientes elementos sean agregados en la misma posición, pero hay que prestar atención al orden en que se agregan los nuevos elementos ya que serán añadidos por encima de los anteriores (al revés de lo normal). Pueden encontrar más información en el blog donde encontré la solución original: Workaround for Injecting menu items into CAB Shell MenuStrip.

Funciones de edición

Las opciones para edición se deben tratar de una forma particular para cumplir los siguientes dos requerimientos: cada botón (del MainToolbar) o ítem del menú (EditMenu) debe ser habilitado o deshabilitado según se pueda o no realizar la operación (ej. Paste sólo cuando hay algo en el clipboard), y cada función debe poder ser implementada por varios módulos (ej. el módulo Document para la edición del texto y el módulo Proyecto para la edición de los archivos y carpetas). Esto se puede resolver empleando comandos para cada función de edición asociados a los ítems y que estos comandos invoquen un evento individual para cada uno, de esta manera los módulos subscriben a los eventos y son responsables de ejecutar la operación de edición siempre que el usuario haya puesto el foco sobre la vista del módulo en cuestión. Los módulos también son responsables de actualizar el estado de activación de cada comando cada vez que alguna vista recibe el enfoque.

Los eventos a los que deben suscribirse los módulos que necesitan realizar operaciones de edición son los siguientes: EditCopy, EditCut, EditPaste, EditSelectAll, EditDelete, EditUndo, EditRedo, ShowFind, ShowReplace, ShowGoTo. Los comandos, que deben ser utilizados sólo para la activación de los items, tienen el mismo nombre (las constantes, el valor no es el mismo).

Servicios de infraestructura

Por ahora tengo pensados 2 servicios de infraestructura, el primero, “ExtensionService“, tiene funciones para facilitar la extensión de los menú y las barras de herramienta. El otro, “LoggingService“, permite mostrar al usuario información de la ejecución de las operaciones, este servicio publica un evento al que deberá suscribirse el módulo que se encargue de mostrar resultados.

En los siguientes posts seguiré con el desarrollo de los módulos que compondrán la aplicación.

Feb
13
Filed Under (CAB, Patterns & Practices) by jcisneros on 13-02-2008

Ya en mi segunda semana en Southworks he estudiado y aprendido mucho sobre Composite UI Application Block (CAB) y Smart Client Software Factory (SCSF) como parte de mi entrenamiento. En este post voy a resumir parte de lo que he aprendido acerca de CAB con el propósito de que le sirva a alguien más como introducción al tema. Sé que CAB es difícil de aprender debido a que muy pocos están acostumbrados a trabajar con todos los patrones y la arquitectura que CAB propone y por esto trataré de explicar los aspectos fundamentales del mismo en la forma más clara que pueda. En próximos posts seguiré con SCSF y WCSF.

Qué es CAB

CAB es un Application Block creada por el equipo de Patterns & Practices de Microsoft y nos ofrece una librería de clases que nos ayudan a implementar un conjunto de patrones de diseño orientados principalmente a la presentación. CAB nos permite crear aplicaciones Windows denominadas “Smart Client”. Una de las características más sobresalientes de CAB es que las aplicaciones resultantes son modularizadas y con muy poco acoplamiento entre los módulos, esto es logrado básicamente por el extenso uso que hace CAB (con su librería ObjectBuilder) de la inyección de dependencias.

Es importante aclarar que el desarrollo de aplicaciones CAB debería ser realizado partiendo de un diagrama de casos de uso ya que los módulos que compongan la aplicación y sus elementos deberán estructurase según estos. Dentro de cada módulo existen elementos, que describiré más adelante, que implementan los casos de uso casi en una relación uno a uno. Estos elementos y sus casos de uso serán agrupados dentro de los módulos según criterios de seguridad, configuración y reusabilidad.

¿Qué se logra al emplear CAB?

Las aplicaciones creadas usando CAB se destacan por las siguientes características:

  • Módulos reusables y con muy bajo acoplamiento,
  • facilidad para el desarrollo en equipos (cada equipo desarrolla un módulo sin necesidad de estar al tanto de los demás),
  • implementación de patrones de diseño y prácticas probadas y recomendadas (M.V.P., Command, Event Brocker, etc.),
  • separación de conceptos (cada módulo tiene bien definida su parte de presentación visual, lógica del dominio de la aplicación y los servicios de infraestructura),
  • mantenimiento y extensibilidad (se pueden añadir nuevas funcionalidades o modificar las existente sin mayores inconvenientes. Se pueden remplazar módulos, vistas, implementaciones, etc. sin tener que adaptar los demás componentes),
  • “Testeabilidad” (debido a que la vista está separada de la lógica que implementa es muy fácil crear pruebas de unidad automatizadas que validen los Controllers y Presenters).

Estructura de una aplicación CAB

Todas las aplicaciones CAB poseen un proyecto central denominado Shell Application. Este es un proyecto Windows Forms que contiene tres elementos importantes: el primero es el formulario principal (llamado Shell) que contiene el Layout en el cual se mostrarán los componentes visuales denominados SmartParts que no son más que controles de usuario, pero no todos los SmartParts deberán ser desplegados en el Shell, los SmartParts a su vez también pueden mostrar otros SmartParts de otros módulos, pero estos no pueden ser desplegados dentro de cualquier tipo de control, existen unos contenedores especializados que se denominan Workspaces los cuales despliegan los SmartParts en distintas disposiciones.

cabshell

El otro elemento importante del Shell Application es el WorkItem raíz, los WorkItems son clases que contienen lógica del negocio (ej. de un determinado caso de uso) y que a su vez contienen colecciones de otros WorkItems creando una estructura jerárquica (esta estructura es importante para comprender el alcance de la disponibilidad de los servicios y demás elementos que serán compartidos), pero también pueden contener colecciones de Servicios, Comandos, SmartParts, y otros ítems. Los WorkItems deben implementarse heredando de la clase WorkItem pero no es necesario en la aplicación Shell, se puede emplear directamente la misma clase.

El último elemento es un archivo de configuración llamado ProfileCatalog.xml en el cual se especifican los módulos que deberán ser cargados a la aplicación.

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

<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile" >

<Modules>

<ModuleInfo AssemblyFile="ModuloA.dll" />

<ModuleInfo AssemblyFile="ModuloB.dll" />

</Modules>

</SolutionProfile>

Luego la solución deberá componerse de un conjunto de Módulos que implementarán la lógica de los casos de uso y las funciones de infraestructura como acceso de servicios web, acceso a datos, etc.

Los módulos son proyectos de librería de clases que pueden contener: servicios que exponen funcionalidades, WorkItems, vistas (SmartParts) con sus respectivos Presenters o Controllers, y por último todos los módulos deberán contener una clase que herede de ModuleInit, esta clase es el punto de entrada al módulo (al cargar un módulo CAB buscará esta clase) y es la encargada de las tareas de la inicialización y configuración inicial del módulo.

Características importantes

Los elementos que detallaré a continuación son algunas características de CAB que permiten la comunicación entre los componentes de la aplicación pero manteniendo un bajo acoplamiento entre los mismos.

Workspaces

Como se describió anteriormente un Workspace es un control que permite desplegar en el mismo los elementos visuales denominados SmartParts. En esta sección lo que se pretende aclarar es que cada WorkSpace tiene un nombre asignado por medio del cual los demás elementos pueden hacer referencia al mismo y así poder desplegar los SmartParts. Los WorkItems poseen una colección Workspaces por medio del cual acceden a los Workspaces. Esta los valores de esta colección es inyectada por CAB.

UIExtensionSites

Un UIExtensionSite es una colección de sitios donde se pueden agregar, quitar, habilitar y deshabilitar dinámicamente ítems de un componente visual como ser menús, botoneras, etc.. Cada elemento del UIExtensionSite es un componente visual fijo que no será modificado salvo por sus elementos hijos (submenús, botones) y estos sitios son referenciados por un nombre (ej. “MainMenu”). Esto puede ser muy útil por ejemplo cuando un módulo necesita añadir una nueva opción en el menú principal de la aplicación para invocar la funcionalidad que el módulo añade.

Ejemplo para registrar un UIExtensionSite:

RootWorkItem.UIExtensionSites.RegisterSite(“FileMenu”, Shell.MainMenuStrip);

Ejemplo para añadir un item a un UIExtensionSite:

ToolStripMenuItem printItem = new ToolStripMenuItem(”Print”);

RootWorkItem.UIExtensionSites[“FileMenu”].Add(printItem);

Publicación y subscripción de eventos

La publicación de eventos es un mecanismo que nos permite publicar eventos y subscribirse a los mismos, de esta forma no existe un acoplamiento directo entre la declaración del evento y su handler pudiéndose crear y quitar eventos sin necesidad de modificar sus manejadores. Cada evento es identificado por una especie de URI donde se define el tema y el nombre del evento.

Ejemplo de publicación de evento:

[EventPublication("topic://BankShell/statusupdate", PublicationScope.Global)]

public event EventHandler<DataEventArgs> UpdateStatusTextEvent;

Ejemplo de subscripción a un evento:

[EventSubscription("topic://BankShell/statusupdate", Thread = ThreadOption.UserInterface)]

public void OnStatusUpdate(object sender, DataEventArgs e)

{

toolStripStatusLabel1.Text = e.Data;

}

Comandos

Es muy común que las aplicaciones contengan más de un modo de ejecutar una determinada función (ej. menú Edición -> Cortar, Botón con ícono de tijera, menú contextual -> Cortar), los comandos nos permiten registrar métodos que podrán ser invocados desde varios lugares, esos lugares son eventos añadidos como invocadores del comando.

Ejemplo de un comando:

[CommandHandler(CommandConstants.EDIT_COPY)]

public void OnEditCopy(object sender, EventArgs args)

{

//…

}

Los comandos son implementados en los Controllers.

Ejemplo para añadir un evento a un comando:

Commands[CommandConstants.EDIT_COPY].AddInvoker(editCopyMenuItem, “Click”);

Servicios

Los servicios son clases que exponen funcionalidades a los demás componentes de la aplicación como ser Web Service Agents, logging, autenticación, etc.. Un servicio se define marcando una clase (no necesita heredar nada) con el atributo [Service], luego los servicios pueden ser inyectados por medio de una propiedad del mismo tipo marcada con el atributo [ServiceDependency] o por medio de la colección Services de un WorkItem.

Glosario CAB

A continuación se incluye un catálogo de los elementos más importantes que componen una aplicación CAB con el fin de aclarar conceptos:

Shell Application: es la aplicación principal que se inicia primero y que es responsable de la inicialización, carga de los módulos, inicializar los servicios base/globales e iniciar el formulario principal (Shell).

Shell: es el formulario principal que contiene la interfaz común a todos los módulos. Además, aloja el WorkItem raíz que será el punto de entrada para las demás partes de la aplicación.

ProfileCatalog: es un documento xml en el que se configuran los módulos y servicios que deben ser cargados.

WorkSpace: son controles que permiten alojar y desplegar los elementos de interfaz (SmartParts) que son creados por los WorkItems.

UIExtensionSite: son contenedores especiales para extender partes fijas del Shell como por ejemplo: menús, botoneras, barra de estado.

Module: un proyecto de librería de clases que encapsula un conjunto de WorkItems.

ModuleInit: es una clase que tienen todos los módulos y que sirve como punto de entrada. Este es responsable de cargar todos los servicios y WorkItems del módulo además de otras tareas de inicialización y configuración.

WorkItem: una clase que encapsula un caso de uso y mantiene todos los estados de las partes involucradas en el caso de uso. También puede ser visto como un contenedor para gestionar objetos requeridos para realizar una tarea.

Service: es una clase que encapsula funcionalidad que es común a toda o a una parte de la aplicación, módulo o jerarquía de WorkItems.

SmartPart: es un control de usuario marcado con el atributo [SmartPart] y son partes visuales de la aplicación.

Model: es una entidad del negocio del lado del cliente que es procesada por un WorkItem.

View: un control de usuario responsable de mostrar una parte del modelo al usuario. Sólo implementa lógica de UI, la lógica del negocio es implementada por el Controller o el Presenter.

Controller: una clase que implementa lógica para modificar un modelo que puede ser presentado por varias vistas.

Presenter: una clase que implementa lógica de negocio para una sola vista.

Command: clase base para implementar el patrón Command. Los comandos se declaran con el atributo [CommandHandler] y luego se le asignan los múltiples invocadores.

EventPublication: permite publicar eventos con bajo acoplamiento. Los eventos se marcan con el atributo [EventPublication] con un identificador tipo URI y luego serán notificados los suscriptores con el mismo URI.

EventSuscription: permite suscribirse a eventos publicados con EventPublication, se marcan los manejadores del evento con el atributo [EventSuscription] y con el mismo URI del evento al que suscribe.

¿Cómo usar CAB?

Actualmente existe un proyecto llamado Smart Client Software Factory el cual brinda un conjunto de herramientas que facilitan enormemente la tarea de implementar una aplicación Smart Client con CAB por medio de un conjunto de asistentes, guías, hows to, clases adaptadas, etc.. Por eso, si deseas crear una aplicación con CAB te recomiendo que sigas con el siguiente paso (SCSF) en lugar de lidiar con una implementación de CAB desde cero.

Enlaces relacionados

Todos los nuevos conocimientos que se adquieren son reforzados mediante la puesta en práctica por eso recomiendo seguir los Hands on Lab y los QuickStarts que vienen con la instalación de CAB.

Documentación oficial

CabPedia

Designing Smart Clients with the Composite UI Application Block & the Smart Client Software Factory

Excelente serie de artículos sobre CAB/SCSF