Recorrer la estructura de un menú por código

A partir de Visual Studio 2005, el diseño de menús para Windows Forms experimentó una mejora considerable gracias a la incorporación de la clase MenuStrip a la plataforma .NET, a la que acompañaban también todo el conjunto de clases relacionadas con ella: ToolStripMenuItem, ToolStripSeparator, ToolStripTextBox, etc., y que incrementaban notablemente las funcionalidades aportadas por la clase dedicada hasta ese momento a tales menesteres: MainMenu.


La creación de un menú utilizando la clase MenuStrip, nos permite definir sus opciones con características que están más allá del mero literal que muestra el título de la opción, siendo posible que su comportamiento sea el de una caja de texto o una lista desplegable.


No obstante, la finalidad del presente artículo no es mostrar las características particulares de tales tipos de opción de menú, sino exponer un ejemplo que nos permita recorrer su estructura de opciones, realizando cambios en las mismas, en función de si pertenecen a un tipo determinado o poseen ciertas características.


Para ello partiremos de un formulario al que añadiremos un menú con el conjunto de opciones que vemos en la siguiente imagen.



Entre las opciones de este menú podemos observar (indicadas con flechas en la imagen) que se encuentran dos de tipo caja de texto -objetos ToolStripTextBox-, mientras que en el resto existen algunas cuyo literal comienza por minúscula. El objetivo del proceso a desarrollar será cambiar a negrita y cursiva el tipo de letra de las opciones situadas en la barra de menú, convertir a mayúscula el contenido de las opciones de tipo ToolStripTextBox, y situar una marca de verificación junto a las opciones que empiezan por minúscula.


Dado que un menú puede organizarse -tal y como vemos en este ejemplo- en opciones que a su vez despliegan nuevos conjuntos de opciones dependientes, se hace necesario implementar un algoritmo recursivo que permita llegar a todos los niveles de submenús existentes en su estructura.


Sin embargo, antes que entrar en la creación de dicho algoritmo, nos ocuparemos de las dos opciones que se encuentran en la barra de menú, cuyo tipo de letra vamos a convertir a negrita y cursiva recorriendo la colección MenuStrip.Items como vemos a continuación. Todas las operaciones de transformación del menú las realizaremos desde el evento Click de un control Button situado en el formulario.


private void btnModificar_Click(object sender, EventArgs e)
{
// crear un nuevo tipo de letra para las opciones de la barra de menú
Font fntTipoLetra = new Font(menuStrip1.Items[0].Font.FontFamily,
menuStrip1.Items[0].Font.Size,
FontStyle.Bold | FontStyle.Italic);

// recorrer las opciones del menú
foreach (ToolStripMenuItem mnuitOpcion in this.menuStrip1.Items)
{
// cambiar el tipo de letra
mnuitOpcion.Font = fntTipoLetra;
}
}


Después de ejecutar el anterior código habremos realizado la transformación de las opciones de menú de la barra… ¡¡¡y también del resto de opciones!!! =8-O, ya que un cambio de estas características, realizado en la opción principal de la barra, se propaga al resto de opciones del mismo tipo que dicha opción despliega, por lo que las opciones de tipo ToolStripTextBox no se verán afectadas.



Puesto que solamente deseamos modificar el tipo de letra en la barra de menú, guardaremos el objeto Font original en una variable, restaurándolo al recorrer las opciones de los menús desplegables.


En lo que respecta al proceso encargado de recorrer estos menús desplegables, el modo de detectar si una opción de menú tiene un submenú asociado, pasa por comprobar su propiedad DropDownItems, la cual es una colección de objetos ToolStripItem.


private void btnModificar_Click(object sender, EventArgs e)
{
// obtener el tipo de letra original de las opciones de menú
// para restaurarlo en las opciones desplegables
Font fntOriginal = this.menuStrip1.Items[0].Font;
//….
// recorrer las opciones del menú
foreach (ToolStripMenuItem mnuitOpcion in this.menuStrip1.Items)
{
//….
// si esta opción despliega un submenú
// llamar a un método para hacer cambios
// en las opciones del submenú
if (mnuitOpcion.DropDownItems.Count > 0)
{
this.CambiarOpcionesMenu(mnuitOpcion.DropDownItems, fntOriginal);
}
}
}

Para cada opción de menú situada en la barra comprobaremos si despliega un submenú, y en caso afirmativo, llamaremos a un método con el nombre CambiarOpcionesMenu, que será ejecutado recursivamente cada vez que volvamos a encontrarnos con una opción que despliegue nuevos elementos. Este método recibirá como parámetros la colección de opciones a recorrer, y el objeto Font original a restaurar sobre las opciones.


private void CambiarOpcionesMenu(ToolStripItemCollection colOpcionesMenu, Font fntTipoOriginal)
{
// recorrer el submenú
foreach (ToolStripItem itmOpcion in colOpcionesMenu)
{
//….
// si esta opción a su vez despliega un nuevo submenú
// llamar recursivamente a este método para cambiar sus opciones
if (((ToolStripMenuItem)itmOpcion).DropDownItems.Count > 0)
{
this.CambiarOpcionesMenu(((ToolStripMenuItem)itmOpcion).DropDownItems,
fntTipoOriginal);
}
}
}

Al recorrer la colección ToolStripItemCollection, cada uno de sus elementos es un tipo de la clase base ToolStripItem. Es por ello que deberemos realizar una operación de conversión de tipos, para obtener el tipo de opción adecuado, y aplicarle los cambios pertinentes cuando sea necesario. Seguidamente podemos ver el código fuente del ejemplo al completo.


private void btnModificar_Click(object sender, EventArgs e)
{
// obtener el tipo de letra original de las opciones de menú
// para restaurarlo en las opciones desplegables
Font fntOriginal = this.menuStrip1.Items[0].Font;

// crear un nuevo tipo de letra para las opciones de la barra de menú
Font fntTipoLetra = new Font(menuStrip1.Items[0].Font.FontFamily,
menuStrip1.Items[0].Font.Size,
FontStyle.Bold | FontStyle.Italic);

// recorrer las opciones del menú
foreach (ToolStripMenuItem mnuitOpcion in this.menuStrip1.Items)
{
// cambiar el tipo de letra
mnuitOpcion.Font = fntTipoLetra;

// si esta opción despliega un submenú
// llamar a un método para hacer cambios
// en las opciones del submenú
if (mnuitOpcion.DropDownItems.Count > 0)
{
this.CambiarOpcionesMenu(mnuitOpcion.DropDownItems, fntOriginal);
}
}
}

private void CambiarOpcionesMenu(ToolStripItemCollection colOpcionesMenu, Font fntTipoOriginal)
{
// recorrer el submenú
foreach (ToolStripItem itmOpcion in colOpcionesMenu)
{
// restaurar el tipo de letra original
itmOpcion.Font = fntTipoOriginal;

// si la opción de menú es de tipo caja de texto
// pasar su contenido a mayúsculas
if (itmOpcion.GetType() == typeof(ToolStripTextBox))
{
itmOpcion.Text = itmOpcion.Text.ToUpper();
}

// si es una opción de menú normal…
if (itmOpcion.GetType() == typeof(ToolStripMenuItem))
{
// … y el primer caracter está en minúscula
// poner en la opción una marca de verificación
if (char.IsLower(itmOpcion.Text.ToCharArray(0, 1)[0]))
{
((ToolStripMenuItem)itmOpcion).Checked = true;
}

// si esta opción a su vez despliega un nuevo submenú
// llamar recursivamente a este método para cambiar sus opciones
if (((ToolStripMenuItem)itmOpcion).DropDownItems.Count > 0)
{
this.CambiarOpcionesMenu(((ToolStripMenuItem)itmOpcion).DropDownItems,
fntTipoOriginal);
}
}
}
}


En la siguiente imagen podemos ver el resultado de ejecutar el anterior código sobre nuestro menú, con el que hemos logrado modificar dinámicamente las propiedades de sus elementos.



Los proyectos de Visual Studio con los fuentes correspondientes a este ejemplo, pueden descargarse en los siguientes enlaces para C# y VB.


Espero que os resulte de utilidad.


Un saludo.

Sin categoría

29 thoughts on “Recorrer la estructura de un menú por código

  1. Hola Álvaro

    Gracias por tu opinión 8-).Al final del post tienes un par de enlaces desde los que puedes descargar el código fuente, uno de ellos corresponde a VB.

    Un saludo,
    Luismi

  2. Interesante post, me ha servidor para tomar un par de ideas para traducir tanto un MenuStrip como un TreeView (funcionan parecido). Gracias por el trabajo.

  3. Excelente ejemplo, pero tengo una consulta.
    Necesito hacer que el menu no se oculte al hacer clic en el ultimo hijo del menu.
    Lo necesito porque en mi aplicación tengo que dejar varias opciones del menu en checkeadas a la vez, pero al esconderse el menu cuando hago clic tengo que volver a recorrer el menu completo por cada vez que checkeo el ultimo hijo del arbol del menu… no se si me entiendes.

    Ejemplo:

    Menu
    ..opcion1
    ….sub opcion 1
    ….sub opcion 2
    ….sub opcion 3
    ….sub opcion 4
    ….sub opcion 5

    la idea es poder dejar checkeadas algunas de las sub opciones sin tener que recorrer todo el menu por cada opcion que quiero chekear.

    Eso, ojala sepas como podría hacerlo.

    Gracias.

  4. Hola Luis Felipe

    Gracias por tu opinión sobre el post, y respecto al problema que mencionas, creo que lo podrías resolver de la siguiente manera:

    Supongamos que tienes un menú con esta estructura:

    -Archivo
    —-Abrir
    —-Guardar
    —-Guardar como
    —-Salir

    En la opción Archivo, asignas a su propiedad Text el valor &Archivo, lo que nos permite acceder a esta opción a través de la combinación de teclas ALT+a para desplegarlo.

    Para el resto de opciones, en la propiedad CheckOnClick asignas el valor True.

    A continuación pasamos al código del formulario y asociamos varias opciones desplegadas al mismo manipulador de evento Click. En este manipulador, a través de la clase SendKeys, obligamos a que cuando se pulse sobre una de las opciones, se envíe la combinación de teclado ALT+a, lo que vuelve a desplegar el menú.

    //—————————–
    public partial class Form1 : Form
    {
    public Form1()
    {
    InitializeComponent();

    this.abrirToolStripMenuItem.Click += DesplegarMenu;
    this.guardarToolStripMenuItem.Click += DesplegarMenu;
    this.guardarComoToolStripMenuItem.Click += DesplegarMenu;
    }

    private void DesplegarMenu(object sender, EventArgs e)
    {
    SendKeys.Send(“%a”);
    }
    }
    //—————————–

    Espero que te sirva para solucionar tu caso.

    Un saludo.
    Luismi

  5. necesito enlazar varios formularioa en VB y no se me a dado….espero me ayuden…
    archivo
    clientes
    empresa
    algo asi…pero en visual 6.. es facil…pero ahora con el visual 2008..estoy algo perdida…espero me puedan ayudar….gracias

  6. Barbaro Luis. Lo que necesito es visualizar o no una opecion del menu dependiendo del perfil de usario que se le elija.
    Ejemplo: si el usuario es operativo solo dejar que acceda a la opcion de “venta”. Por supuesto que las otras opciones de menu estaran en otro estado.

    Gracias

  7. Hola Sergio

    Antes de ejecutar el proceso que recorre la estructura de opciones del menú, puedes obtener el perfil del usuario y comprobar sus características, de modo que en el proceso que recorre el menú, si el usuario que ha accedido a la aplicación no tiene permisos para ver unas determinadas opciones, a la propiedad Visible de dichas opciones le asignas el valor False.

    Para almacenar el perfil del usuario supongo que puedes utilizar una base de datos en la que tengas una tabla con los usuarios y otra con las opciones a las que los diferentes usuarios tienen acceso.

    Un saludo.
    Luismi

  8. Muy bueno el artículo, pero cómo puedo saber si al recorrer el MenuStrip, el elemento en el que estoy es de nivel superior, o es dependiente de otro. Ejemplo: Archivo –> Abrir

    Gracias.

  9. Hola Diego

    Gracias por tu opinión, y respecto a tu pregunta, una posible solución sería utilizar una variable numérica, en la que asignaremos el número de nivel, cada vez que vayamos a entrar en un grupo de opciones de menú, algo similar a lo siguiente:

    //———————————
    public partial class Form1 : Form
    {
    private int nNivel = 0;
    //….

    private void btnModificar_Click(object sender, EventArgs e)
    {
    //….
    if (mnuitOpcion.DropDownItems.Count > 0)
    {
    nNivel++;
    this.CambiarOpcionesMenu(mnuitOpcion.DropDownItems, fntOriginal);
    }
    //….

    private void CambiarOpcionesMenu(ToolStripItemCollection colOpcionesMenu, Font fntTipoOriginal)
    {
    //….
    if (((ToolStripMenuItem)itmOpcion).DropDownItems.Count > 0)
    {
    nNivel++;
    this.CambiarOpcionesMenu(((ToolStripMenuItem)itmOpcion).DropDownItems,
    fntTipoOriginal);
    }
    //….
    //———————————

    Otra forma de resolverlo sería utilizar la propiedad Tag de los objetos ToolStripMenuItem. A esta propiedad le asignaríamos el número de nivel en tiempo de diseño, y en tiempo de ejecución recuperaríamos ese valor para saber en qué nivel del menú nos encontramos.

    Un saludo.
    Luismi

  10. es entendible con el ejemplo la forma de funcionamiento de este componente, pero como seria el código para que al hacer click sobre una de las opciones te envie a un form

  11. Hola Blanca

    Para abrir un nuevo formulario que tengas en tu proyecto al hacer clic en una de las opciones del menú, lo que debes hacer es escribir el manipulador del evento Click de la opción de menú que necesites, y allí realizar la instanciación del nuevo formulario.

    Por ejemplo, supongamos que en el proyecto tenemos un formulario llamado frmDatos, y queremos que al hacer clic en la opción Abrir del menú se instancie un nuevo objeto de dicho formulario. El código a utilizar sería el siguiente:

    //———————————
    private void abrirToolStripMenuItem_Click(object sender, EventArgs e)
    {
    frmDatos ofrmDatos = new frmDatos();
    ofrmDatos.Show();
    }
    //———————————

    Un saludo,
    Luismi

  12. Hola Tavito

    Al final del texto de este post tienes dos enlaces con el código fuente para cada lenguaje: C# y VB.

    Un saludo.
    Luismi

  13. Haber me parece muy interesante tu nota sobre MenuStrip, haber; si yo quisiera tenes bd con una tabla que me guardara los name de todo el menu excepto los separadores ¿Como lo haria?.

    y segundo ,si quisiera Habilitar o Hinabilitar uno o varios menus dependiendo de un rol de usuarios que se guarde en otra table ¿Como lo Haria?

    Te recuerdo que trabajo con c# vs2010

    Te agradezco mi correo es marcos301 arroba yahoo punto com

  14. Hola Marcos

    Gracias por tu opinión. Respecto al planteamiento que comentas, puedes crear una tabla en tu bbdd con una estructura en la que haya un campo por cada propiedad que necesites controlar para tus opciones de menú: Nombre, Checked, Enabled, etc.

    En esta tabla añadirías un registro por cada opción de tu menú. También podrías incluir un campo que serviría como indicador o flag, que según su valor, informara de si la opción tiene a su vez opciones dependientes.

    De esta forma, al iniciar tu aplicación, un proceso recursivo, similar al que se expone en este artículo, recorrería tu tabla e iría leyendo los registros y creando los diferentes objetos ToolStripMenuItem, construyendo así el menú del formulario.

    Un saludo,
    Luismi

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *