Una vez tengamos el hotfix explicado en la primera entrega, seguimos con el objetivo de ver SCSF y crear una pequeña agenda de contactos, para comenzar deberemos crear un nuevo proyecto:
-
En VS2008, vamos al menú Archivo y dentro de él Nuevo y por último Proyecto..
-
En Nuevo proyecto, debemos situarnos en Tipos de Proyecto de Guidance Package y dentro Custom Smart Client Facotry Guidance Package - April 2008 [fixed for SP1]
-
Seleccionamos Smart Client Application (C#) y aceptamos
-
Nos aparece un wizard, por defecto debemos des seleccionar Create a separate module to define the layout for the shell y seleccionamos Show Documentation after recipe completes y hacemos click en finalizar.
Nos debería quedar la solución de la siguiente forma:

Comenzaremos explicando cada uno de los proyectos, que son y que sirven. Vemos que tenemos 4 proyectos dentro de la solución: Infrastructure.Interface, Infrastructure.Library, Infrastructure.Module y Shell.
En Infrastructure.Interface, contiene los elementos que deben estar expuestos a otros módulos de la aplicación. En Infraestrucutre.Module, es un componente de interfaz de usuario del módulo que actúa como contenedor para la aplicación de los elementos que se comparten en todos los proyectos de la aplicación cliente. En Infrastructure.Library tenemos la aplicación de componentes comunes utilizados por un conjunto de aplicaciones clientes inteligentes, como por ejemplo, un servicio para recuperar el perfil de un catálogo de servicios Web. Y por último, tenemos el Shell que es el elemento principal de la aplicación que se encarga de la UI y de cargar y ejecutar la aplicación.
Antes de empezar con nada, os voy a explicar la distribución de la user interface y cómo está pensada. Si nos fijamos en el proyecto del Shell, veremos que contiene un ShellForm.cs que no es nada más que la ventana principal de la aplicación. Esta contiene, de arriba hacia abajo, mainMenuStrip que será el menú principal de la aplicación, mainToolStrip que será la barra de herramientas de la aplicación, leftWorkspace que se usa para ubicar el acceso a todos los apartados de la aplicación, rightWorkspace que muestra la información solicitada des de cualquier acción de los elementos citados anteriormente y por último, mainStatusStrip para ubicar los estados de carga de ventanas, una barra de progreso... Aunque parezca sencillo, es básico saber ubicar cada elemento en su sitio para que nos quede una user interface más amigable y usable para facilitar el trabajo al usuario final.
Cómo la idea de SCSF es crear la aplicación por módulos, básicamente para que equipos grandes de desarrollo puedan trabajar de forma simultánea sobre el mismo proyecto, lo primero que haremos será añadir un módulo de negocio a nuestro proyecto, para ello añadimos una nueva carpeta dentro de source que se llame Modulos, esto es opcional y es solamente para una mejor organización de nuestro proyecto, y haremos click con el botón derecho en la nueva carpeta y en el submenu de Smart Client Factory añadiremos "Add Business Module C#" con el nombre ModuleContactos desmarcando la opción de "Create an interface library for this module" quedando así la estructura del proyecto:

Es importante leer las definiciones explicadas en la primera parte para entender los diferentes conceptos que voy a explicar. Cuándo hemos creado el modulo, además de una serie de carpetas que trataremos más adelante, podremos ver que nos ha creado dos clases.
La primera de ellas es el Module que hereda siempre de ModuleInit y será el encargado de gestionar el modulo. En el método Load podemos ver cómo crea un WorkItem que será el encargado de guiar a todo nuestro módulo y siempre tendremos uno de diferente para cada modulo creado. Y la segunda de ellas, el ModuleController que contiene los métodos necesarios para personalizar el comportamiento de nuestro modulo durante la ejecución, como por ejemplo, los menús.
Antes de seguir con nuestro modulo, hay un archivo muy importante dentro del proyecto Shell a tener en cuenta, este es el ProfileCatalog.xml que si lo editáis podréis ver que ha añadido una sección en Modules con un archivo con el nombre de nuestro modulo, así <ModuleInfo AssemblyFile="SmartClientDevelopmentSolution.ModuleContactos.dll" />. En SCSF cuándo se ejecuta nuestra solución, este va a consultar a este fichero para saber que módulos se deben inicializar que son los que van a ser usados en nuestra aplicación.
Seguimos con el modulo. La mayoría de aplicaciones basadas en Windows hacen uso de elementos de la interfaz de usuario común a través de una aplicación. Aunque shell es la que realmente contiene los menús, WorkSpace, se pueden crear instancias y acceder a estas propiedades des de nuestros módulos para adaptar la interfaz tal y como necesitemos.
Vamos a poner un menú para nuestro modulo. Nos situamos en ModuleController.cs en el método, ExtendMenu() y en él, con la clase ToolStripMenuItem crearemos el elemento principal de nuestro menú y los elementos hijos. Para ello, usaremos el constructor de la clase indicando el nombre del menú y opcionalmente el icono que se visualizará. CAB (Composite UI Application Block) tiene una gestión propia para los eventos de nuestros menús. Antes de poder gestionar estos eventos, si nos ubicamos en el directorio Constants de ModuleContactos en UIExtensionSiteNames.cs y vemos la definición (Ir a definición...) de la clase que hereda, veremos que este contiene varios ficheros con constantes que únicamente son textos,
public const string MainMenu = "MainMenu";
public const string MainToolbar = "MainToolbar";
public const string MainStatus = "MainStatus";
Esto es porqué CAB usa esas constantes para saber en qué barra de menú estamos usando o queremos usar. Hay varios archivos de constantes, UIExtensionSiteNames.cs contiene los elementos de la pantalla principal para que puedan ser extendidos, CommandNames.cs contiene los nombres de los eventos que vamos a necesitar en nuestro módulo, WorkspaceNames.cs contiene los espacios dónde vamos a ubicar nuestras vistas y EventTopicNames.cs que contiene los eventos comunes que suceden en la pantalla principal, como por ejemplo, actualizar la barra de estado. Si volvemos a nuestro método, ExtendMenu(), añadiremos un menú principal para el módulo y dos ítems más, nuestro código va a quedar así:
ToolStripMenuItem rootMenuItem;
ToolStripMenuItem itemGestContactos;
ToolStripMenuItem itemBuscContactos;
//Item principal
rootMenuItem = new ToolStripMenuItem("Contactos", (Bitmap)recursos.ResourceManager.GetObject("book_open"));
//Items hijos
itemGestContactos = new ToolStripMenuItem("Gestión contactos",(Bitmap)recursos.ResourceManager.GetObject("group"));
rootMenuItem.DropDownItems.Add(itemGestContactos);
itemBuscContactos = new ToolStripMenuItem("Buscar", (Bitmap)recursos.ResourceManager.GetObject("folder_find"));
rootMenuItem.DropDownItems.Add(itemBuscContactos);
Una vez tenemos el menú principal y también sus elementos hijos, debemos indicar a CAB dónde se va a ubicar dicho menú. Tal y cómo he dicho anteriormente, siempre nos maneja un WorkItem el cual nos proporciona el objeto necesario del Shell para modificar su presentación.
WorkItem.UIExtensionSites[UIExtensionSiteNames.MainMenu].Add(rootMenuItem);
A través del WorkItem principal del módulo accedemos a la propiedad UIExtensionSites dónde le indicaremos (fijaos que lo hacemos con la constante) el menú del Shell dónde se añadirá el nuevo elemento. En este caso le pasamos el rootMenuItem con dos items hijos para que nos quede un menú cómo este:

Cómo tendremos eventos propios, lo que haremos es ubicarnos en el directorio Constants en CommandNames.cs y añadiremos dos nuevas constantes, una para gestionar el click de "Gestión contactos" y otra para gestionar el "Buscar":
public const string gestContactos = "GestionContactos";
public const string buscContactos = "BuscarContactos";
¿Y ahora cómo hacemos la relación con el item del menú y evento? Muy fácil. Seguimos dentro del método ExtendMenu() y añadiremos estas dos líneas de código. Fijaos que ahora hemos accedido a la propiedad Commands y con las constantes creadas anteriormente, le hemos indicado que hemos añadido a esos elementos un evento. Saber que "Click" o el nombre del evento que sea es case sensitive, o sea, que "click" no sirve y da lugar a error. Os recomiendo que os creéis una clase en el directorio Constants de Interface para la gestión correcta de los eventos ya que los usareis en toda la aplicación.
WorkItem.Commands[CommandNames.gestContactos].AddInvoker(itemGestContactos, "Click");
WorkItem.Commands[CommandNames.buscContactos].AddInvoker(itemBuscContactos, "Click");
Ahora, solo nos queda un paso. Indicar a CAB la acción que debe realizar cuándo el itemGestContactos invoque el evento click. Nada complejo, nos debemos crear un método público que devuelva vacío y en la firma del método indicar que es un manejador, por ejemplo, para la constante gestContactos:
[CommandHandler(CommandNames.gestContactos)]
public void gestContactos(object sender, EventArgs e)
{
//Código de las acciones
}
Por defecto y al estar indicado en el ProfileCatalog, al cargarse nuestro módulo, en el método Run() se llama al método ExtendMenu() para que inicialice la Shell adaptada para cada uno de nuestros desarrollos. Hará lo mismo para la ToolStrip que usará ToolStripButton para añadir botones en la barra de herramientas.
En la entrega III seguiremos con la gestión de los menus y introduciremos ya las vistas.
Enjoy ;)