• Introducción a Composite UI Application Block (CAB) III

    Published by mconverti on September 3rd, 2007 7:01 pm under CAB

    2 Comments

    Módulos

    Componentes de cada módulo

    En una aplicación CAB un módulo es una unidad de desarrollo y despliegue. CAB provee la posibilidad de cargar los módulos teniendo en cuenta el tipo de usuario que utiliza la aplicación. Esto permite presentar una interfaz y comportamiento distinto dependiendo del rol del usuario.

    Los módulos generalmente están compuestos por:

    • WorkItems: Cada módulo debe poseer uno o más WorkItems. Estas clases heredan de la clase WorkItem de CAB y se construyen a partir del RootWorkItem. Son usadas para construir y albergar a todos los objetos que el módulo necesite, agregar servicios y acceder a ellos. El WorkItem es el único que tiene acceso a todos los componentes del módulo.
    • SmartParts: Son las partes visuales que agregará el módulo a la aplicación. Se implementan mediante clases que heredan de UserControl y presentan el atributo SmartPart. Mantienen una referencia a su controlador o presenter.
    • Servicios: El módulo puede agregar sus propios servicios. Dependiendo en qué WorkItem se los agregue estos pueden estar disponibles para todos los módulos o sólo para el que los creó.
    • Business Entities: Son las clases propias del modelo de negocio que la aplicación utilizará para implementar su comportamiento.

    ProfileCatalog

    Composite UI Application Block provee un servicio para la carga de los módulos durante la etapa de inicialización de la aplicación. Utiliza un archivo XML como catálogo para determinar qué módulos se van cargar. Su nombre por defecto es ProfileCatalog.xml y debe estar ubicado en la carpeta principal de la aplicación (donde está el ejecutable). Dentro de este archivo se especifican todos los módulos de la aplicación y cuales son los usuarios que tendrán acceso a ellos. Si no se especifica ningún rol de usuario todos los módulos presentes en el archivo se cargarán.

    A continuación se muestra el formato del archivo ProfileCatalog.xml.

    XML

    sdfasd

    <?xml version=”1.0″ encoding=”utf-8″ ?>

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

    <Modules>

    <ModuleInfo AssemblyFile=”Module1.dll”>

    <Roles>

    <Role Allow=”Users”/>

    </Roles>

    </ModuleInfo>

    <ModuleInfo AssemblyFile=”Module2.dll”>

    <Roles>

    <Role Allow=”Administrators”/>

    </Roles>

    </ModuleInfo>

    </Modules>

    </SolutionProfile>

    En el ejemplo anterior se especifican dos módulos y el rol del usuario necesario para que estos se carguen.

    Dependencias

    Durante el desarrollo de la aplicación puede darse el caso de tener módulos independientes y dependientes. Cuando un módulo posee una dependencia hacia otro módulo esta se debe indicar a CAB mediante el atributo ModuleDependency. Este atributo afecta la secuencia de carga asegurando que primero se cargarán todos los módulos de los cuales se depende.

    La siguiente línea de código muestra como se implementa esto. Se la debe colocar en el archivo AssemblyInfo.cs/vb del módulo que tiene la dependencia (cambiar MyModule por el nombre del módulo del cual depende). Antes de hacer esto asegurarse que el proyecto tenga referencias a los assemblies Microsoft.Practices.CompositeUI.dll y Microsoft.Practices.ObjectBuilder.dll.

    [C#]

    [assembly: Microsoft.Practices.CompositeUI.ModuleDependency("MyModule")]

    [Visual Basic]

    <Assembly: Microsoft.Practices.CompositeUI.ModuleDependency(“MyModule”)>

    Para que funcione el código anterior se debe agregar en el archivo AssemblyInfo.cs/vb del módulo del cual se depende la siguiente línea:

    [C#]

    [assembly: Microsoft.Practices.CompositeUI.Module("MyModule")]

    [Visual Basic]

    <Assembly: Microsoft.Practices.CompositeUI.Module(“MyModule“)>

    Esto permite establecerle un nombre al módulo para que los otros lo puedan identificar.

    Nota: En Smart Client Software Factory, se pueden especificar las dependencias entre los módulos directamente desde el Profile Catalog.

    Proceso de carga

    Cuando CAB carga un módulo usa reflection para saber si el assembly contiene una clase que implementa la interfaz IModule. En general en vez de implementar esa interfaz, se hereda de la clase abstracta ModuleInit (que ya implementa la interfaz anterior) y se sobrescriben los métodos Load() y AddServices().

    En el método Load() generalmente se inicializa el WorkItem principal del módulo y se lo ejecuta llamando a su método Run(), y en el método AddServices() se agregan al RootWorkItem los servicios del módulo (esto se verá en más detalle en los próximos posts). El primero en ejecutarse es el método AddServices() y luego Load().

    A continuación se muestra un ejemplo de cómo construir esta clase:

    [C#]

    using Microsoft.Practices.ObjectBuilder;

    using Microsoft.Practices.CompositeUI;

    // namespace donde se encuentran los servicios en el ejemplo.

    using ExampleModule.Services;

    namespace ExampleModule

    {

    public class ExampleModuleInit : ModuleInit

    {

    private WorkItem _rootWorkItem;

    [InjectionConstructor]

    public ExampleModuleInit([ServiceDependency] WorkItem rootWorkItem)

    {

    _rootWorkItem = rootWorkItem;

    }

    public override void AddServices()

    {

    base.AddServices();

    _rootWorkItem.Services.AddNew();

    }

    public override void Load()

    {

    ExampleWorkItem workitem = _rootWorkItem.WorkItems.AddNew();

    workitem.Run();

    }

    }

    }

    [Visual Basic]

    Imports Microsoft.Practices.CompositeUI

    Imports Microsoft.Practices.ObjectBuilder

    ‘ namespace donde se encuentran los servicios en el ejemplo.

    Imports ExampleModule.Services

    Public Class ExampleModuleInit

    Inherits ModuleInit

    Private _rootWorkItem As WorkItem

    <InjectionConstructor()> _

    Public Sub New(<ServiceDependency()> ByVal rootWorkItem As WorkItem)

    _rootWorkItem = rootWorkItem

    End Sub

    Public Overrides Sub AddServices()

    MyBase.AddServices()

    _rootWorkItem.Services.AddNew(Of Service, IService)()

    End Sub

    Public Overrides Sub Load()

    MyBase.Load()

    Dim workItem As ExampleWorkItem = _rootWorkItem.WorkItems.AddNew(Of ExampleWorkItem)()

    workItem.Run()

    End Sub

    End Class

    La clase ExampleModuleInit posee una referencia al RootWorkItem la cual se inyecta en el constructor. El atributo InjectionConstructor le indica al ObjectBuilder que deberá usar ese constructor en el momento de instanciar la clase y el atributo ServiceDependency le informa que se debe inyectar una dependencia en el parámetro del constructor.

    El método AddServices() agrega un servicio que implementa la interfaz IService al contenedor de servicios del RootWorkItem.

    En el método Load() se crea una instancia de la clase ExampleWorkItem (que representa al WorkIem principal del módulo) y se ejecuta el método Run(). La clase ExampleWorkItem deberá sobrescribir el método OnRunStarted() para agregar el código que se encargará de crear las vistas del módulo y cargarlas en el Workspace correspondiente. A continuación se muestra un ejemplo de cómo implementar esta clase:

    [C#]

    using Microsoft.Practices.CompositeUI;

    using Microsoft.Practices.CompositeUI.SmartParts;

    // namespace donde se encuentran las vistas en el ejemplo.

    using ExampleModule.Views;

    namespace ExampleModule

    {

    public class ExampleWorkItem : WorkItem

    {

    protected override void OnRunStarted()

    {

    AddViews();

    base.OnRunStarted();

    }

    private void AddViews()

    {

    IWorkspace workspace = Workspaces["MainWorkspace"];

    workspace.Show(Items.AddNew<UserControl1>());

    }

    }

    }

    [Visual Basic]

    Imports Microsoft.Practices.CompositeUI

    Imports Microsoft.Practices.CompositeUI.SmartParts

    ‘ namespace donde se encuentran las vistas en el ejemplo.

    Imports ExampleModule.Views

    Public Class ExampleWorkItem

    Inherits WorkItem

    Protected Overrides Sub OnRunStarted()

    AddViews()

    MyBase.OnRunStarted()

    End Sub

    Public Sub AddViews()

    Dim workspace As IWorkspace = Workspaces(“MainWorkspace”)

    workspace.Show(Items.AddNew(Of UserControl1)())

    End Sub

    End Class

    Al ejecutar el método Run() de la clase ExampleWorkItem, se invocará el método sobrescrito OnRunStarted(). Este método crea una vista de tipo UserControl1 y la muestra en el Workspace del shell identificado con el nombre “MainWorkspace”.

    En el próximo post se explicará el patrón Model-View-Presenter (MVP) y se dará un ejemplo de cómo aplicarlo en CAB.

  • 2 Comments:

    1. lucasontivero said on September 14, 2007:

      Hola Mariano:
      Veo que me has pasado en las búsquedas de google con “Composite UI Application Block” :(
      jajaja. La verdad es que me alegro mucho. Estas haciendo un buen trabajo y este artículo está mejor que los anteriores!
      Te mando un saludo y dale masa.

    2. http:// said on September 17, 2007:

      Muchas gracias por tus comentarios Lucas, veo que estuviste leyendo mis artículos.
      Espero que sigas leyendo los próximos y me sigas dando tu opinión.
      Saludos…

    Leave a comment

    Your email address will not be published.