MSDN Magazine > Inicio > Todos los números > 2007 > February >  RibbonX API: Ampliación del sistema Office...
RibbonX API
Ampliación del sistema Office 2007 con las fichas y controles de Cinta
Eric Faller

En este artículo se analizan los siguientes temas:
  • Introducción a la Cinta
  • Controles y características de RibbonX
  • Actualización de complementos para usar RibbonX
  • Creación de complementos para Word y Excel
En este artículo se utilizan las siguientes tecnologías:
El sistema Microsoft Office 2007
Descargar el código de este artículo: RibbonX2007_02.exe (201 KB)
Examinar el código en línea
Si ha visto el sistema Microsoft Office 2007, habrá notado que la nueva interfaz de usuario de Microsoft® Office es totalmente diferente a la de las versiones anteriores de Office. De modo que, probablemente, no le sorprenderá oír que el modelo de extensibilidad tras la interfaz de usuario también es completamente nuevo.
De forma muy parecida a las antiguas barras de herramientas y menús que ampliaba, el modelo de objetos CommandBars (disponible desde Office 97) era difícil de usar de forma eficaz. El nuevo modelo se denomina RibbonX y reclama muchos de los mismos atributos de la nueva interfaz de usuario: facilidad de uso, coherencia, modernidad y capacidad de predicción para los usuarios finales.
RibbonX desenreda el diseño de la interfaz de usuario desde el código subyacente usando XML para especificar su contenido y estructura, con un mecanismo de devolución de llamada dinámico subyacente. A lo largo de este artículo entraré en todos los detalles, pero primero hay algunas cosas que puede que los expertos desarrolladores de Office quieran saber acerca de la nueva interfaz de usuario.
Al alejarse de la antigua interfaz de usuario, Microsoft también está dejando atrás mucha terminología familiar y la está sustituyendo por conceptos nuevos y desconocidos. Empecemos analizando los conceptos básicos de la nueva interfaz de usuario para asegurarnos de que todos estamos en el mismo nivel. En la figura 1 se muestra Microsoft Office Word 2007 con etiquetas en cada uno de los componentes principales de la interfaz de usuario.
Figura 1 Elementos de la nueva interfaz de Word 2007 (Hacer clic en la imagen para ampliarla)
1. Cinta La zona grande y rectangular que hay encima del documento se conoce como cinta. Contiene la barra de título, el botón de Office, la barra de herramientas de acceso rápido y las fichas. RibbonX se aplica principalmente a la cinta y a todo lo que hay en su interior.
2. Botón de Office Este botón abre el menú Office, el equivalente al menú Archivo de las versiones anteriores de Office. El menú Office contiene comandos que actúan sobre los documentos y no sobre el contenido de éstos. Los complementos de RibbonX pueden alterar libremente el contenido del menú Office (aunque no pueden personalizar el propio botón de Office).
3. Barra de herramientas de acceso rápido Esta barra de herramientas contiene comandos que se usan habitualmente y es la ubicación principal para las personalizaciones de los usuarios finales. Los usuarios pueden hacer clic con el botón secundario en cualquier control de la cinta y agregarlo a la barra de herramientas de acceso rápido (incluidos los controles personalizados de RibbonX). Como es el espacio que está pensado para que "pertenezca" al usuario final, normalmente los complementos de RibbonX no pueden alterar la barra de herramientas de acceso rápido a menos que tengan activado el modo StartFromScratch.
4. Fichas Las fichas forman el contenido principal de la cinta y contienen controles de la interfaz de usuario que gestionan el contenido del documento actual. Los complementos de RibbonX pueden crear sus propias fichas personalizadas y alterar la visibilidad y las etiquetas de las fichas integradas.
5. Conjuntos de fichas contextuales Cuando se seleccionan objetos como imágenes o tablas dentro del documento, aparecen conjuntos de fichas contextuales que contienen todos los elementos de la interfaz de usuario para controlar dichos objetos. Los complementos de RibbonX pueden alterar la visibilidad de los conjuntos de fichas integrados y agregarles fichas personalizadas. Una característica que no se admite en la versión 2007 de Office es la creación de conjuntos de fichas contextuales personalizados. Los conjuntos de fichas contienen fichas contextuales, que de otro modo se comportan igual que las fichas normales.
6. Grupos Las fichas contienen conjuntos de grupos que, a su vez, contienen controles individuales de la interfaz de usuario. Los complementos de RibbonX pueden alterar la visibilidad de los grupos integrados y crear sus propios grupos personalizados. Algo que no pueden hacer es alterar el contenido de los grupos integrados. Esta limitación protege el diseño de la interfaz de usuario y evita que los complementos tengan conflictos entre sí y con futuras versiones de Office. Opcionalmente, los grupos tienen iniciadores de cuadros de diálogo en la esquina que muestran los diálogos relevantes para el grupo (como los diálogos de Fuente o Párrafo).
7. Paneles de tareas Hay muchos paneles de tareas que siguen presentes en el sistema Office 2007 y ahora se pueden tener abiertos más de uno a la vez. Los complementos de COM ahora pueden crear CustomTaskPanes que albergan contenido como controles ActiveX® o controles Windows® Forms. (La característica CustomTaskPane es distinta de RibbonX y no se trata en este artículo.)
8. MiniToolbar MiniToolbar es una colección de comandos de formato habituales que aparecen sobre las selecciones de texto y los menús contextuales que se abren al hacer clic con el botón secundario. Los complementos de RibbonX no pueden modificar el contenido de MiniToolbar, pero pueden desactivar o volver a planificar los comandos en ella.
9. Menús contextuales Son los mismos menús contextuales que se abren al hacer clic con el botón secundario que todos conocemos y apreciamos de las versiones anteriores de Office. En la versión 2007 de Office, RibbonX no se aplica a los menús contextuales, pero se pueden ampliar y personalizar usando el modelo de objetos CommandBars igual que antes.
10. Barra de estado La barra de estado contiene varios controles nuevos y prácticos como el número de palabras y la vista del control deslizante. La barra de estado no se puede personalizar con los complementos en el sistema Office 2007, aunque se puede ocultar.

Compatibilidad con complementos existentes
Si ya tiene complementos escritos para Office 2003, puede que se esté preguntando si seguirán funcionando en la versión 2007. Afortunadamente, la respuesta es sí, seguirán funcionando igual de bien (a menos que tengan un comportamiento extraño como comprobar la versión de Office y negarse a ejecutarse en nuevas versiones). Todos los antiguos menús y barras de herramientas siguen existiendo aunque no sean visibles por la simple razón de que los complementos heredados que los manipulan tienen que seguir ejecutándose en el sistema Office 2007. ¿Cómo se muestran en la nueva interfaz de usuario? Veamos un ejemplo.
En la figura 2 se muestra un complemento hipotético de Word 2003 que crea una barra de herramientas personalizada y agrega algunos botones a los antiguos menús y barras de herramientas, y el aspecto que tiene cuando se carga en Word 2007.
Figura 2a Los mismos complementos en Word 2003 y Word 2007 
Figura 2b
Como puede ver, cuando se carga un complemento heredado CommandBar, la ficha Complementos se muestra en la cinta. La ficha Complementos contiene tres grupos: Comandos del menú, Comandos de la barra de herramientas y Barras de herramientas personalizadas. Los grupos Comandos del menú y Comandos de la barra de herramientas contienen controles personalizados que se agregaron, respectivamente, a los menús y barras de herramientas integrados heredados. También mostrarán cualquier control integrado heredado que se hubiera vuelto a planificar con los complementos para realizar acciones personalizadas (estableciendo las propiedades OnAction o Hyperlink). El grupo de Barras de herramientas personalizadas muestra cualquier barra de herramientas personalizada heredada como secciones horizontales. La información sobre herramientas de los controles muestra el nombre de la barra de herramientas heredada a la que pertenecen.
En caso de que un complemento con comportamiento anormal deje botones después de haberlo desinstalarlo, el usuario puede hacer clic con el botón secundario en los controles de la ficha Complementos y eliminarlos de la estructura CommandBar heredada.

Nuevas características de RibbonX
RibbonX traslada la programación de Office a la era moderna con la declaración de la interfaz de usuario basada en XML. En lugar de escribir código complicado que crea la interfaz de usuario mediante una serie de llamadas de modelo de objetos, puede crear un archivo XML que especifique la apariencia de la interfaz de usuario en una forma de marcado estructurada. Esto tiene muchas ventajas para el escritor de complementos.
En primer lugar, está separando la interfaz de usuario del código. El diseñador de la interfaz de usuario no necesita saber cómo se actualiza el código de forma manual para probar nuevos diseños. De hecho, si el complemento elige cargar su XML desde un archivo externo, la interfaz de usuario se puede modificar y perfeccionar sin volver a compilar el complemento. Esto elimina el paso intermedio del ciclo repetido Editar, Compilar, Ejecutar necesario para los sistemas de interfaz de usuario codificados.
En segundo lugar, Office conoce la interfaz de usuario completa del complemento. Puede que esto no parezca significativo hasta que se dé cuenta de que, con CommandBars, las versiones anteriores de Office no sabían a qué complementos pertenecían las barras de herramientas y los menús. Todas las modificaciones de la interfaz de usuario se realizaban en llamadas COM, que no se podían volver a atribuir a ningún complemento individual. Esto provocaba problemas muy molestos que ahora se pueden evitar por completo.
En tercer lugar, Office puede limpiar automáticamente la interfaz de usuario de un complemento. Anteriormente, los complementos tenían que tener un código especial que entraba y eliminaba sus menús y barras de herramientas al cerrarlos o desinstalarlos. Si el complemento se bloqueaba o no se limpiaba bien (como hacían muchos), podía dejar botones inútiles en la interfaz de usuario. Desde que el sistema Office 2007 limpia automáticamente la interfaz de usuario de un complemento, no es necesario escribir código de limpieza y no existe la posibilidad de que haya interfaz de usuario con restos.
El control de versiones entre diferentes versiones de Office es otro problema que se evitará. Se requiere que los archivos XML válidos contengan una declaración de espacio de nombres xmlns que identifique con qué versión de esquema son compatibles. Esto le proporciona a RibbonX información acerca de la versión de Office a la que está destinado un complemento concreto y le permite asignar la interfaz de usuario adecuadamente, teniendo en cuenta cualquier cambio que haya podido haber entre versiones. La falta de control de versiones y de propiedad en el modelo de objetos CommandBars son dos de las razones principales que suelen romper los complementos heredados al actualizar a nuevas versiones de Office y que la ficha de Complementos es incapaz de distinguir mejor entre el embrollo de controles que se le agregan con las llamadas COM.
Por último, XML tiene buena compatibilidad con herramientas. Teniendo en cuenta el archivo de definición de esquema RibbonX (XSD), se puede usar una gran variedad de editores de XML (incluido el propio Word 2007) para crear archivos de interfaz de usuario RibbonX válidos con la simplicidad de arrastrar y colocar.
El modelo de objetos CommandBars se puede describir como que usa un modelo de "inserción" en el que los complementos insertan todos los datos de la interfaz de usuario en Office mediante el establecimiento de varias propiedades en el modelo de objetos. Cada propiedad de los controles de la interfaz de usuario debe establecerse explícitamente por el complemento durante el inicio, aunque nunca se muestre el menú o la barra de herramientas donde se encuentra el control. Si esto implica cargar un gran número de archivos de imagen, podría resultar bastante caro, en lo referente al rendimiento.
RibbonX usa un modelo de "inserción" en el que Office inserta los datos desde el complemento sólo cuando es necesario. En lugar de establecer propiedades como CommandBar.Name o CommandBarButton.Picture, los complementos proporcionan devoluciones de llamadas como getLabel o getImage. Office llama a estas devoluciones de llamada cuando necesita saber qué es la etiqueta o la imagen de un control.
La ventaja principal del modelo de inserción es el rendimiento. En la versión 2007 de Office, es probable que la mayor parte de la interfaz de usuario de un complemento no sea visible cuando se inicia la aplicación, sobre todo si ese complemento ha agregado su propia ficha de nivel superior o un grupo a la ficha Complementos. No hay una necesidad inmediata de disminuir el rendimiento cargando todas las imágenes para esos controles. Las imágenes sólo tienen que cargarse cuando el usuario hace clic en esa ficha. RibbonX retrasa la llamada de las devoluciones de llamada lo máximo posible para amortizar el costo de la carga de recursos en toda la sesión y mantiene las aplicaciones de Office iniciándose con agilidad aun cuando hay muchos complementos instalados.
Si Office está decidiendo cuándo llamar a las devoluciones de llamada, puede que se pregunte cómo cambiar las propiedades del control sobre la marcha. RibbonX almacena en la memoria caché los valores devueltos por la devolución de llamada y no volverá a llamar hasta que el complemento los invalide. Los complementos pueden hacer esto usando los métodos Invalidate e InvalidateControl en la interfaz IRibbonUI. Una vez que se han invalidado las propiedades del control, RibbonX sabrá volver a llamar la próxima vez que se necesiten. Si este control se encuentra actualmente en la pantalla, volverá a llamar y actualizará el valor inmediatamente. Si no está en la pantalla, RibbonX no volverá a llamar hasta que lo esté. Más adelante, entraremos en todos los detalles esenciales acerca de cómo implementar funciones de devolución de llamada y hacer invalidaciones en los complementos de muestra.
RibbonX proporciona muchos tipos de control diferentes de extensibilidad, incluidos los que se muestran en la figura 3. Sólo los seis primeros elementos de la lista estaban disponibles en CommandBars, de modo que hay muchas nuevas opciones de interfaz de usuario disponibles para los complementos de RibbonX. Tal vez los nuevos tipos de control más atractivos sean un splitButton que incluye un botón y un menú desplegable (consulte la figura 4), y una galería que se expande para mostrar una selección de imágenes. Los dos se usan mucho en la versión 2007 de Office para reducir la aglomeración de la interfaz de usuario y para ver fácilmente una vista previa de las operaciones visuales. Se anima a los escritores de complementos a usar estos nuevos tipos de control para dar a su interfaz de usuario el mismo impacto.

Controles RibbonX
1. botón
2. toggleButton
3. editBox
4. menú
5. comboBox
6. dropDown
7. dialogBoxLauncher
8. galería
9. splitButton
10. etiqueta
11. checkBox
12. grupo
13. etiqueta
14. superTip
Figura 4 SplitButton 

Modo StartFromScratch
Muchos desarrolladores usan Office como una plataforma para crear sus propias aplicaciones independientes. Mientras se ejecutan, estas aplicaciones a menudo eliminan toda la interfaz de usuario integrada de Office y la sustituyen por la suya. No había un mecanismo estándar para hacer esto usando CommandBars, de modo que resultaba muy complicado y producía muchos errores.
RibbonX introduce un modo StartFromScratch, que hace que la interfaz de usuario de Office se oculte con tan sólo escribir una línea de XML. Al establecer startFromScratch="true" en la etiqueta raíz <ribbon> sucederá lo siguiente:
  • Se ocultan todas las fichas principales integradas de nivel superior (pero no los conjuntos de fichas contextuales).
  • Se oculta el contenido de la barra de herramientas de acceso rápido.
  • Se activa el complemento para agregar sus propios botones a la barra de herramientas de acceso rápido (normalmente no se permite).
  • Se ocultan todos los comandos del menú Office excepto Nuevo, Abrir y Guardar.
Una vez que se han aplicado estos cambios, los complementos pueden hacer más cambios a la interfaz de usuario, como revelar algunas de las fichas integradas u ocultar más elementos de TabSets o del menú Office.
La mayoría de las funciones del modo StartFromScratch se pueden duplicar ocultando manualmente todos estos elementos de la interfaz de usuario, pero StartFromScratch tiene un par de ventajas sobre hacerlo a mano. En primer lugar, es muchísimo más fácil escribir una línea de XML que cincuenta. En segundo lugar, está diseñado para seguir siendo compatible con las futuras versiones de Office. Si la próxima versión de Word agrega nuevas fichas de nivel superior o elementos del menú Office, el modo StartFromScratch debería ocultarlos automáticamente, pero los complementos que ocultan la interfaz de usuario a mano tendrán que estar actualizados para ocultar estos nuevos elementos.
Otra gran característica del modo StartFromScratch es que funciona por documentos. Si el usuario está editando varios documentos en el modo de interfaz de múltiples documentos (MDI), uno de estos documentos puede activar el modo StartFromScratch y toda la interfaz de usuario se ocultará automáticamente y mostrará cómo el usuario cambia entre ese documento y los demás. Y no se necesita código.

Deshabilitación y replaneación de comandos
Otra operación que realizan habitualmente los complementos de Office es la deshabilitación de comandos integrados. Con RibbonX también es tan sencillo como escribir una línea. Los desarrolladores pueden deshabilitar comandos integrados usando líneas como esta en la sección <commands> de su XML:
<command idMso="Bold" enabled="false"/> 
Esto deshabilitará el botón Negrita en todos los sitios en que aparezca en la interfaz de usuario, lo que en la configuración predeterminada incluye el grupo Fuente de la cinta y también en MiniToolbar.
Como esto no pasaba con CommandBars, es importante tener en cuenta que esto deshabilita el botón Negrita en los dos sitios. Se requería que los complementos enumeraran toda la interfaz de usuario y desactivaran manualmente cada copia del botón, en caso de que el usuario hubiera personalizado sus barras de herramientas y hubiera colocado una copia en algún otro lugar. También tenían que buscar comandos por su número de Id. (el 43, ¿alguien?), en lugar de por nombres legibles como "Negrita". También se puede usar una devolución de llamada getEnabled si el complemento quiere desactivar el comando de forma condicional sólo en ciertas ocasiones.
El elemento <commands> de XML también es donde se encuentra la característica RibbonX Command Repurposing. Los complementos suelen mejorar o ampliar la funcionalidad integrada de Office, sustituir botones de la interfaz de usuario y replanearlos. Opcionalmente, los administradores de TI a veces eligen restringir la funcionalidad usando la replaneación, por ejemplo, mostrando un cuadro de diálogo de contraseña cuando se hace clic en el botón Imprimir.
Cualquier botón que realice una acción al hacer clic en él se puede replanear usando RibbonX (aunque los tipos de control más complicados como galerías o cuadros combinados no se pueden replanear). Aquí hay un ejemplo del XML necesario para replanear el botón Guardar:
<command idMso="Save" onAction="MySaveFunction"/>
Al hacer clic en Guardar, se ejecutará MySaveFunction del complemento, dándole a éste la oportunidad de realizar sus propias acciones y de elegir si permitirá que se ejecute la función integrada Guardar.
Una vez más, esta única línea de XML replaneará todas las copias del botón Guardar (tanto del menú Office como de la barra de herramientas de acceso rápido) y, si un documento está realizando la replaneación, no se aplicará a otros documentos que también estén abiertos (a menos que el documento esté cargado como una plantilla o complemento global).

Carga a petición
Un problema con los complementos de Office es que sus DLL a veces tardan mucho en iniciarse, sobre todo si están escritas en código administrado y el Common Language Runtime (CLR) aún no está cargado. Los usuarios con varios complementos instalados pueden ver una caída considerable del rendimiento cada vez que inician una aplicación de Office. La ironía es que, probablemente, el usuario ni siquiera usará la mayoría de las funciones de los complementos en alguna de sus sesiones, pero el precio lo paga en todas.
Como las ralentizaciones se reflejan poco en Office y en los complementos, Office proporciona una característica para los complementos de COM que se denomina carga a petición. Esta característica se ha actualizado en la versión 2007 de Office y se ha ampliado para incluir compatibilidad con RibbonX. El proceso se resume a continuación. El escritor de complementos establece la clave de Registro LoadBehavior del complemento en ConnectFirstTime (16). El complemento se inicia la primera vez que se inicia la aplicación desde que el complemento se instaló. Office consulta al complemento su XML de RibbonX y lo almacena en la memoria caché del disco. Entonces, RibbonX llama a todas las devoluciones de llamada Get del complemento para cargar todas sus imágenes y almacena en la memoria caché el estado inicial del complemento.
Cuando se cierra, la clave de Registro del complemento cambia a DemandLoad. En los siguientes inicios de la aplicación el complemento no se inicia, pero su interfaz de usuario de RibbonX se muestra como si se hubiera cargado. En cuanto el usuario hace clic en uno de los botones del complemento, éste se carga para ejecutar el comando del usuario.
La carga a petición es transparente para el usuario final y, aparte de establecer la clave de Registro, los desarrolladores no tienen que hacer nada más. (Compare esto con la dificultad de configurar cadenas <!ProgID> para usar carga a petición con CommandBars.) Evidentemente, la carga a petición no será útil para cada complemento, pero debería ser una ventaja para la mayoría de complementos que sólo agregan un único botón o un elemento de menú a la interfaz de usuario y luego activan su funcionalidad cuando se hace clic en ellos. Sin embargo, los complementos más complicados que tienen que ejecutar código elaborado antes de que el usuario interactúe con su interfaz de usuario no podrán disfrutar de las ventajas de la carga a petición ya que tienen que cargarse para ejecutar el código.

Actualización de una plantilla de Word a RibbonX
Ahora que hemos visto las principales características de RibbonX, entraremos en los detalles con algunos complementos y fragmentos de código de ejemplo. Primero analizaré un caso en el que se encuentran muchos desarrolladores al cambiar al sistema Office 2007: la actualización de un complemento heredado desde CommandBars a RibbonX.
El complemento que voy a actualizar es una plantilla de Word bastante sencilla (.dot) que proporciona funcionalidad para insertar páginas en blanco en un documento legal, en la que se incluye el texto estándar (y contradictorio) "Esta página se ha dejado en blanco intencionadamente".
El complemento se implementa con un botón del menú Insertar en la CommandBars adjunta al documento. La propiedad OnAction del botón se establece en InsertBlankPage, una macro de Visual Basic® for Applications (VBA) que contiene el siguiente código:
Sub InsertBlankPage()
    ' Insert a blank page
    Selection.InsertBreak Type:=wdPageBreak
    Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
    Selection.Font.Size = 14
    Selection.Font.Color = wdColorGray50
    Selection.Font.Name = "Arial Black"
    Selection.TypeText Text:="This Page Intentionally Left Blank"
    Selection.InsertBreak Type:=wdPageBreak
End Sub
Es obvio que no es muy complicado, pero sirve de demostración. El archivo se puede descargar en el sitio web de MSDN®Magazine. Puede instalarlo como una plantilla global usando el diálogo Plantillas y complementos.
El primer paso a la hora de actualizar un complemento CommandBar heredado a la nueva interfaz de usuario del sistema Office 2007 es asignar la antigua interfaz de usuario al nuevo modelo. Con todas las barras de herramientas y menús sustituidos por la cinta, no es inmediatamente obvio el lugar donde la interfaz del complemento encaja mejor.
Antes de llegar a este paso, retroceda un poco y pregúntese, ¿"realmente necesitamos actualizar este complemento"? En muchos casos, si el presupuesto es ajustado, puede que la respuesta sea "No". Si carga el complemento en Word 2007, funciona igual de bien en la ficha Complementos (consulte la figura 5).
Figura 5 Complemento heredado 
No hay nada particularmente malo en cómo funciona aquí, pero puede que no le guste cómo está estancado en el grupo Comandos del menú con todos los demás complementos heredados, y no está usando ninguna de las nuevas características de RibbonX. Tampoco sería una buena demostración si nos quedáramos en este punto, así que vamos a continuar.
Como el complemento inserta páginas en un documento, probablemente un buen lugar para la interfaz de usuario sería la ficha Insertar. Si el complemento con el que estamos tratando manipula el documento como un todo, podría elegir poner su interfaz de usuario en el menú Office. Si no hay espacio suficiente para él en la interfaz de usuario existente, sería mejor un grupo personalizado en la ficha Complementos (para evitar crear una ficha con un solo botón).
La ficha integrada Insertar de Word contiene un grupo para insertar páginas y, quién lo iba a decir, ya contiene un botón para insertar páginas en blanco (una característica nueva de la versión 2007 de Office). Como el complemento heredado también inserta el texto de marcador de posición "Intencionadamente en blanco", me gustaría diferenciarlo de la funcionalidad integrada. Vamos a agregar un grupo personalizado llamado "Páginas legales" junto al otro grupo, y le pondremos un gran botón con un icono instructivo. En la figura 6 se muestra el aspecto que debería tener una vez terminado.
Figura 6 Complemento para insertar página 
El siguiente paso será crear el archivo XML que nos dará el resultado esperado. En las demostraciones suele ser más rápido empezar con el producto final y, después, explicar cómo funciona. En ese sentido, en la figura 7 se muestra el XML final.
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <ribbon>
    <tabs>
      <tab idMso="TabInsert">
        <group id="LegalGroup" label="Legal Pages" 
               insertAfterMso="GroupInsertPages">
          <button id="BlankPageButton" label="Intentionally Blank Page"
                  size="large" image="BlankIcon"/>
    </group>
   </tab>
  </tabs>
 </ribbon>
</customUI>

Como puede ver, es bastante sencillo. La etiqueta raíz es <customUI> y especifica el espacio de nombre XML de RibbonX del sistema Office 2007 para empezar. Después, pasa por una serie de etiquetas que reflejan la estructura de la interfaz de usuario: Ribbon > Tabs > Tab > Group > Button.
El primer elemento interesante es la propiedad idMso="TabInsert" de la etiqueta <tab>. Todos los controles del XML deben tener propiedades Id. para identificarlos. Los controles integrados usan idMso para mostrar que son controles de Office y para diferenciarse de los controles personalizados, que usan la propiedad Id. Por tanto, el archivo XML básicamente dice "buscar la ficha integrada Insertar y crear un grupo personalizado en ella". La propiedad insertAfterMso="GroupInsertPages" dice "insertar este grupo después del grupo integrado Páginas". Finalmente, el botón id="BlankPageButton" dice "crear un botón personalizado dentro del grupo".
En este ejemplo, las etiquetas del grupo y del botón se especifican estadísticamente en el XML, por lo que no pueden cambiar nunca. En este caso está bien, pero si quisiera cambiar las etiquetas de forma dinámica (tal vez para la localización), podría usar en su lugar la propiedad getLabel para especificar una función que devolverá la etiqueta.
La última parte interesante es la propiedad image="BlankIcon". Ordena a RibbonX buscar una imagen en el archivo de plantillas de Word con esa Id. Tendrá que agregar esta imagen al archivo a la vez que inserta el XML.

Adición de XML e imágenes
El XML de RibbonX XML sólo se admite en el nuevo formato de archivo para la versión 2007 de Office, así que lo primero que tendrá que hacer es abrir el archivo .dot y guardarlo como un archivo .dotm (la "m" significa que el archivo contiene macros). A continuación, cambie el nombre del archivo con una extensión .zip y ábralo para comprobar el contenido que se muestra en la figura 8.
Figura 8 Dentro de la plantilla del documento (Hacer clic en la imagen para ampliarla)
Los detalles del formato de archivo XML abierto son un tema para otro artículo, pero ahora basta con decir que los archivos, básicamente, son paquetes comprimidos que contienen un grupo de archivos relacionados entre sí de diferentes maneras. En este ejemplo, document.xml es el documento principal de Word y está relacionado con varias cosas como la información del estilo, el código de VBA y el CommandBars adjunto. Como no quiero que el archivo siga teniendo el CommandBars, vamos a eliminar el archivo attachedToolbars.bin del archivo .zip y, a continuación, volver a incluirlo.
Podríamos editar manualmente el archivo .zip para insertar el XML de RibbonX y la imagen del icono, pero sería mucho más fácil usar una herramienta, así que vamos a hacerlo. Aparte de openxmldeveloper.org hay un montón de herramientas para abordar el formato de archivo XML abierto. El que queremos es Custom UI Editor.
Custom UI Editor es una pequeña herramienta práctica para insertar XML de RibbonX e imágenes asociadas en los archivos de formato XML abierto. Lo usaremos para abrir el archivo .dotm, después pegaremos en XML e insertaremos la imagen (consulte la figura 9). Guarde el archivo. Si tiene curiosidad, puede volver a abrirlo como un archivo .zip y ver que la herramienta ha creado el archivo XML en \customUI\customUI.xml y la imagen en \customUI\images\BlankIcon.png. También ha modificado un par de relaciones rels para señalar los nuevos archivos.
Figura 9 Edición de una plantilla (Hacer clic en la imagen para ampliarla)
Si abre el archivo en Word verá el nuevo botón, pero no hará nada cuando lo presione. Eso es obvio, porque no le hemos dicho a qué macro llamar. Para hacerlo, agregue al XML las propiedades que se muestran en rojo:
<button id="BlankPageButton" label="Intentionally Blank Page" 
        size="large" image="BlankIcon"
        onAction="RibbonXOnAction" tag="InsertBlankPage" />
Al principio puede resultar raro introducir una nueva macro llamada RibbonXOnAction en lugar de, simplemente, volver a usar la macro InsertBlankPage. La razón por la que lo he hecho así es porque las macros OnAction de RibbonX tienen diferentes firmas que sus CommandBar homólogos. Si tuviera muchas macros, tendría que cambiarlas todas a mano, lo que podría resultar una tarea muy larga y propensa a los errores. También supondría romper el código para que no se ejecutara en versiones anteriores de Office.
Puede esquivar estos problemas haciendo que la nueva macro de RibbonX llame a las antiguas macros de CommandBar. Aquí es cuando entra la propiedad tag: es una propiedad de cadena que no se usa en la interfaz de usuario, pero se transfiere al código del complemento para usarla aquí. Así es como se usa en la macro RibbonXOnAction, que puede pegar en un nuevo módulo de VBA:
Sub RibbonXOnAction(button As IRibbonControl)
    Application.Run button.Tag
End Sub
La macro OnAction de RibbonX toma un objeto IRibbonControl como un parámetro, que representa al botón sobre el que se ha hecho clic. En CommandBars, las macros OnAction no tenían parámetros, por lo que a veces era complicado determinar el botón sobre el que se estaba haciendo clic. El objeto IRibbonControl proporciona propiedades ID y Tag para resolver este problema en RibbonX.
Una vez que tiene la macro en su sitio, el nuevo botón empieza a funcionar. Esto es todo lo que se necesita para actualizar el código de este complemento para que se ejecute usando RibbonX: una nueva macro de una línea y ningún cambio en el código existente. Fácil.

Un complemento de COM para Excel
Como segundo ejemplo, vamos a complicar un poco más las cosas y a escribir un complemento de COM para Excel®. El complemento será una aplicación de línea de negocio (LOB) en miniatura para rellenar y enviar tarjetas de tiempo. Aquí sólo podré mostrar fragmentos del código, pero todo el código fuente se encuentra disponible en la descarga.
De nuevo, el primer paso es determinar qué tipo de interfaz de usuario se necesita. El complemento permite a los usuarios crear tarjetas de tiempo y, posteriormente, editarlas y enviarlas. Como la única interfaz de usuario de tarjeta de tiempo que tiene que mostrar al principio es un botón de Nueva tarjeta de tiempo, colóquelo usted mismo con el menú de Office (consulte la figura10).
Figura 10 Un nuevo botón 
Cuando se hace clic en el botón Nueva tarjeta de tiempo (o se abre una tarjeta de tiempo existente), el resto de la interfaz de usuario de tarjeta de tiempo se muestra en una ficha personalizada. Como algunas fichas de Excel como Diseño de página, Fórmulas, Revisar y Datos no se aplican para rellenar tarjetas de tiempo, ocúltelas a la vez (consulte la figura 11).
Figura 11 Una ficha de cinta de tarjeta de tiempo personalizada (Hacer clic en la imagen para ampliarla)
La interfaz de usuario de tarjeta de tiempo es claramente más complicada que la del complemento anterior y, por tanto, el XML para crearla, como se muestra en la figura 12. Lo primero que necesita un complemento de COM para usar RibbonX es implementar la interfaz IRibbonExtensibility. Afortunadamente, la característica Visual Studio® IntelliSense® nos lo pone más fácil. Cree un complemento, para ello seleccione Nuevo proyecto en Visual Studio, vaya a la categoría Otros tipos de proyecto y seleccione Complemento compartido en la sección Extensibilidad.
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui"
          onLoad="OnLoad" loadImage="LoadImage">
  <ribbon>
    <officeMenu>
      <button id="NewTimecard" insertAfterMso="FileNew" 
              label="New Timecard" imageMso="StartAfterPrevious"
              onAction="NewTimecard" />
    </officeMenu>
    <tabs>
      <tab id="TimecardTab" label="Timecard"
           getVisible="IsCustomTabVisible">
        <group id="EditingTools" label="Editing Tools">
          <button id="ClearTimecard" label="Clear Timecard" size="large" 
                  imageMso="TableDeleteRowsAndColumnsMenuWord"
                  onAction="ClearTimecard"/>
          <button id="InsertDay" label="Insert New Day" size="large"
                  imageMso="CellsInsertDialog" onAction="InsertNewDay"/>
          <button id="CalculateTotal" label="Calculate Hours Worked" 
                  size="large" imageMso="SlideShowRehearseTimings" 
                  onAction="CalculateHours"/>
        </group>
        <group id="WageTools" label="Wage Tools">
          <editBox id="Wage" label="Hourly wage:"
                   image="dollar1.png" onChange="OnWageChanged"
                   getText="GetWage"/>
          <editBox id="Overtime" label="Overtime bonus:"
                   image="dollar2.png" onChange="OnOvertimeChanged"
                   getText="GetOvertime"/>
          <button id="CalculatePay" label="Calculate Pay" 
                  size="large" imageMso="AcceptInvitation" 
                  onAction="CalculatePay"/>
        </group>
        <group id="SubmissionTools" label="Submission Tools">
          <button id="SignTimecard" label="Sign Timecard" 
                  size="large" image="signature.png" 
                  onAction="SignTimecard"/>
          <button id="SubmitTimecard" label="Submit Timecard" 
                  size="large" image="submit.png" 
                  onAction="SubmitTimecard" getEnabled="IsTimecardSigned"
                  screentip="Make sure to sign your timecard before 
                            submitting" />
        </group>
      </tab>
      <tab idMso="TabPageLayoutExcel" getVisible="IsBuiltinTabVisible"/>
      <tab idMso="TabData" getVisible="IsBuiltinTabVisible" />
      <tab idMso="TabReview" getVisible="IsBuiltinTabVisible" />
      <tab idMso="TabFormulas" getVisible="IsBuiltinTabVisible" />
    </tabs>
  </ribbon>
</customUI>

Haga clic en el diálogo y en el asistente que aparece después. Tendrá que seleccionar varias opciones como qué aplicaciones de Office debería tener instaladas su complemento y qué lenguaje de programación usar. Para este ejemplo he elegido usar C#.
Una vez que ha creado el complemento, debería ver la clase Connect, que implementa la interfaz IDTExtensibility2 del complemento de COM estándar. Este es el objeto que necesita para implementar IRibbonExtensibility. Agregue la interfaz y deje que Visual Studio implemente automáticamente sus métodos. Al final debería tener algo parecido a esto:
using Office = Microsoft.Office.Core;
[GuidAttribute("E8407539-C642-43BB-8FCB-2E27A46179DE"), 
 ProgId("TimecardAddin.Connect")]
public class Connect : Object, Extensibility.IDTExtensibility2, 
    Office.IRibbonExtensibility
{
    public string GetCustomUI(string RibbonID)
    {
        throw new Exception("Not implemented.");
    }

    // IDTExtensibility methods
}
El único método de IRibbonExtensibility es GetCustomUI, que devuelve el XML de RibbonX XML como una cadena. También adopta un parámetro RibbonID, que es una cadena como "Microsoft.Word.Document" o "Microsoft.Excel.Workbook" y es especialmente útil a la hora de escribir complementos que se ejecutan en varias aplicaciones o aplicaciones con varios tipos de cinta (como Microsoft Outlook).
Si agrega el archivo XML al proyecto como un recurso incrustado llamado customui.xml, puede devolverlo desde GetCustomUI usando código como este:
public string GetCustomUI(string RibbonID)
{
    Assembly assembly = Assembly.GetExecutingAssembly();
    using(Stream stream = assembly.GetManifestResourceStream(
        "TimecardAddin.customui.xml"))
    {
        return new StreamReader(stream).ReadToEnd();
    }
}
En este punto, puede crear el complemento e instalarlo. Después de iniciar Excel debería ver la interfaz de usuario personalizada, con las excepciones de que no se han cargado las imágenes personalizadas y de que el botón no hace nada al hacer clic sobre él.
Todas las funciones de devolución de llamada a las que se hace referencia en el XML no se llamarán porque aún no las ha implementado. Si está activada la opción "Mostrar los errores de la interfaz de usuario del complemento" de la sección Avanzada del diálogo de opciones de la aplicación, verá una secuencia de mensajes de error sobre las devoluciones de llamada que no se han ejecutado correctamente. Asegúrese de comprobar esta opción mientras desarrolla los complementos de RibbonX. Activa un amplio conjunto de mensajes de error que le avisan cuando algo va mal en su complemento. El primer mensaje de error que aparece explica que la devolución de llamada "onLoad" no se ha ejecutado, así que vamos a implementarla en primer lugar.

Implementación de funciones de devolución de llamada
La etiqueta raíz <customUI> de la Cinta tiene una devolución de llamadas opcional onLoad que los complementos pueden implementar para obtener una referencia al objeto IRibbonUI. El objeto IRibbonUI se usa para invalidar valores devueltos por la devolución de llamada almacenados en la memoria caché cuando es necesario cambiar el estado de la interfaz de usuario del complemento. En breve, entraré en los detalles sobre cómo usarlo, pero de momento sigamos con la variable de miembro. Agregue este código a la clase Connect:
Office.IRibbonUI m_Ribbon;

public void OnLoad(Office.IRibbonUI ribbon)
{
    m_Ribbon = ribbon;
}
Otra devolución de llamada opcional de la etiqueta <customUI> es loadImage, que es como los complementos de COM cargan los iconos para sus controles. Aquí es donde entran en juego las líneas image="dollar1.png" del XML. Para cada cadena especificada en una propiedad de imagen, RibbonX llama a loadImage para cargar la imagen.
Las imágenes se pueden almacenar como quiera el complemento, pero en este ejemplo vamos a agregar imágenes como recursos incrustados, igual que en el XML. Suponiendo que haya agregado dollar1.png como recurso en el ensamblado TimecardAddin, así es como hay que implementar la devolución de llamada loadImage:
public Bitmap LoadImage(string imageName)
{
    Assembly assembly = Assembly.GetExecutingAssembly();
    Stream stream = assembly.GetManifestResourceStream(
        "TimecardAddin." + imageName);
    return new Bitmap(stream);
}
Hay un par de cosas interesantes que hay que tener en cuenta sobre esta función. Primero, está devolviendo un objeto System.Drawing.Bitmap como la imagen. La compatibilidad con System.Drawing.Bitmap es nueva en la versión 2007 de Office con RibbonX y es la forma más fácil y aconsejable de devolver imágenes para los complementos administrados. Los complementos no administrados (C++ y Visual Basic 6.0) pueden devolver objetos IPictureDisp para sus imágenes, que es el mismo formato que se usaba en CommandBars.
La segunda cosa que hay que tener en cuenta es que sólo está devolviendo una imagen que contiene los datos del mapa de bits y los datos de transparencia. Eso es bueno. Ya no es necesario crear dos iconos Imágenes y Máscara separados para sus complementos de Office. Con RibbonX, Office ahora admite iconos de 32 bits con canales alpha. Actualmente, el formato PNG tiene la mejor compatibilidad con herramientas para canales alpha y es el formato recomendado para sus iconos RibbonX.
El siguiente conjunto de devolución de llamadas que tiene que implementar son las devoluciones de llamada getVisible en las fichas integradas y en la personalizada. Las fichas integradas usan IsBuiltinTabVisible y la personalizada usa IsCustomTabVisible, ya que estas dos funciones tienen que devolver valores contrarios (las fichas integradas deberían ocultarse cuando se está editando una tarjeta de tiempo, que es cuando debería mostrarse la ficha personalizada).
El complemento puede realizar un seguimiento de si un libro de Excel concreto es una tarjeta de tiempo estableciendo la propiedad Category del archivo en Timecard. Se omitirán los archivos que no tengan esta propiedad.
Las funciones IsBuiltinTabVisible e IsCustomTabVisible usarán una función auxiliar IsTimecard:
public bool IsCustomTabVisible(Office.IRibbonControl tab) {
    return IsTimecard(tab.Context as Excel.Window);
}

public bool IsBuiltinTabVisible(Office.IRibbonControl tab) {
    return !IsTimecard(tab.Context as Excel.Window);
}
private bool IsTimecard(Excel.Window window)
{
    if (null == window) return false;

    Excel.Workbook workbook = (Excel.Workbook)window.Parent;
    Office.DocumentProperties properties = 
        (Office.DocumentProperties)workbook.BuiltinDocumentProperties;
    Office.DocumentProperty category = properties["Category"];

    return (null != category && null != category.Value && 
            category.Value.Equals("Timecard"));
}
Este código usa la propiedad Context de IRibbonControl que se transfiere a las devoluciones de llamada de RibbonX. La propiedad Context es igual al objeto Window en la mayoría de aplicaciones de Office. (Access no tiene objetos Window, por lo que es NULL y en Outlook es igual que los objetos Inspector.) Los complementos deberían usar el objeto Window para determinar a qué documento se está refiriendo la devolución de llamada de RibbonX, porque no siempre es igual a la propiedad Application.ActiveDocument. El objeto Window también puede usarse para diferenciar entre dos ventanas que están visualizando el mismo documento.
Desde el objeto Window puede obtener el objeto Workbook y, desde ahí, la propiedad Category y comprobar si es igual a Timecard.

Invalidación de valores de devolución de llamada getVisible
Si inicia Excel ahora, verá que la ficha personalizada se oculta y que las fichas integradas se muestran. Esto es correcto porque el documento predeterminado no es una tarjeta de tiempo. Pero si abre un documento de Tarjeta de tiempo, las fichas no se mostrarán. ¿Por qué sucede esto?
Por cuestiones de rendimiento, RibbonX almacena en la memoria caché los valores devueltos procedentes de las devoluciones de llamadas. Si no lo hiciera, volvería a llamar al complemento constantemente por si éste quisiera cambiar algo en su interfaz de usuario. El complemento tiene que permitir que Office sepa cuándo quiere cambiar su interfaz de usuario.
Aquí es cuando entra el IRibbonUI del que hemos hablado antes. Contiene dos métodos: Invalidate e InvalidateControl. El método Invalidate invalidará todas las devoluciones de llamada almacenadas en la memoria caché, mientras que el método InvalidateControl adopta un parámetro Id. de control e invalida las devoluciones de llamada almacenadas en la memoria caché simplemente para un control personalizado en concreto.
Tiene que invalidar los valores de devolución de llamada getVisible de la ficha siempre que se produzca un cambio de documento en Excel, ya que es entonces cuando la interfaz de usuario puede que tenga que hacerse visible. Excel proporciona el evento WorkbookActivate que puede usar para esto. Agregue un controlador para ese evento en el método OnStartupComplete:
public void OnStartupComplete(ref System.Array custom)
{
    excelApplication.WorkbookActivate += 
       new Excel.AppEvents_WorkbookActivateEventHandler(
           OnWorkbookActivate);
}

void OnWorkbookActivate(Excel.Workbook Wb)
{
    // the custom tab might need to become visible on workbook
    // switch, so invalidate the cached getVisible values
    m_Ribbon.Invalidate();
}
Por suerte, IntelliSense de Visual Studio puede implementar el controlador del evento automáticamente cuando escriba los caracteres +=, así de fácil.
Si ahora abre Excel y cambia entre un documento de Tarjeta de tiempo y otro documento, verá que la ficha personalizada sólo se muestra para la tarjeta de tiempo y que las fichas integradas se ocultan a la vez. Perfecto. Ahora sólo tiene que poder usar el complemento para crear, en primer lugar, tarjetas de tiempo.
La devolución de llamada del botón Nueva tarjeta de tiempo es bastante sencilla: usa la misma firma OnAction que se ha mostrado anteriormente. Crea un nuevo libro con la propiedad Category igual a Timecard y configura la hoja predeterminada:
public void NewTimecard(Office.IRibbonControl button)
{
    Excel.Workbook timecard = 
        excelApplication.Workbooks.Add(Missing.Value);
    Office.DocumentProperties properties =
        (Office.DocumentProperties)timecard.BuiltinDocumentProperties;
    properties["Category"].Value = "Timecard";
    Excel.Worksheet timesheet = 
        (Excel.Worksheet)timecard.Worksheets[1];
    NewTimesheet(timesheet);
}
Usa una función auxiliar para inicializar la tarjeta de tiempo en la primera hoja del libro (el botón Borrar tarjeta de tiempo llama a la misma función).

Otras devoluciones de llamada
Las demás devoluciones de llamada son similares a la del botón Nueva tarjeta de tiempo, por lo que no incluiré todo el código, pero hay un par que vale la pena mencionar. Las EditBoxes que albergan el salario por hora y el multiplicador de horas extra son un poco más complicadas que los botones, porque necesitan dos devoluciones de llamada para comportarse correctamente. Estas son las devoluciones de llamada para el cuadro de salario:
double m_Wage = 5.25;

public void OnWageChanged(Office.IRibbonControl c, string wage)
{
    m_Wage = double.Parse(wage);
}

public string GetWage(Office.IRibbonControl c)
{
    return m_Wage.ToString();
}
OnWageChanged es llamada cuando el usuario escribe un nuevo valor en el cuadro de salario. Este ejemplo convierte y registra el nuevo valor, pero un complemento más realista probablemente querría hacer una comprobación y validación adicional de algunos límites en el valor antes de aceptarlo.
El método GetWage proporciona el valor inicial para el EditBox. Es importante tener en cuenta que también es llamada cuando el objeto IRibbonUI invalida la interfaz de usuario. Los complementos no deberían basarse en el hecho de que la entrada del usuario se quedará en la EditBox, por lo que recomiendo que realicen un seguimiento del valor actual usando onChanged y getText como en este ejemplo (OnWageChanged y GetWage).
El complemento almacena el valor del salario como una variable de miembro en el complemento, lo que es correcto ya que el valor es global para el complemento y no específico del documento. Si este valor estuviera unido a cada tarjeta de tiempo individual, tendría que almacenarlo en memoria asociado a la tarjeta de tiempo, de modo que pudiera actualizarse bien cuando el usuario cambiara entre dos tarjetas de tiempo.
El otro control interesante es el botón Firmar tarjeta de tiempo. Permite que emerja un diálogo para que el usuario introduzca su nombre y, posteriormente, escribe ese nombre en la tarjeta de tiempo:
public void SignTimecard(Office.IRibbonControl button)
{
    SignatureDialog sd = new SignatureDialog();
    if (DialogResult.OK == sd.ShowDialog())
    {
        string signature = sd.GetSignature();

        timesheet.get_Range("E" + (daycount + 5), 
        Missing.Value).Formula = signature;

        m_Ribbon.InvalidateControl("SubmitTimecard");
    }
}
También llama a IRibbonUI.InvalidateControl en el botón SubmitTimecard. El botón Enviar se deshabilita hasta que se firma la tarjeta de tiempo usando una función getEnabled que comprueba la firma. Cuando la firma está en su sitio, tiene que invalidar el valor devuelto getEnabled para volver a habilitar el botón.
Como el complemento sólo llama a InvalidateControl cuando se hace clic en el botón Firmar tarjeta de tiempo, el botón Enviar no se habilitará si el usuario escribe manualmente su firma en la celda de la tarjeta de tiempo (o se desactiva si el usuario elimina su firma). Si quisiera hacer que funcionase, podríamos enlazar el evento SheetChanged y llamar a InvalidateControl siempre que la celda de firma se modifique.

Conclusión
Esto es todo acerca del complemento para Tarjetas de tiempo. El complemento completo sólo ocupa unos cientos de líneas de código, incluyendo todo el código que Visual Studio genera automáticamente. La verdad es que es bastante simple y necesita mucho más código de comprobación de errores y validación antes de poder usarlo, pero demuestra lo fácil que es usar RibbonX para aprovechar Office como una plataforma para sus propias aplicaciones. Si le interesa probarlo, puede descargar el código fuente.
En este artículo sólo se ha tratado superficialmente la funcionalidad de RibbonX, de modo que si quiere más detalles y código de ejemplo, visite la página oficial de MSDN. Hay vínculos a la documentación completa de RibbonX, listas de Id. de control, complementos de muestra y blogs relacionados con RibbonX.

Eric Faller es desarrollador de software en el equipo Office User Experience de Microsoft y está trabajando para hacer que la nueva interfaz de usuario sea extensible a los desarrolladores. Se puede poner en contacto con él a través de eric.faller@microsoft.com.

Page view tracker