Marco Besteiro y Miguel Rodríguez Menús Menús Hoy en día la mayoría de las aplicaciones para Windows tienen menús. Es realmente sencillo trabajar con ellos con C#. Hay tres tipos de menús y los tres derivan de una clase común, denominada Menu, que proporciona la funcionalidad básica y común a las tres. En la figura 17.1 se puede apreciar la jerarquía de estas clases. Object MarshalByRefObject MarshalByRefComponent Menu MainMenu ContextMenu MenuItem Figura 17.1. Jerarquía de las clases para el trabajo con menús. Las clases MainMenu y MenuItem MainMenu es el menú principal. Un formulario puede tener definidos varios menús principales, dependiendo del estado de algunas variables de la aplicación. Estos menús pueden unirse entre sí. Cada uno de los elementos o items o submenús –ya sean de primer o segundo nivel- de cada menú son objetos de la clase MenuItem. Estos elementos, a su vez, pueden contener otros objetos o submenús. Todos los elementos u objetos de un menú son almacenados en la colección MenuItemCollection que es una propiedad de las clases MainMenu y ContextMenu. Los menús contextuales o también llamados menús PopUp se despliegan generalmente cuando se pulsa sobre un control con el botón derecho. Al igual que los MainMenu 1/15 Marco Besteiro y Miguel Rodríguez Menús pueden contener varios elementos MenuItem. A continuación se estudiarán las propiedades más importantes de la clase MenuItem. Posteriormente se estudiarán los menús PopUp y se realizará un ejemplo básico. Propiedades Checked y RadioChecked DefaultItem Enabled MdiList MergeOrder MergeType OwnerDraw ShortCut ShowShortCut Text Visible Propiedad booleana. Indica si un elemento aparece activado con un elemento tipo Check o RadioCheck. Para que éste último tenga funcionalidad debe estar a True la propiedad Checked. Propiedad booleana. Indica si es el elemento por defecto o predeterminado. Se escribe en letra negrita. Propiedad booleana. Indica si está habilitada o no esa opción de menú. Propiedad booleana. Indica si la lista de ventanas hijas MDI se añadirá a este item. Indica el orden donde será ubicado el menú. Indica el tipo de unión de los menús. Propiedad booleana. Indica si será Windows o el programador el responsable de dibujar el item o elemento de menú. Indica la combinación de teclas o acceso directo asociado con ese elemento. Propiedad booleana. Indica si aparece escrito a la derecha del item la combinación de teclas asociada al item. Texto del elemento. Indica si un item es visible o no. Es un propiedad booleana. Eventos Click DrawItem MeasureItem PopUp Select Ocurre cuando se pulsa un item con el ratón. Ocurre si un item de menú necesita ser redibujado. Ocurre justo antes de que sea redibujado un item. Ocurre cuando se presentan en pantalla un submenú. Ocurre cuando se selecciona un item del menú. Hay además dos métodos interesantes de esta clase: CloneMenu que permite crear un nuevo menú a partir de otros existentes y MergeMenu que permite unir dos menús. Para trabajar con menús se utiliza la clase MenuItemCollection que almacena los elementos de cada menú. Para añadir y eliminar elementos de menú a MainMenu o ContextMenu se utilizan los métodos de esta colección. La clase ContextMenu Es el menú que aparece cuando se pulsa con el botón derecho del ratón. Se trabaja de la misma manera que un MainMenu con la única diferencia de que no existe un nivel 2/15 Marco Besteiro y Miguel Rodríguez Menús principal de items y por lo tanto no se puede nombrar el principal. Los menú PopUp contienen también objetos de la clase MenuItem. Una vez diseñado el menú es necesario asociarlo a un control o a un formulario por medio de la propiedad ContextMenu del control. Ejemplo: Trabajando con menús. Como en los demás casos, es posible añadir un menú a un formulario de dos modos: Escribiendo directamente el código o bien mediante el asistente de Visual Studio. En la práctica, casi siempre se trabajará con esta segunda opción. A continuación se implementa una aplicación en la cual se desea escribir un determinado texto en una caja de texto por medio de la pulsación de un botón o a través de un menú. Para ello: • • Cree un proyecto llamado HolaMundoConMenu. Añada un botón con sus propiedades Name y Text a btnAceptar y Aceptar respectivamente. Añada una caja de texto vacía de propiedad Name texto. Escriba el código necesario para que al pulsar sobre el botón btnAceptar se escriba en la caja de texto una determinada frase, por ejemplo, “Zidane es el mejor jugador del planeta”. private void btnAceptar_Click_1(object sender, System.EventArgs e) { texto.Text="Zidane es el mejor jugador del planeta"; } • Añada el control MainMenu. Este control no es visible en tiempo de diseño. Cuando se añada al formulario se sitúa en la parte inferior del IDE que Visual Studio utiliza para almacenar este tipo de controles. Con esto, el IDE indica que son controles que no forman parte directa del diseño gráfico de la aplicación. Sin embargo, con estos controles se trabaja de manera parecida al resto de los controles. En realidad se sitúan aquí todos los controles cuyas clases no derivan de la clase Control como Menu, ImageList, Time, ToolTip, etc. • En la parte superior izquierda del formulario aparece un asistente para crear las opciones de menú. Para ello, debe estar seleccionado el control MainMenu que se ha añadido (figura 17.2). 3/15 Marco Besteiro y Miguel Rodríguez Menús Figura 17.2. Añadiendo un control MainManu al formulario. Para añadir nuevas opciones de menú –submenús- sólo hay que hacer “doble click” sobre las opciones de menú en las que está escrito “Escriba aquí”. Para añadir el código de manejo de los eventos de menú se ha de hacer igual que con los botones de comando, es decir, a través de la ventana propiedades o haciendo doble click en la opción de menú. A modo de ejemplo se añade una opción de menú de primer nivel llamada “Archivo”, que tendrá otras dos segundo nivel: la primera que ejecute lo mismo que el botón btnAceptar y que lleva el texto “Ejecutar botón Aceptar” y la segunda que cierre la aplicación, “Salir”. Para dibujar una línea que divida en regiones un subnivel, basta con poner como texto de un subnivel el guión (-) (Figura 17.3). 4/15 Marco Besteiro y Miguel Rodríguez Menús Figura 17.3. Diseñando un menú. Línea separadora. Menu 2.bmp Para asignar el mismo método al menú que al botón, seleccione la opción de menú “Ejecutar botón Aceptar” y en la ventana de propiedades, pulse sobre el icono “eventos” y seleccione el evento click. Aparecerá un control ComboBox que le permitirá definir el método manejador de este evento entre los que hasta ahora están escritos. Seleccione btnAceptar_Click (Figura 17.4). Figura 17.4. El evento Click del menú y del botón son manejados por el mismo método. 5/15 Marco Besteiro y Miguel Rodríguez Menús No se añadirá ningún código, pero se asocia este evento al método. Compruebe que el código de la aplicación aparecen las dos líneas siguientes: this.btnAceptar.Click += System.EventHandler(this.btnAceptar_Click); this.menuItem2.Click += System.EventHandler(this.btnAceptar_Click); new new que indican que los eventos btnAceptar.Click y menuItem2.Click del formulario serán manejados ambos por el método btnAceptar_Click. Para salir de la aplicación al pulsar la opción Salir (menuItem4), habrá que implementar el método menuItem4_Click. Para ello, con esta opción de menú seleccionada, pulse sobre el icono de Eventos en la ventana de propiedades y seleccione el evento Click. Haga doble click sobre él y escriba el siguiente código: private void menuItem4_Click(object sender, System.EventArgs e) { Application.Exit(); } Se utiliza el método estático Exit() de la clase Application, que provoca que se salga de la aplicación. Ejecúte la aplicación y compruebe las distintas posibilidades. Nota: Observe que en la propiedad Menu, de la ventana de propiedades del formulario aparece mainMenu1 que es el menú asociado al formulario. Si se define otro menú, se puede cambiar esta propiedad en tiempo de ejecución, de modo que se pueden tener varios menús asociados a un determinado formulario. 6/15 Marco Besteiro y Miguel Rodríguez Menús La clase ImageList Este control proporciona una colección para almacenar imágenes que puede ser utilizadas en otros controles como una caja de herramientas o un ListView. Aunque puede almacenar imágenes de cualquier tamaño, las que se añaden a un mismo control deben tener –en general- el mismo. Sin embargo, en el caso del control ListView –y a veces las cajas de herramientas- es preciso almacenar dos tipos de tamaños de imágenes. Este control no es visible en tiempo de diseño ni en tiempo de ejecución, aunque se le pueden añadir imágenes en tiempo de diseño y en tiempo de ejecución. La clase ToolBar Los controles ToolBar se usan para construir una barra de herramientas. Un buen ejemplo lo constituye la barra de herramientas del Word 2000 (figura 17.5) Figura 17.5. Barra de herramientas del Word 2000 Una barra de herramientas contiene, por lo general, varios controles de la clase ToolBarButton que pueden aparecer como botones estándar –como por ejemplo los botones Abrir, Guardar, ...-, controles ComboBox –como el tipo de letra- botones desplegables –como los iconos color de la letra, resaltar o ayuda- , botones ToogleButton o de dos posiciones o de ON/OFF –como el tipo de alineación del párrafo, y botones con texto, imagen o ambos. Además, normalmente suelen disponer de una pequeña etiqueta o ToolTip, con una breve descripción de la función del botón. En general, las barras de herramientas sirven para acceder de manera más rápida y gráfica a algunos de los elementos más importantes o más utilizados de un menú. Se puede asociar imágenes a cada uno de los botones a través de un objeto ImageList, asignando el índice de cada imagen a la propiedad ImageIndex del botón. También se puede asignar un texto a la derecha o en la parte inferior del botón a través de la propiedad Text del ToolBarButton. La apariencia de la barra de herramientas viene definida por la propiedad Appearance. Por ejemplo, puede ser la apariencia normal o Flat que es el aspecto exterior que tiene la barra de herramientas de Visual Studio .NET o el Word 2000 de Microsoft, donde los botones no tienen relieve, pero que al pasar con el ratón sobre ellos, adquieren un aspecto tridimensional. Los botones de la caja de herramientas se pueden agrupar en 7/15 Marco Besteiro y Miguel Rodríguez Menús unidades lógicas utilizando los separadores. Un separador es un botón de la barra de herramientas con la propiedad Style en Separator. Para crear una colección de botones de la barra de herramientas se ha de utilizar la propiedad Buttons de dicho control. Es posible añadir o eliminar controles en tiempo de diseño o utilizando los métodos Add o Insert de la propiedad Buttons. Esta clase deriva de Control y por lo tanto hereda todas sus propiedades, métodos y eventos. Además tiene otras propiedades muy útiles: Propiedades Buttons Es una colección que contiene los botones de la barra de herramientas – de la clase ToolBarButton-. ImageList Es una colección que contiene las imágenes de los botones de la barra de herramientas. ShowToolTips Propiedad booleana. Permite que aparezcan las pequeñas etiquetas (ToolTips) con alguna explicación de la acción de cada botón. Wrappable Propiedad booleana. Permite que si la barra de herramientas es muy grande, se amplíe a otra línea de botones. ButtonSize Determina el tamaño de los botones en la barra de herramientas. Eventos Además de los eventos que hereda de la clase Control, hay dos que tienen especial importancia y son específicos de este control: ButtonClick Ocurre siempre que se pulsa un botón de la barra de herramientas. En el método auditor de este evento se pasan dos parámetros. El primero, sender es el objeto que ha generado el evento y el otro es un objeto de la clase ToolBarButtonClickEventArgs que encapsula información sobre el evento. Para determinar qué botón – de la clase ToolBarButton- de la barra de herramientas se ha pulsado se puede utilizar la propiedad Button de esta clase que devuelve el botón pulsado. Es el evento por defecto. ButtonDropDrown Ocurre cuando se pulsa un botón de la barra de herramientas con la propiedad Style a DropDownButton. Para determinar el botón que se ha pulsado se procede como en el caso anterior. Los botones de la barra de herramientas son una colección de objetos de la clase ToolBarButton que se asocian a la barra por medio de la propiedad Buttons. Este es un procedimiento muy sencillo en tiempo de diseño porque Visual Studio proporciona un editor para añadir botones a la colección. A continuación se estudian este tipo de botones. 8/15 Marco Besteiro y Miguel Rodríguez Menús La clase ToolbarButton Los objetos de esta clase son los controles que se ubican en la barra de herramientas. Esta clase no deriva de la clase Control, sino directamente de la clase Component. Propiedades Esta clase permite configurar para cada botón de la caja de herramientas la imagen, estilo, texto y ToolTip. A través de ellos se puede configurar un botón “desplegable” como, por ejemplo los dos botones situados en la parte superior derecha de la barra de herramientas del Visual Studio .NET. Las propiedades más importantes son: DropDownMenu ImageIndex Style Text ToolTipText ParcialPush Pushed Asigna un menú de tipo PopUp a un botón “desplegable”. Para ello, debe tener la propiedad Style a DropDownButton-. Contiene el índice de la imagen del botón. Es el estilo del botón. La etiqueta o texto –opcional- del botón en la barra de herramientas. Es el texto de la etiqueta explicativa del botón. Para ello, la barra de herramientas debe tener la propiedad ShowToolTips a True. Sólo tiene sentido con la propiedad Style a ToogleButton. Si es True, los botones están en gris, indicando que están inhabilitados. Propiedad booleana. Indica si el botón está pulsado o no (ON/OFF). Para controlar los eventos sobre la barra de herramientas, se procede como en los demás controles. Cuando se desea determinar qué botón ha sido pulsado, se utiliza la propiedad Button que devuelve el botón que se ha pulsado- de la clase ToolBarButtonClickEventArgs que se ha pasado al auditor del evento. Ejemplo: Construyendo una barra de herramientas. A continuación se desea implementar un apalicación con una barra de herramientas. El aspecto final de esta aplicación es el de la figura 17.7. Para ello. se deben seguir los siguientes pasos: • • Cree un nuevo proyecto para una Aplicación Windows y llámele ProyectoBarraDeHerramientas. Cambie el nombre de la clase que define a la aplicación de Form1 a FormularioPrincipal. Para ello, seleccione Form1 en la ventana Vista de clases y en la ventana de propiedades cambie la propiedad Name de la clase a FormularioPrincipal. 9/15 Marco Besteiro y Miguel Rodríguez Menús • Aunque no es necesario hacerlo así, cambie también el nombre del fichero de la clase, para que coincida con el de la clase. Para ello, o bien en la ventana Explorador de Soluciones seleccione el fichero Form1.cs, pulse el botón derecho del ratón y seleccione la opción del menú contextual “Cambiar nombre” o bien con Form1.cs seleccionado en el Exploracdor de Soluciones, en la Ventana de propiedades modifique la propiedad “Nombre de archivo” a FormularioPrincipal.cs. • Al menos, en la versión beta es necesario cambiar en el código del formulario la línea: Application.Run(new Form1()); por la línea Application.Run(new FormularioPrincipal()); • Este es un buen momento para salvar la aplicación. • Añada un control ImageList al formulario. Se ubicará en una zona destinada a los controles no visuales. Modifique la propiedad Name a imagenes. • Añada cuatro imágenes a la colección de imágenes que está definida en la propiedad Images del control. Pulse en el pequeño botón situado a la derecha del nombre de la palabra Collection de la propiedad Images y se abrirá un editor para añadir o eliminar imágenes a la colección de imágenes del control. Para añadir una imagen, pulse el botón Agregar del editor y escoja desde, por ejemplo, C:\Archivos de programa\Microsoft Visual Studio.NET\Common7\Graphics\icons\Misc los iconos MISC06.ICO, MISC05.ICO, MISC04.ICO y TIMER01.ICO. Pulse el botón Aceptar para cerrar el editor. En caso de equivocación, puede eliminar una imagen seleccionándola y pulsando Quitar o puede cambiarse la ubicación relativa de las imágenes en la colección por medio de las flechas. • Añada tres ContextMenu que posteriormente serán los menús que se desplieguen al pulsar en uno de los botones, en la barra de herramientas y en el formulario respectivamente y cambie sus propiedades Name a menuPopUpBoton, menuPopUpBarra y menuPopUpFormulario y añada respectivamente las siguientes opciones a cada menú: Hora de Londres, Hora de Los Angeles, Hora de Moscú. Arriba, Abajo, Izquierda, Derecha. Ver barra de herramientas, Ocultar barra de herramientas. Añada un control ToolBar al formulario. Por defecto se sitúa en la parte superior de éste, aunque puede ubicarse en cualquier otro lugar modificando la propiedad Dock. Modifique o defina las siguientes propiedades de la barra de herramientas: Name Appearance BorderStyle ContextMenu ImageList ShowToolTips barra Flat FixedSimple menuPopUpBarra imagenes true 10/15 Marco Besteiro y Miguel Rodríguez Menús Observe que se asocia a este control uno de los tres menús PopUp o contextuales, a través de la propiedad ContextMenu. Además, cuando se define la propiedad ImageList se asocia las imágenes de dicho control a la barra. La propiedad ShowToolTips asocia pequeñas etiquetas autoexplicativas a cada botón. A continuación, se procede a añadir los botones a la barra de herramientas. Para ello, los botones se añaden a la propiedad Buttons de la Barra de herramientas que es una colección de objetos de tipo ToolBarButton. Se puede también hacer esta operación en tiempo de ejecución. Sin embargo, si es posible, es mejor hacerlo en tiempo de diseño. Desde la propiedad Buttons, pulse el botón situado a la derecha de la palabra Collection. Se presenta en pantalla un editor para añadir, eliminar y editar las propiedades de los distintos botones (figura 17.6). Añada cuatro botones con las siguientes propiedades que se asignarán en el propio editor, como se indica en la figura: Name ImageIndex DropDownMenu ToolTextTip Text Style boton0 0 None Botón cero Name ImageIndex DropDownMenu ToolTextTip Text Style boton1 1 None Botón uno Name ImageIndex DropDownMenu ToolTextTip Text Style boton2 2 None Botón dos Name ImageIndex DropDownMenu ToolTextTip boton3 3 MenuPopUpBoton Ubicación de herramientas Text Style PushButton PushButton PushButton la barra de DropDownButton Añada un botón más, que actuará como separador. Sitúelo entre los botones 2 y 3, como se indica en la figura 17.6: Name ImageIndex DropDownMenu ToolTextTip Text Style separador None Separator 11/15 Marco Besteiro y Miguel Rodríguez Menús Figura 17.6. Editor de la colección que contiene los botones del control ToolBar • Asigne a la propiedad ContextMenu del formulario el menú menuPopUpFormulario. En la barra de herramientas, asigne a esta propiedad el menú menuPopUpBarra. Ahora es un buen momento para guardar la aplicación. Ejecútela y vea que aunque los botones de la barra de herramientas no tienen código, al situar el ratón sobre cada uno de ellos se realizar un pequeño efecto 3D y además aparecen las etiquetas ToolTip definidas para cada botón. Observe el separador (figura 17.7). Vea que en la parte derecha del cuarto botón –boton3- aparece una flecha que si se pulsa despliega un menú. Además se puede observar que el hecho de pulsar en el propio botón es distinto que pulsar en la flecha. Por otro lado, si se sitúa en la parte derecha de la barra de herramientas y pulsa con el botón derecho del ratón puede ver que se despliega un menú contextual distinto que si se realiza esta misma acción sobre el formulario, ya que aparecen otras opciones correspondientes al menuPopUpFormulario. • A continuación se va a proceder a cumplimentar el código de los botones. Para ello, se implementará el evento ButtonClick, que es el que ocurre cuando se pulsa cualquiera de los botones de la barra de herramientas. Haga doble click sobre la barra de herramientas, o en la ventana de propiedades, pulse el icono correspondiente a eventos y haga doble click sobre el evento ButtonClick. De manera automática Visual Studio registra el evento ButtonClick para que sea auditado o manejado por el método denominado barra_ButtonClick de la clase añadiendo la línea: barra.ButtonClick+=new ToolBarButtonClickEventHandler(barra_ButtonClick); 12/15 Marco Besteiro y Miguel Rodríguez Menús Implemente el método barra_ButtonClick con el siguiente código: private void barra_ButtonClick(object sender,ToolBarButtonClickEventArgs e) { int indiceDelBotonSeleccionado = barra.Buttons.IndexOf(e.Button); switch (indiceDelBotonSeleccionado) { case 0: MessageBox.Show("Ha pulsado el primer botón"); break; case 1: MessageBox.Show("Ha pulsado el segundo botón"); break; case 2: MessageBox.Show("Ha pulsado el tercer botón"); break; } if(e.Button == boton3) MessageBox.Show("Botón con dos eventos distintos"); } e.Button es el botón pulsado. barra.Buttons es la colección de botones de la barra. Pasándole el botón pulsado al método IndexOf éste devuelve el índice del botón que se ha pulsado. Aunque lo lógico hubiera sido incluirlo en el bloque switch se trata la pulsación del último de los botones a través de una sentencia if, de una manera algo diferente. Debe quedar claro que éste último es el evento click de ese botón, no el evento correspondiente a pulsar sobre la flecha asociada al botón. Para tratar este evento, en la ventana de propiedades, pulse en el icono correspondiente a eventos y haga doble click sobre el evento ButtonDropDown. Por defecto el IDE sugiere el nombre barra_ButtonDropDown. Acéptelo. escriba el siguiente código en el método: private void barra_ButtonDropDown(object sender, ToolBarButtonClickEventArgs e) { //Indica el momento en que ocurre este evento MessageBox.Show("Ha pulsado la flecha"); } 13/15 Marco Besteiro y Miguel Rodríguez Menús Figura 17.7. Aspecto final de la aplicación • Para tratar cada uno de los eventos del menú, se trabaja igual que en un menú PopUp –de hecho lo es-. Para ello, pulse en el control menuPopUpBoton en la ventana de diseño, seleccione la primera opción –Hora de Londres- y haga doble click en la ventana de propiedades en el evento Click. Cambie el nombre que le ofrece el IDE por defecto que será menuItem1_Click por menuItemBoton_Click para poder tratar los tres eventos con un mismo método y escriba el código siguiente: private void menuItemBoton_Click(object sender, System.EventArgs e) { if((MenuItem)sender==menuItem1) MessageBox.Show("Es la hora de Londres"); else if ((MenuItem)sender==menuItem2) MessageBox.Show("Es la hora de Los Angeles"); else if ((MenuItem)sender==menuItem3) MessageBox.Show("Es la hora de Moscú"); } Realmente es un estorbo el hecho de que aparezca una ventana de diálogo cada vez que se pulsa la flecha. Sólo se trata este evento aquí para comprender el momento en que ocurre. • A continuación asocie a los eventos click de los otros dos items de este menú –Hora de Los Angeles y Hora de Moscu- el mismo evento menuItemBoton_Click desde la ventana de propiedades. Proceda del mismo modo con los otros dos menús PopUp y escriba los códigos que a continuación se indican, teniendo cuidado de asociar el mismo nombre para todos los eventos del mismo menú. El primero de ellos ubicará la barra de herramientas en los cuatro bordes del formulario y el correspondiente al formulario ocultará y hará visible la barra de herramientas. private void menuItemBarra_Click(object sender,System.EventArgs e) { if((MenuItem)sender==menuItem4) barra.Dock=DockStyle.Top; 14/15 Marco Besteiro y Miguel Rodríguez Menús else if ((MenuItem)sender==menuItem5) barra.Dock=DockStyle.Bottom; else if ((MenuItem)sender==menuItem6) barra.Dock=DockStyle.Left; else if ((MenuItem)sender==menuItem7) barra.Dock=DockStyle.Right; } private void menuItemFormulario_Click(object e) { if((MenuItem)sender==menuItem8) barra.Visible=true; else if ((MenuItem)sender==menuItem9) barra.Visible=false; } 15/15 sender,System.EventArgs