none
Una buen diseño para hacer menús con submenús RRS feed

  • Pregunta

  • Hola gente:

    Me han contado que para hacer menús y submenús en el modo consola C# 2019. Hay que usar el modo programación a objetos. No al estilo chapuza que suelo ahcer con tantos switch.

    Quiero aprender como se hace estas cosas, pensé que hay que hacer primero una estructura o palntilla para poder programar fácil sobre estos menús. Solo hay que usar las flechas de dirección y Enter, ninguna tecla más.

    Un esquema de ejemplo.

    Antes lo hacía de forma procedural o programación clásica o antigua, ahora hay personas que mejor hacerlo en la época actual, ya no estamos en la prehistoria. Por ejemplo, con objetos, que para eso está C#.

    ¿Alguna idea?

    Saludos.


    http://electronica-pic.blogspot.com

    martes, 7 de abril de 2020 16:10

Respuestas

  • Hola Metaconta y Leandro

    Yo tampoco me especializo en Consola sino más bien en Windows Forms (además de procesos lógicos y algoritmos sofisticados y videojuegos), pero es como decis Leandro, hay que tomarse un ratito

    Metaconta, aquí está el programa con la clase MenuTree, con el código aún más compacto y más orientado a objetos

    using System;
    
    namespace ConsoleMenuTreeTheUltimateVersion
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Declaraciones de OBJETOS
                Menu Inicial;
                Menu Principal, OpcionA, OpcionB, OpcionC, Salir;
                Menu OpcionB1, OpcionB2, SalirB, OpcionColor1, OpcionColor2, OpcionC1;
                Menu OpcionSI, OpcionNO;
    
                // Instanciación de OBJETOS
                OpcionNO = new Menu("", Layout.Vertical, null, -4);
                OpcionNO.Name = "OpcionNO";
                OpcionSI = new Menu("Estás en Opción Sí.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionSI.Name = "OpcionSI";
                OpcionC1 = new Menu("Estás en Opción C-1.", Layout.Horizontal, new string[] { "SI", "NO" }, 1);
                OpcionC1.Name = "OpcionC1";
                OpcionC1.subMenus = new Menu[] { OpcionSI, OpcionNO };
                OpcionColor1 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor1.Name = "OpcionColor1";
                OpcionColor2 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor2.Name = "OpcionColor2";
                OpcionB1 = new Menu("Estás en Opción B-1.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB1.Name = "OpcionB1";
                OpcionB2 = new Menu("Estás en Opción B-2.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB2.Name = "OpcionB2";
                SalirB = new Menu("", Layout.Vertical, null, null, -2);
                SalirB.Name = "SalirB";
    
                // opción A
                OpcionA = new Menu("Estás en Opción A.", Layout.Vertical, new string[] { "Pulsa Enter para salir." }, -1);
                OpcionA.Name = "OpcionA";
                // opción B
                OpcionB = new Menu("Estás en Opción B.", Layout.Vertical, new string[] { "Subopción B-1.", "Subopción B-2.", "Salir." }, 1);
                OpcionB.Name = "OpcionB";
                OpcionB.subMenus = new Menu[] { OpcionB1, OpcionB2, SalirB };
                // opción C
                OpcionC = new Menu("Estás en Opción C.", Layout.Vertical, new string[] { "Color 1.", "Color 2.", "Opción C-1." }, 1);
                OpcionC.Name = "OpcionC";
                OpcionC.subMenus = new Menu[] { OpcionColor1, OpcionColor2, OpcionC1 };            
                // opción Salir
                Salir = new Menu("", Layout.Vertical, null, null, -2);
                Salir.Name = "Salir";
    
                // menú principal
                Principal = new Menu("", 0, new string[] { "Opción A", "Opción B", "Opción C", "Salir" }, 1);
                Principal.Name = "Principal";
                Principal.subMenus = new Menu[] { OpcionA, OpcionB, OpcionC, Salir };
                
                // pantalla inicial
                Inicial = new Menu("", Layout.Vertical, new string[] { "MENÚ PRINCIPAL" }, 1);
                Inicial.Name = "Inicial";
                Inicial.subMenus = new Menu[] { Principal };
                Inicial.method = Menu.PrintDate;
    
                MenuTree menuTree = new MenuTree(Inicial);
                
                // ejecuta el "árbol" de menús y termina cuando se pulsa Esc en Menú Principal
                if (menuTree.DoMenuTree() == -1)
                    return;
            }
        }
    
        public enum Layout { Vertical, Horizontal };
    
        // Definición de CLASE DE OBJETOS 
        public class Menu
        {
            public string Name { get; set; }
            public string title { get; set; }     
            public string[] options { get; set; } 
            private Layout layout { get; set; }
            private int index = 0;
    
            public int goWhere { get; set; }
            public Menu[] subMenus { get; set; }
            public Menu beforeMenu { get; set; }
            public Action method;
    
            public Menu() { }
    
            public Menu(string title, Layout layout, string[] options, int goWhere) 
            {
                this.title = title;
                this.layout = layout;
                this.options = options;
                this.goWhere = goWhere;
            }
    
            public Menu(string title, Layout layout, string[] options, Menu[] subMenus, int goWhere)
            {
                this.title = title;
                this.layout = layout;
                this.options = options;            
                this.subMenus = subMenus;
                this.goWhere = goWhere;
            }
    
            public static void PrintDate()
            {
                Console.SetCursorPosition(3, 6);
                Console.Write(DateTime.Now.ToString("ddd dd MMM"));
            }
    
            // DoMenu: Ejecuta el menú y retorna: -1 -> Se pulsó Esc | De 0 a Nro de menús - 1 -> índice de opción elegida
            public int DoMenu()
            {
                int a;
                ConsoleKey key;
                Console.BackgroundColor = ConsoleColor.Green;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.CursorVisible = false;
                Console.Clear();
                Console.SetCursorPosition(12, 2);
                Console.Write(title);
    
                if (method != null) 
                    method();
    
                if (this.title == "" && this.goWhere < 0)
                    return this.goWhere - 5;
    
                do
                {
                    for (a = 0; this.options != null && a < this.options.Length; a++)
                    {
                        if (layout == Layout.Vertical)
                        {
                            Console.SetCursorPosition(2, 4 + a);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2, 4 + a);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4, 4 + a);
                        }
                        else if (layout == Layout.Horizontal)
                        {
                            Console.SetCursorPosition(2 + a * 12, 4);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2 + a * 12, 4);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4 + a * 12, 4);
                        }
                        Console.Write(options[a]);
                    }
    
                    key = Console.ReadKey(true).Key;
    
                    if (key == ConsoleKey.UpArrow || key == ConsoleKey.LeftArrow)
                    {
                        index--;
                        if (index < 0)
                            index = options.Length - 1;
                    }
                    else if (key == ConsoleKey.DownArrow || key == ConsoleKey.RightArrow)
                    {
                        index++;
                        if (index == options.Length)
                            index = 0;
                    }
                    else if (key == ConsoleKey.Enter)
                    {
                        if (this.goWhere < 0)
                            return this.goWhere - 5;
                        return index;
                    }
    
                } while (key != ConsoleKey.Escape);
    
                return -1;
            }
        }
    
        // Definición de otra CLASE DE OBJETOS
        public class MenuTree 
        {
            private Menu menu;
    
            public MenuTree(Menu menu) 
            {
                this.menu = menu;
            }
    
            public int DoMenuTree() 
            {
                int option;
                int limit;
                Menu curMenu = this.menu;
                Menu beforeMenu;
                while (true)
                {
                    option = curMenu.DoMenu();
                    beforeMenu = curMenu;
                    if (option >= 0)
                    {
                        if (curMenu.subMenus != null)
                        {
                            curMenu = curMenu.subMenus[option];
                            curMenu.beforeMenu = beforeMenu;
                        }
                        else if (curMenu.goWhere < 0)
                        {
                            limit = -curMenu.goWhere;
                            for (int a = 0; a < limit; a++)
                                curMenu = curMenu.beforeMenu;
                        }
                    }
                    else if (option == -1)
                    {
                        if (curMenu.beforeMenu != null)
                            curMenu = curMenu.beforeMenu;
                        else
                            return -1;
                    }
                    else if (option < 0)
                    {
                        limit = -(option + 5);
                        for (int a = 0; a < limit; a++)
                            curMenu = curMenu.beforeMenu;
                    }
                }
            }
        }
    }

    Saludos

    Pablo


    • Marcado como respuesta Metaconta miércoles, 29 de abril de 2020 15:51
    miércoles, 8 de abril de 2020 14:25
  • Hola Metaconta

    Fijate que hay un cambio en el método DoMenuTree(), eliminé una parte innecesaria, que te lo pasé en la pregunta del checkbox y radiobutton, para esto es lo mismo, lo probé y anda bien sin esa parte innecesaria. Te paso el método DoMenuTree() ya (creo) sin errores ni cosas de más. 

           public int DoMenuTree() 
            {
                int option;
                int limit;
                Menu curMenu = this.menu;
                Menu beforeMenu;
                while (true)
                {
                    option = curMenu.DoMenu();
                    beforeMenu = curMenu;
                    if (option >= 0)
                    {
                        if (curMenu.subMenus != null && curMenu.subMenus[option] != null)
                        {
                            curMenu = curMenu.subMenus[option];
                            curMenu.beforeMenu = beforeMenu;
                        }
                    }
                    else if (option == -1)
                    {
                        if (curMenu.beforeMenu != null)
                            curMenu = curMenu.beforeMenu;
                        else
                            return -1;
                    }
                    else if (option < 0)
                    {
                        limit = -(option + 5);
                        for (int a = 0; a < limit; a++)
                            curMenu = curMenu.beforeMenu;
                    }
                }
            }

    Saludos


    • Editado Tigre Pablito jueves, 30 de abril de 2020 0:00
    • Marcado como respuesta Metaconta jueves, 30 de abril de 2020 16:22
    miércoles, 29 de abril de 2020 23:58

Todas las respuestas

  • Hola Metaconta

     

    Gracias por levantar tu consulta en los foros de MSDN. Con respecto a la misma, vamos a darte seguimiento e investigaremos para buscar la mejor respuesta para ti.

     

    Gracias por usar los foros de MSDN.

     

    Luis Diego Mora

     ____

     

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde. 

     

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.  

     

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft.

    martes, 7 de abril de 2020 17:06
    Moderador
  • hola

    >>Me han contado que para hacer menús y submenús en el modo consola C# 2019. Hay que usar el modo programación a objetos

    quien te ha contado y que te contaron ?

    >>Solo hay que usar las flechas de dirección y Enter, ninguna tecla más.

    pero esto no tiene que ver con programacion orientada a objetos, esto tiene que ver con el diseño de la UI

    >>Antes lo hacía de forma procedural o programación clásica o antigua,

    pero esa UI es del tiempo de las cavernas, es como esperar conseguir una TV ultra delgada cuando aun usas valvulas

    >>con objetos, que para eso está C#.

    pero cuanto conoces sobre objetos? que es un polimorfismo, una sobrecarga, herencia, etc

    aplicaste algun patron de diseño?

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 7 de abril de 2020 19:14
  • Se me ocurre esta idea:

    Listas circulares de listas circulares

    serian listas circulares para que puedas dar la vuelta completa desde fin a inicio o de inicio a fin, asi es mas facil navegar por las opciones, ademas de que sea el caso de tengan muchas opciones y mucho trabajo bajar todo solo por ir a la ultima.
     
    Tienes digamos 3 opciones principales, esas 3 estan en una lista circular doblemente enlazada
    Digamos que la primera opcion tiene un submenu con 1 sola opcion (sigue siendo lista circular doble pero solo de una opcion)
    Digamos que la opcion 2 (en las opciones principales) tiene 3 opciones, las tres son una lista circular doble de 3 elementos! entre estas 3 hay una de salir, pienso que esta opcion o cualquier otra que te regrese al menu anterior podria hacer uso de un puntero (referencia) a padre y devolverse a la lista anterior (al menu anterior)

    Tratare de hacer un example de esto y te lo comparto para que se pueda entender mejor

    martes, 7 de abril de 2020 19:21
    Moderador
  • Hola Metaconta

    Ya te escribí este programita para modo consola, con objetos. Fijate que todos los menus que instancio al principio son objetos y que después uso esas referencias a los objetos e invoco sus métodos, más que nada para ver cual submenú se seleccionó. Recién lo retoqué para que esté a la última moda de pantalla verde claro y letra negra, y que resalte menús con el símbolo >.

    using System;
    
    namespace MyMenu
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Declaraciones de OBJETOS
                Menu Principal, OpcionA, OpcionB, OpcionC;
                Menu OpcionB1, OpcionB2, OpcionC1, OpcionSI;
                
                ConsoleKey tecla;
                int irAtras = 0;
                int opcion;
    
                // Instanciación de OBJETOS
                Principal = new Menu("", 0, new string[] { "Opción A", "Opción B", "Opción C", "Salir" });
                OpcionA = new Menu("Estás en Opción A.", 0, new string[] { "Pulsa Enter para salir." });
                OpcionB = new Menu("Estás en Opción B.", 0, new string[] { "Subopción B-1.", "Subopción B-2.", "Salir." });
                OpcionC = new Menu("Estás en Opción C.", 0, new string[] { "Color 1.", "Color 2.", "Opción C-1." });
                OpcionB1 = new Menu("Estás en Opción B-1.", 0, new string[] { "Pulsa Enter para volver atrás." });
                OpcionB2 = new Menu("Estás en Opción B-2.", 0, new string[] { "Pulsa Enter para volver atrás." });
                OpcionC1 = new Menu("Estás en Opción C-1.", 1, new string[] { "SI", "NO" });
                OpcionSI = new Menu("Estás en Opción Sí.", 0, new string[] { "Pulsa Enter para volver atrás." });
    
                do
                {
                    Console.BackgroundColor = ConsoleColor.Green;
                    Console.ForegroundColor = ConsoleColor.Black;
                    Console.SetWindowSize(48, 12);
                    Console.Clear();
                    Console.SetCursorPosition(10, 1);
                    Console.Write("MENÚ PRINCIPAL");
                    Console.SetCursorPosition(10, 3);
                    Console.Write(DateTime.Now.ToString("ddd dd MMM"));
    
                    tecla = Console.ReadKey(true).Key;
    
                    if (tecla == ConsoleKey.Enter)
                    {
                        while (true)
                        {
                            opcion = Principal.DoMenu();
                            if (opcion == -1)
                                break;
                            else if (opcion == 0)
                            {
                                while (true)
                                {
                                    // OBJETO que invoca a un método que retorna un valor importante
                                    opcion = OpcionA.DoMenu();
                                    if (opcion == -1)
                                        break;
                                    else if (opcion == 0)
                                        break;
                                }
                            }
                            else if (opcion == 1)
                            {
                                while (true)
                                {
                                    // OBJETO que invoca a un método que retorna un valor importante
                                    opcion = OpcionB.DoMenu();
                                    if (opcion == -1)
                                        break;
                                    else if (opcion == 0)
                                    {
                                        while (true)
                                        {
                                            // OBJETO que invoca a un método que retorna un valor importante
                                            opcion = OpcionB1.DoMenu();
                                            if (opcion == -1)
                                                break;
                                            else if (opcion == 0)
                                                break;
                                        }
                                    }
                                    else if (opcion == 1)
                                    {
                                        while (true)
                                        {
                                            // OBJETO que invoca a un método que retorna un valor importante
                                            opcion = OpcionB2.DoMenu();
                                            if (opcion == -1)
                                                break;
                                            else if (opcion == 0)
                                                break;
                                        }
                                    }
                                    else if (opcion == 2)
                                        break;
                                }
                            }
                            else if (opcion == 2)
                            {
                                while (true)
                                {
                                    // OBJETO que invoca a un método que retorna un valor importante
                                    opcion = OpcionC.DoMenu();
                                    if (opcion == -1)
                                        break;
                                    else if (opcion == 2)  // opción 3, la Opción C-1 
                                    {                      // la 1 y la 2 no hacen nada, por eso ni pregunto por ellas
                                        while (true)
                                        {
                                            // OBJETO que invoca a un método que retorna un valor importante
                                            opcion = OpcionC1.DoMenu();
                                            if (opcion == -1)
                                                break;
                                            else if (opcion == 0)
                                            {
                                                while (true)
                                                {
                                                    opcion = OpcionSI.DoMenu();
                                                    if (opcion == -1)
                                                        break;
                                                    else if (opcion == 0)
                                                        break;
                                                }
                                            }
                                            else if (opcion == 1)
                                            {
                                                irAtras = 2;
                                                break;
                                            }
                                        }
                                        if (irAtras > 0)
                                        {
                                            irAtras--;
                                            break;
                                        }
                                    }
                                }
                                if (irAtras > 0) 
                                {
                                    irAtras--;
                                    break;
                                }
                            }
                            else if (opcion == 3)
                                break;
                        }
                    }
    
                } while (tecla != ConsoleKey.Escape);
            }
        }
    
        // Definición de CLASE DE OBJETOS 
        class Menu
        {
            private string title;      // título
            private string[] options;  // cadenas de opciones
            private int layout;        // si es vertical 0, horizontal 1
            private int index = 0;     // índice de opción
    
            public Menu(string title, int layout, string[] options)
            {
                this.title = title;
                this.layout = layout;
                this.options = options;
            }
    
            // DoMenu: Ejecuta el menú y retorna: -1 -> Se pulsó Esc | De 0 a Nro de menús - 1 -> índice de opción elegida
            public int DoMenu()
            {
                int a;
                ConsoleKey key;
                Console.BackgroundColor = ConsoleColor.Green;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.CursorVisible = false;
                Console.Clear();
                Console.SetCursorPosition(12, 2);
                Console.Write(title);
    
                do
                {
                    for (a = 0; a < options.Length; a++)
                    {
                        if (layout == 0)
                        {
                            Console.SetCursorPosition(2, 4 + a);
                            Console.Write(" ");                        
                            if (a == index)
                            { 
                                Console.SetCursorPosition(2, 4 + a);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4, 4 + a);
                        }
                        else
                        {
                            Console.SetCursorPosition(2 + a * 12, 4);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2 + a * 12, 4);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4 + a * 12, 4);
                        }
                        Console.Write(options[a]);
                    }
    
                    key = Console.ReadKey(true).Key;
    
                    if (key == ConsoleKey.UpArrow || key == ConsoleKey.LeftArrow)
                    {
                        index--;
                        if (index < 0)
                            index = options.Length - 1;
                    }
                    else if (key == ConsoleKey.DownArrow || key == ConsoleKey.RightArrow)
                    {
                        index++;
                        if (index == options.Length)
                            index = 0;
                    }
                    else if (key == ConsoleKey.Enter)
                    {
                        return index;
                    }
    
                } while (key != ConsoleKey.Escape);
    
                return -1;
            }
        }
    }

    Y también te escribí el mismo programita para Windows Forms, con objetos y con botones que pueden ser clickeados y que hacen lo mismo que las flechas, el Enter, y el Escape, que este último creo que no lo viste.

    https://social.msdn.microsoft.com/Forums/es-ES/067e3ee8-6af6-442a-9fb3-c39b8f9e31fd/mens-con-windows-forms?forum=winformses

    Espero que todo te sea útil.

    Saludos

    Pablo

    martes, 7 de abril de 2020 20:12
  • hola

    >>Ya te escribí este programita para modo consola, con objetos.

    Pero eso no es objetos, es solo una clase que define unas propiedades pero sigues con miles de while, if, etc para ir de un menu al otro

    Objeto seria implementar algun patron estilo Command

    En el ejemplo tienes la clase Menu, pero que hace la clase en si misma, yo diria que nada

    Minimanente deberias tener un menu base de cual hereden los demas pantallas de menu y cada implementacion representar su interface

    Quizas un menu podria tener un SetNext() o SetBack() para indicar cuando se presiona una flacha hacia delante o atras que otra instancia de menu debe ir

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 7 de abril de 2020 20:23
  • hola

    >>Eso y nada, tengo entendido que no es lo mismo.

    estoy de acuerdo, eso es verdad

    >>sumarse también al Challenge, algo como lo que vos sugeris, y posteémoslo acá

    puedo ver de armar un esqueleto, no creo que tenga el tiempo para hacer algo completo y funcional

    mas que nada porque no tengo practica en desarrollo en consola, soy de interfaces graficas mas visuales orientadas a xaml o web

    Igual mas alla de eso seria bueno que Metaconta se sumerja en conceptos de POO y patrones de diseño, porque por mas que armenos algo sino quien plantea la duda no lo entiendo, como que no serviria de nada

    saludos


    Leandro Tuttini

    Blog
    MVP Profile
    Buenos Aires
    Argentina

    martes, 7 de abril de 2020 23:35
  • Hola Metaconta y Leandro

    Yo tampoco me especializo en Consola sino más bien en Windows Forms (además de procesos lógicos y algoritmos sofisticados y videojuegos), pero es como decis Leandro, hay que tomarse un ratito

    Metaconta, aquí está el programa con la clase MenuTree, con el código aún más compacto y más orientado a objetos

    using System;
    
    namespace ConsoleMenuTreeTheUltimateVersion
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Declaraciones de OBJETOS
                Menu Inicial;
                Menu Principal, OpcionA, OpcionB, OpcionC, Salir;
                Menu OpcionB1, OpcionB2, SalirB, OpcionColor1, OpcionColor2, OpcionC1;
                Menu OpcionSI, OpcionNO;
    
                // Instanciación de OBJETOS
                OpcionNO = new Menu("", Layout.Vertical, null, -4);
                OpcionNO.Name = "OpcionNO";
                OpcionSI = new Menu("Estás en Opción Sí.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionSI.Name = "OpcionSI";
                OpcionC1 = new Menu("Estás en Opción C-1.", Layout.Horizontal, new string[] { "SI", "NO" }, 1);
                OpcionC1.Name = "OpcionC1";
                OpcionC1.subMenus = new Menu[] { OpcionSI, OpcionNO };
                OpcionColor1 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor1.Name = "OpcionColor1";
                OpcionColor2 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor2.Name = "OpcionColor2";
                OpcionB1 = new Menu("Estás en Opción B-1.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB1.Name = "OpcionB1";
                OpcionB2 = new Menu("Estás en Opción B-2.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB2.Name = "OpcionB2";
                SalirB = new Menu("", Layout.Vertical, null, null, -2);
                SalirB.Name = "SalirB";
    
                // opción A
                OpcionA = new Menu("Estás en Opción A.", Layout.Vertical, new string[] { "Pulsa Enter para salir." }, -1);
                OpcionA.Name = "OpcionA";
                // opción B
                OpcionB = new Menu("Estás en Opción B.", Layout.Vertical, new string[] { "Subopción B-1.", "Subopción B-2.", "Salir." }, 1);
                OpcionB.Name = "OpcionB";
                OpcionB.subMenus = new Menu[] { OpcionB1, OpcionB2, SalirB };
                // opción C
                OpcionC = new Menu("Estás en Opción C.", Layout.Vertical, new string[] { "Color 1.", "Color 2.", "Opción C-1." }, 1);
                OpcionC.Name = "OpcionC";
                OpcionC.subMenus = new Menu[] { OpcionColor1, OpcionColor2, OpcionC1 };            
                // opción Salir
                Salir = new Menu("", Layout.Vertical, null, null, -2);
                Salir.Name = "Salir";
    
                // menú principal
                Principal = new Menu("", 0, new string[] { "Opción A", "Opción B", "Opción C", "Salir" }, 1);
                Principal.Name = "Principal";
                Principal.subMenus = new Menu[] { OpcionA, OpcionB, OpcionC, Salir };
                
                // pantalla inicial
                Inicial = new Menu("", Layout.Vertical, new string[] { "MENÚ PRINCIPAL" }, 1);
                Inicial.Name = "Inicial";
                Inicial.subMenus = new Menu[] { Principal };
                Inicial.method = Menu.PrintDate;
    
                MenuTree menuTree = new MenuTree(Inicial);
                
                // ejecuta el "árbol" de menús y termina cuando se pulsa Esc en Menú Principal
                if (menuTree.DoMenuTree() == -1)
                    return;
            }
        }
    
        public enum Layout { Vertical, Horizontal };
    
        // Definición de CLASE DE OBJETOS 
        public class Menu
        {
            public string Name { get; set; }
            public string title { get; set; }     
            public string[] options { get; set; } 
            private Layout layout { get; set; }
            private int index = 0;
    
            public int goWhere { get; set; }
            public Menu[] subMenus { get; set; }
            public Menu beforeMenu { get; set; }
            public Action method;
    
            public Menu() { }
    
            public Menu(string title, Layout layout, string[] options, int goWhere) 
            {
                this.title = title;
                this.layout = layout;
                this.options = options;
                this.goWhere = goWhere;
            }
    
            public Menu(string title, Layout layout, string[] options, Menu[] subMenus, int goWhere)
            {
                this.title = title;
                this.layout = layout;
                this.options = options;            
                this.subMenus = subMenus;
                this.goWhere = goWhere;
            }
    
            public static void PrintDate()
            {
                Console.SetCursorPosition(3, 6);
                Console.Write(DateTime.Now.ToString("ddd dd MMM"));
            }
    
            // DoMenu: Ejecuta el menú y retorna: -1 -> Se pulsó Esc | De 0 a Nro de menús - 1 -> índice de opción elegida
            public int DoMenu()
            {
                int a;
                ConsoleKey key;
                Console.BackgroundColor = ConsoleColor.Green;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.CursorVisible = false;
                Console.Clear();
                Console.SetCursorPosition(12, 2);
                Console.Write(title);
    
                if (method != null) 
                    method();
    
                if (this.title == "" && this.goWhere < 0)
                    return this.goWhere - 5;
    
                do
                {
                    for (a = 0; this.options != null && a < this.options.Length; a++)
                    {
                        if (layout == Layout.Vertical)
                        {
                            Console.SetCursorPosition(2, 4 + a);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2, 4 + a);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4, 4 + a);
                        }
                        else if (layout == Layout.Horizontal)
                        {
                            Console.SetCursorPosition(2 + a * 12, 4);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2 + a * 12, 4);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4 + a * 12, 4);
                        }
                        Console.Write(options[a]);
                    }
    
                    key = Console.ReadKey(true).Key;
    
                    if (key == ConsoleKey.UpArrow || key == ConsoleKey.LeftArrow)
                    {
                        index--;
                        if (index < 0)
                            index = options.Length - 1;
                    }
                    else if (key == ConsoleKey.DownArrow || key == ConsoleKey.RightArrow)
                    {
                        index++;
                        if (index == options.Length)
                            index = 0;
                    }
                    else if (key == ConsoleKey.Enter)
                    {
                        if (this.goWhere < 0)
                            return this.goWhere - 5;
                        return index;
                    }
    
                } while (key != ConsoleKey.Escape);
    
                return -1;
            }
        }
    
        // Definición de otra CLASE DE OBJETOS
        public class MenuTree 
        {
            private Menu menu;
    
            public MenuTree(Menu menu) 
            {
                this.menu = menu;
            }
    
            public int DoMenuTree() 
            {
                int option;
                int limit;
                Menu curMenu = this.menu;
                Menu beforeMenu;
                while (true)
                {
                    option = curMenu.DoMenu();
                    beforeMenu = curMenu;
                    if (option >= 0)
                    {
                        if (curMenu.subMenus != null)
                        {
                            curMenu = curMenu.subMenus[option];
                            curMenu.beforeMenu = beforeMenu;
                        }
                        else if (curMenu.goWhere < 0)
                        {
                            limit = -curMenu.goWhere;
                            for (int a = 0; a < limit; a++)
                                curMenu = curMenu.beforeMenu;
                        }
                    }
                    else if (option == -1)
                    {
                        if (curMenu.beforeMenu != null)
                            curMenu = curMenu.beforeMenu;
                        else
                            return -1;
                    }
                    else if (option < 0)
                    {
                        limit = -(option + 5);
                        for (int a = 0; a < limit; a++)
                            curMenu = curMenu.beforeMenu;
                    }
                }
            }
        }
    }

    Saludos

    Pablo


    • Marcado como respuesta Metaconta miércoles, 29 de abril de 2020 15:51
    miércoles, 8 de abril de 2020 14:25
  • Hola Metaconta y Leandro

    Yo tampoco me especializo en Consola sino más bien en Windows Forms (además de procesos lógicos y algoritmos sofisticados y videojuegos), pero es como decis Leandro, hay que tomarse un ratito

    Metaconta, aquí está el programa con la clase MenuTree, con el código aún más compacto y más orientado a objetos

    using System;
    
    namespace ConsoleMenuTreeTheUltimateVersion
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Declaraciones de OBJETOS
                Menu Inicial;
                Menu Principal, OpcionA, OpcionB, OpcionC, Salir;
                Menu OpcionB1, OpcionB2, SalirB, OpcionColor1, OpcionColor2, OpcionC1;
                Menu OpcionSI, OpcionNO;
    
                // Instanciación de OBJETOS
                OpcionNO = new Menu("", Layout.Vertical, null, -4);
                OpcionNO.Name = "OpcionNO";
                OpcionSI = new Menu("Estás en Opción Sí.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionSI.Name = "OpcionSI";
                OpcionC1 = new Menu("Estás en Opción C-1.", Layout.Horizontal, new string[] { "SI", "NO" }, 1);
                OpcionC1.Name = "OpcionC1";
                OpcionC1.subMenus = new Menu[] { OpcionSI, OpcionNO };
                OpcionColor1 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor1.Name = "OpcionColor1";
                OpcionColor2 = new Menu("", Layout.Vertical, null, -1);
                OpcionColor2.Name = "OpcionColor2";
                OpcionB1 = new Menu("Estás en Opción B-1.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB1.Name = "OpcionB1";
                OpcionB2 = new Menu("Estás en Opción B-2.", Layout.Vertical, new string[] { "Pulsa Enter para volver atrás." }, -1);
                OpcionB2.Name = "OpcionB2";
                SalirB = new Menu("", Layout.Vertical, null, null, -2);
                SalirB.Name = "SalirB";
    
                // opción A
                OpcionA = new Menu("Estás en Opción A.", Layout.Vertical, new string[] { "Pulsa Enter para salir." }, -1);
                OpcionA.Name = "OpcionA";
                // opción B
                OpcionB = new Menu("Estás en Opción B.", Layout.Vertical, new string[] { "Subopción B-1.", "Subopción B-2.", "Salir." }, 1);
                OpcionB.Name = "OpcionB";
                OpcionB.subMenus = new Menu[] { OpcionB1, OpcionB2, SalirB };
                // opción C
                OpcionC = new Menu("Estás en Opción C.", Layout.Vertical, new string[] { "Color 1.", "Color 2.", "Opción C-1." }, 1);
                OpcionC.Name = "OpcionC";
                OpcionC.subMenus = new Menu[] { OpcionColor1, OpcionColor2, OpcionC1 };            
                // opción Salir
                Salir = new Menu("", Layout.Vertical, null, null, -2);
                Salir.Name = "Salir";
    
                // menú principal
                Principal = new Menu("", 0, new string[] { "Opción A", "Opción B", "Opción C", "Salir" }, 1);
                Principal.Name = "Principal";
                Principal.subMenus = new Menu[] { OpcionA, OpcionB, OpcionC, Salir };
                
                // pantalla inicial
                Inicial = new Menu("", Layout.Vertical, new string[] { "MENÚ PRINCIPAL" }, 1);
                Inicial.Name = "Inicial";
                Inicial.subMenus = new Menu[] { Principal };
                Inicial.method = Menu.PrintDate;
    
                MenuTree menuTree = new MenuTree(Inicial);
                
                // ejecuta el "árbol" de menús y termina cuando se pulsa Esc en Menú Principal
                if (menuTree.DoMenuTree() == -1)
                    return;
            }
        }
    
        public enum Layout { Vertical, Horizontal };
    
        // Definición de CLASE DE OBJETOS 
        public class Menu
        {
            public string Name { get; set; }
            public string title { get; set; }     
            public string[] options { get; set; } 
            private Layout layout { get; set; }
            private int index = 0;
    
            public int goWhere { get; set; }
            public Menu[] subMenus { get; set; }
            public Menu beforeMenu { get; set; }
            public Action method;
    
            public Menu() { }
    
            public Menu(string title, Layout layout, string[] options, int goWhere) 
            {
                this.title = title;
                this.layout = layout;
                this.options = options;
                this.goWhere = goWhere;
            }
    
            public Menu(string title, Layout layout, string[] options, Menu[] subMenus, int goWhere)
            {
                this.title = title;
                this.layout = layout;
                this.options = options;            
                this.subMenus = subMenus;
                this.goWhere = goWhere;
            }
    
            public static void PrintDate()
            {
                Console.SetCursorPosition(3, 6);
                Console.Write(DateTime.Now.ToString("ddd dd MMM"));
            }
    
            // DoMenu: Ejecuta el menú y retorna: -1 -> Se pulsó Esc | De 0 a Nro de menús - 1 -> índice de opción elegida
            public int DoMenu()
            {
                int a;
                ConsoleKey key;
                Console.BackgroundColor = ConsoleColor.Green;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.CursorVisible = false;
                Console.Clear();
                Console.SetCursorPosition(12, 2);
                Console.Write(title);
    
                if (method != null) 
                    method();
    
                if (this.title == "" && this.goWhere < 0)
                    return this.goWhere - 5;
    
                do
                {
                    for (a = 0; this.options != null && a < this.options.Length; a++)
                    {
                        if (layout == Layout.Vertical)
                        {
                            Console.SetCursorPosition(2, 4 + a);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2, 4 + a);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4, 4 + a);
                        }
                        else if (layout == Layout.Horizontal)
                        {
                            Console.SetCursorPosition(2 + a * 12, 4);
                            Console.Write(" ");
                            if (a == index)
                            {
                                Console.SetCursorPosition(2 + a * 12, 4);
                                Console.Write(">");
                            }
                            Console.SetCursorPosition(4 + a * 12, 4);
                        }
                        Console.Write(options[a]);
                    }
    
                    key = Console.ReadKey(true).Key;
    
                    if (key == ConsoleKey.UpArrow || key == ConsoleKey.LeftArrow)
                    {
                        index--;
                        if (index < 0)
                            index = options.Length - 1;
                    }
                    else if (key == ConsoleKey.DownArrow || key == ConsoleKey.RightArrow)
                    {
                        index++;
                        if (index == options.Length)
                            index = 0;
                    }
                    else if (key == ConsoleKey.Enter)
                    {
                        if (this.goWhere < 0)
                            return this.goWhere - 5;
                        return index;
                    }
    
                } while (key != ConsoleKey.Escape);
    
                return -1;
            }
        }
    
        // Definición de otra CLASE DE OBJETOS
        public class MenuTree 
        {
            private Menu menu;
    
            public MenuTree(Menu menu) 
            {
                this.menu = menu;
            }
    
            public int DoMenuTree() 
            {
                int option;
                int limit;
                Menu curMenu = this.menu;
                Menu beforeMenu;
                while (true)
                {
                    option = curMenu.DoMenu();
                    beforeMenu = curMenu;
                    if (option >= 0)
                    {
                        if (curMenu.subMenus != null)
                        {
                            curMenu = curMenu.subMenus[option];
                            curMenu.beforeMenu = beforeMenu;
                        }
                        else if (curMenu.goWhere < 0)
                        {
                            limit = -curMenu.goWhere;
                            for (int a = 0; a < limit; a++)
                                curMenu = curMenu.beforeMenu;
                        }
                    }
                    else if (option == -1)
                    {
                        if (curMenu.beforeMenu != null)
                            curMenu = curMenu.beforeMenu;
                        else
                            return -1;
                    }
                    else if (option < 0)
                    {
                        limit = -(option + 5);
                        for (int a = 0; a < limit; a++)
                            curMenu = curMenu.beforeMenu;
                    }
                }
            }
        }
    }

    Saludos

    Pablo


    Lo voy a probar y les cuento.

    En realidad soy de Windwos form. Donde se les enseña primero es precisament en consola, por lo menos en España.


    http://electronica-pic.blogspot.com


    • Editado Metaconta viernes, 10 de abril de 2020 5:28
    viernes, 10 de abril de 2020 5:25
  • Hola Metaconta

     

    ¿Alguna actualización sobre la consulta realizada? ¿Alguna de las respuestas proporcionadas te ayudó?

     

    Espero tu respuesta.

     

    Gracias por usar los foros de MSDN.

     

     

    Luis Diego Mora

     

    --------------------------------------------------------------------------

     

    Por favor recuerde "Marcar como respuesta" las respuestas que hayan resuelto su problema, es una forma común de reconocer a aquellos que han ayudado, y hace que sea más fácil para los otros visitantes encontrar la solución más tarde. 

     

    Microsoft ofrece este servicio de forma gratuita, con la finalidad de ayudar a los usuarios y la ampliación de la base de datos de conocimientos relacionados con los productos y tecnologías de Microsoft.  

     

    Este contenido es proporcionado "tal cual" y no implica ninguna responsabilidad de parte de Microsoft.

    lunes, 13 de abril de 2020 13:51
    Moderador
  • Funciona.

    http://electronica-pic.blogspot.com

    miércoles, 29 de abril de 2020 15:51
  • Hola Metaconta

    Fijate que hay un cambio en el método DoMenuTree(), eliminé una parte innecesaria, que te lo pasé en la pregunta del checkbox y radiobutton, para esto es lo mismo, lo probé y anda bien sin esa parte innecesaria. Te paso el método DoMenuTree() ya (creo) sin errores ni cosas de más. 

           public int DoMenuTree() 
            {
                int option;
                int limit;
                Menu curMenu = this.menu;
                Menu beforeMenu;
                while (true)
                {
                    option = curMenu.DoMenu();
                    beforeMenu = curMenu;
                    if (option >= 0)
                    {
                        if (curMenu.subMenus != null && curMenu.subMenus[option] != null)
                        {
                            curMenu = curMenu.subMenus[option];
                            curMenu.beforeMenu = beforeMenu;
                        }
                    }
                    else if (option == -1)
                    {
                        if (curMenu.beforeMenu != null)
                            curMenu = curMenu.beforeMenu;
                        else
                            return -1;
                    }
                    else if (option < 0)
                    {
                        limit = -(option + 5);
                        for (int a = 0; a < limit; a++)
                            curMenu = curMenu.beforeMenu;
                    }
                }
            }

    Saludos


    • Editado Tigre Pablito jueves, 30 de abril de 2020 0:00
    • Marcado como respuesta Metaconta jueves, 30 de abril de 2020 16:22
    miércoles, 29 de abril de 2020 23:58