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.

29 Comentarios

  1. anonymous

    Excelente. Como se traduce a VB net

  2. lmblanco

    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

  3. anonymous

    Exelente, gracias por compartir tus conocmientos, me sirvio de mucho,

  4. lmblanco

    Hola Jurahack

    Muchas gracias por tu interés en el artículo, y me alegra que te haya sido de utilidad.

    Un saludo.
    Luismi

  5. anonymous

    temas interesantes

  6. lmblanco

    Hola karl

    Gracias por leer el post y por tu opinión, espero que te resulte de ayuda.

    Un saludo.
    Luismi

  7. anonymous

    Excelente el articulo lo necesitaba, me ayudo muchisimo… Gracias =)

  8. lmblanco

    Hola Pavel

    Muchas gracias a tí por leer el artículo y me alegra que te resultara útil 😎

    Un saludo,
    Luismi

  9. anonymous

    QUIERO SABER MAS QUE OTRO

  10. anonymous

    y en visual basic 6??

  11. anonymous

    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.

  12. anonymous

    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.

  13. lmblanco

    Hola Albert

    Gracias por tu interés en el post y me alegra que te haya sido de utilidad.

    Un saludo.
    Luismi

  14. lmblanco

    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

  15. anonymous

    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

  16. anonymous

    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

  17. lmblanco

    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

  18. anonymous

    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.

  19. lmblanco

    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

  20. anonymous

    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

  21. lmblanco

    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

  22. anonymous

    podrias facilitar el recorrido el del menu para codigo visual basic forms 2005

  23. lmblanco

    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

  24. anonymous

    mil gracias ya tenia mas de dos dias con esto y no lograba leer todos los subsubsubsubmenus.

    La opcion descargable super util

  25. lmblanco

    Hola Vanesa

    Gracias por el interés en el post y me alegro que te haya servido de ayuda.

    Un saludo.
    Luismi

  26. anonymous

    Compadre eres el mejor un verdadero crack saludos desde Panama

  27. lmblanco

    Hola Joehit

    Gracias por tu interés en el artículo.

    Un saludo
    Luismi

  28. anonymous

    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

  29. lmblanco

    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

Tema creado por Anders Norén