• Introducción a Composite UI Application Block (CAB) VI – Commands

    Published by on April 3rd, 2008 1:25 am under CAB

    2 Comments

    Introducción

    Las aplicaciones de Windows a menudo presentan situaciones donde más de un control realiza la misma acción. Por ejemplo un ítem de un menú y un botón de la barra de herramientas pueden ambos ejecutar el método SaveFile para guardar un archivo. Composite UI Application Block resuelve estas situaciones utilizando el concepto de comandos (Commands). Los comandos permiten, entre otras cosas, implementar un solo handler y asociarlo con más de un UIElement y/o asociar un UIElement con más de un handler. La Figura 1 muestra un escenario posible.

    UsosDeLosComandos

    Figura 1
    Dos botones diferentes que ejecutan la misma acción para el evento Click.

    Los comandos de CAB son una implementación del patrón de diseño Command. Este patrón permite desacoplar el código que ejecuta el comando, del código que lo invoca. Para entender el diseño e implementacón de este patrón en CAB, hay que tener en cuenta a las siguientes clases:

    Clase Características
    Command Es la clase que representa a un comando. Utiliza CommandAdapters para poder manejar distintos tipos de invocadores de comandos. La clase WorkItem contiene la colección Commands donde se van guardando todos los comandos creados en la aplicación. Cada comando está identificado en esta colección con un string como ID.
    CommandAdapter Es una clase abstracta que define la lógica e interfaz necesaria para que un objeto pueda ser registrado como un invocador de un comando.
    EventCommandAdapter Clase genérica que extiende a CommandAdapter. Esta implementación dispara un evento cuando el comando es lanzado.
    CommandAdapterMapService Servicio que implementa la interfaz ICommandAdapterMapService usado para realizar el mapeo entre un CommandAdapter y un determinado tipo de invoker.

    Uso de los Comandos

    Como se pudo ver en previos post, cuando un módulo se carga puede agregar ítems al Shell utilizando la colección UIExtensionSites del WorkItem. La clase WorkItem además posee la colección Commands donde se almacenan todos los comandos identificándolos con un string de ID. El siguiente código muestra cómo agregar un botón a la barra de herramientas del Shell y registrarlo como invocador del comando cuyo ID es “SaveCommand“.

    [C#]

    private void ExtendToolStrip()

    {

    ToolStripButton saveButton = new ToolStripButton(“Save”);

    RootWorkItem.UIExtensionSites[“MainToolStrip”].Add(saveButton);

    RootWorkItem.Commands[“SaveCommand”].AddInvoker(saveButton, “Click”);

    }

    [Visual Basic]

    Private Sub ExtendToolStrip()

    Dim saveButton As ToolStripButton = New ToolStripButton(“Save”)

    RootWorkItem.UIExtensionSites(“MainToolStrip”).Add(saveButton)

    RootWorkItem.Commands(“SaveCommand”).Add(saveButton, “Click”)

    End Sub

    La registración de invocadores de comandos se logra mediante el método AddInvoker de la clase Command. A continuación se muestra la declaración del método de esta clase.

    [C#]

    public virtual void AddInvoker(object invoker, string eventName);

    [Visual Basic]

    Public Overridable Sub AddInvoker(ByVal invoker As Object, ByVal eventName As String)

    El parámetro invoker es el UIElement que será invocador del comando y el parámetro eventName es el nombre del evento del invoker que disparará el comando (por ejemplo el evento “Click”). El objeto invoker debe contener entre sus atributos al evento especificado en eventName pues sino se lanzará una excepción.

    Nota:
    CAB provee soporte de CommandAdapters solo para las clases ToolStripItem y Control y sus descendientes (ver las clases ToolStripItemCommandAdapter y ControlCommandAdapter). Para poder registrar otros tipos de objetos como invocadores de comandos, será necesario implementar los correspondientes CommandAdapter y registrarlos en el servicio CommandAdapterMapService.

    Hasta ahora vimos cómo se registran los comandos. Pero ¿cómo se identifica a los métodos que queremos que se ejecuten cuando un comando es lanzado?

    Para identificar a los métodos que se ejecutarán cuando se dispare el comando se los debe marcar con el atributo [CommandHandler] especificando el ID del comando con el que cual se lo quiere asociar. El siguiente código registra al método SaveButton_Click como handler del comando “SaveCommand”.

    [C#]

    [CommandHandler(“SaveCommand”)]

    public void SaveButton_Click(object sender, EventArgs e)

    {

    // TODO: Save application’s state.

    }

    [Visual Basic]

    <CommandHandler(“SaveCommand”)> _

    Public Sub SaveButton_Click(ByVal sender As Object, ByVal e As EventArgs)

    ‘ TODO: Save application’s state.

    End Sub

    Como se puede observar en el código anterior, el identificador de visibilidad del método debe ser public.

    Para lograr el correcto funcionamiento de los comandos, la clase en donde se agregan los objetos invocadores de comandos debe ser construida a partir de un WorkItem descendiente (o el mismo) del que construyó la clase que contiene a los métodos marcados con el atributo [CommandHandler]. De lo contrario no se encontrarán los comandos requeridos en la colección Commands.

    Una última característica a tener en cuenta es que no se puede decidir qué parámetros pasar a los handlers de los comandos debido a que no se tiene control desde nuestro código de cómo y cuando estos se lanzarán. Si lo que se necesita es pasar parámetros al handler entonces es recomendable usar la funcionalidad Event Broker en lugar de comandos (este sistema se verá en más detalle en próximos post).

    ¿Cómo funciona el atributo [CommandHandler]?

    Para responder esta pregunta primero debemos introducirnos a ObjectBuilder.

    ObjectBuilder es una fábrica abstracta de objetos. CAB utiliza este framework como base para construir a todos los objetos e implementar el patrón Dependency Injection. Es por eso que para crear objetos en una aplicación CAB no se debe utilizar new, sino que se debe utilizar, por ejemplo, el método genérico AddNew.

    ObjectBuilder posee un un pipeline por donde pasan los objetos durante su etapa de construcción. Este pipeline está dividido en distintas etapas donde se van ejecutando distintas Strategies (clases que realizan una parte del proceso de construcción) y Policies (objetos usados por las Strategies para customizar sus acciones y para pasar a la siguiente en la cadena).

    ObjectBuilder define las siguientes etapas para la construcción de un objeto, las cuales suceden en el siguiente orden:

    • PreCreation
    • Creation
    • Initialization
    • PostInitialization

    CAB agrega a la cadena de Strategies la clase CommandStrategy la cual se ejecuta durante la etapa de Initialization. Esta strategy busca por reflection dentro del objeto que se está construyendo a todos los métodos marcados con el atributo [CommandHandler] y los registra en la colección Commands del WorkItem (con el ID correspondiente).

    Para más información acerca de ObjectBuilder recomiendo la lectura de los siguientes artículos (en inglés):

    Alcance de los Comandos

    Hay casos donde la disponibilidad de un comando depende del estado de la aplicación. Por ejemplo el botón Copiar de la barra de herramientas en un editor de textos estará deshabilitado si no hay ningún texto seleccionado. En una aplicación normal esto se logra modificando acordemente la propiedad Visible en cada ítem de la barra de herramientas. CAB provee una forma de realizar esta tarea indirectamente usando los comandos. Esto permite que si hay más de un UIElement que invocan al mismo comando, entonces todos se actualizarán acordemente.

    Cada comando presenta la propiedad Status a la cual se le puede asignar cualquiera de los siguientes valores:

    • Enable: El CommandAdapter deberá mostrar el UIElement.
    • Disable: El CommandAdapter deberá deshabilitar el UIElements (o una operación equivalente disponible). En general el elemento estará visible pero no disponible para su uso.
    • Unavailable: El CommandAdapter deberá ocultar el UIElements (o una operación equivalente disponible). En general el elemento no estará visible y obviamente tampoco disponible para su uso.

    Cuando el valor de esta propiedad cambia, cada CommandAdapter relacionado con el su UIElement aplica los cambios correspondientes de la manera apropiada.

    El siguiente código muestra cómo se usa esta propiedad en el comando cuyo ID es “SaveCommand”:

    [C#]

    // CommandStatus es un tipo enumerado con los posibles estados

    // de un comando.

    // Habilita al comando.

    WorkItem.Commands[“SaveCommand”].Status = CommandStatus.Enabled;

    // Deshabilita el comando.

    WorkItem.Commands[“SaveCommand”].Status = CommandStatus.Disabled;

    // Oculta y deshabilita al comando.

    WorkItem.Commands[“SaveCommand”].Status = CommandStatus.Unavailable;

    [Visual Basic]

    ‘ CommandStatus es un tipo enumerado con los posibles estados

    de un comando.

    ‘ Habilita al comando.

    WorkItem.Commands(“SaveCommand”).Status = CommandStatus.Enabled

    ‘ Deshabilita el comando.

    WorkItem.Commands(“SaveCommand”).Status = CommandStatus.Disabled

    ‘ Oculta y deshabilita al comando.

    WorkItem.Commands(“SaveCommand”).Status = CommandStatus.Unavailable

    La Figura 2 muestra una sucesión de screenshots de una aplicación donde se han aplicado los tres estados posibles al comando Save.

    500x155.aspx

    Figura 2
    La secuencia corresponde primero al comando habilitado, luego al comando deshabilitado y por último al comando oculto.

    En el próximo post explicaré en detalle cómo se implementan los servicios en CAB.

    • http://

      Muy buen post!

    • cesar

      muy buen post lastima que no seguiste!!!, esta informacion no se encuentra en castellano…

About

Mariano Converti Profile Picture
Mariano Converti

Map