C# Console Menu + Submenu's Help

Discussion in 'Programming & Software Development' started by Sarquiss, Jun 13, 2010.

  1. Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    NOTE - I am not asking for someone to provide me with the code to answer my question just some guidance.

    I am currently working on a C# assignment which has to be done via the console. I have setup the main menu using switch statement, but I was wondering if someone knew of a better way of implementing a menu with submenus etc

    e.g.

    Main menu

    1 - Registration
    2 - Item
    3 - blah
    4 - blah
    5 - blah

    If the user selects 1 they will be presented with the following menu:-

    1 - Add user
    2 - Edit user
    3 - Delete user

    if the user selects option 2 a similar menu will come up. How can I make it so I don't repeat code??

    Also, how would I make it so the user can go back to the main menu from a submenu?

    I have the following code:-

    Code:
           
    public static void Main()
            {
                string myChoice;
    
                Menu m = new Menu();
                Registration r = new Registration();
                ItemManagement i = new ItemManagement();
    
                do
                {
                    myChoice = m.getChoice();
    
                    // Make a decision based on the user's choice
                    switch (myChoice)
                    {
                        case "R":
                        case "r":
                            r.Menu();
                            break;
                        case "I":
                        case "i":
                            i.Menu();
                            break;
                        case "S":
                        case "s":
                            Console.WriteLine("You wish to search for book or member.");
                            break;
                        case "C":
                        case "c":
                            Console.WriteLine("You wish to check an item out.");
                            break;
                        case "Q":
                        case "q":
                            Console.WriteLine("You wish to quit.");
                            break;
                        default:
                            Console.WriteLine("{0} is not a valid choice", myChoice);
                            break;
                    }
    
                    // Pause to allow the user to see the results
                    Console.WriteLine("\n-----------------------------------------\n");
                    //Console.Write("press Enter key to continue...");
    
                    //Console.ReadLine();
                    //Console.WriteLine();
    
                } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit
            }
    
            string getChoice()
            {
                string myChoice;
    
                // Print A Menu
                Console.WriteLine("Please select one of the following options\n");
    
                Console.WriteLine("R : New Member Registration");
                Console.WriteLine("I : Item Management");
                Console.WriteLine("S : Book & Member Search");
                Console.WriteLine("C : Proceed to Checkout");
                Console.WriteLine("Q : Quit\n");
    
                Console.Write("Choice (R,I,S,C,or Q): ");
    
                // Retrieve the user's choice
                myChoice = Console.ReadLine();
                Console.WriteLine("\n");
    
                return myChoice;
            }
        }
    Code:
            
    public void Menu()
            {
                string myChoice;
    
                do
                {
                    myChoice = getChoice();
    
                    // Make a decision based on the user's choice
                    switch (myChoice)
                    {
                        case "A":
                        case "a":
                            //Console.WriteLine("Add New User");
                            addUser();
                            break;
                        case "E":
                        case "e":
                            //Console.WriteLine("Edit Existing User");
                            editUser();
                            break;
                        case "D":
                        case "d":
                            //Console.WriteLine("Delete Existing User");
                            deleteUser();
                            break;
                        case "Q":
                        case "q":
                            Console.WriteLine("Quiting");
                            break;
                        default:
                            Console.WriteLine("{0} is not a valid choice", myChoice);
                            break;
                    }
    
                    // Pause to allow the user to see the results
                    Console.WriteLine("\n-----------------------------------------\n");
                    Console.Write("press Enter key to continue...");
    
                    Console.ReadLine();
                    Console.WriteLine();
    
                } while (myChoice != "Q" && myChoice != "q"); // Keep going until the user wants to quit
            }
    
            string getChoice()
            {
                string myChoice;
    
                // Print A Menu
                Console.WriteLine("User Registration\n");
    
                Console.WriteLine("A : Add New User");
                Console.WriteLine("E : Edit Existing User");
                Console.WriteLine("D : Delete Existing User");
                Console.WriteLine("Q : Quit\n");
    
                Console.Write("Choice (A,E,D or Q): ");
    
                // Retrieve the user's choice
                myChoice = Console.ReadLine();
                Console.WriteLine();
    
                return myChoice;
            }
    
            public void addUser()
            {
                Console.WriteLine("You have added new user");
            }
    
            public void editUser()
            {
                Console.WriteLine("You have updated an existing user");
            }
    
            public void deleteUser()
            {
                Console.WriteLine("You have deleted existing user");
            }
        }
     
  2. SaTaN

    SaTaN Member

    Joined:
    Jun 18, 2002
    Messages:
    4,790
    Location:
    Caulfield-ish
    are you asking because it doesnt work? or because you want something cleaner?


    (I'm going to get yelled at for this but anyway...) OO code should almost never need to use switches if you have the classes done properly, I'm guessing this is first year programming so this is meaningless but what you probably want is to look at the factory design pattern to create the correct menu type...

    THAT SAID....

    you havnt provided enough code to tell you why it isnt doing what you want... what class is the second paste from? Do you Registration and ItemManagement classes have a Menu() method?
     
  3. TheChemist

    TheChemist R.I.P

    Joined:
    Apr 5, 2007
    Messages:
    1,035
    To answer your questions, you reduce duplicate code by breaking code into methods which can be reused. If you're using objects, these will need to either be in the object class, or will need to be a global method.


    To answer your second question, it depends how the .menu() method of Registration and ItemRegistration works.. you'll need to post that if you want more detailed help. But essentially, if you're in a method a and then call method b, when b finishes it will return to a.
     
  4. OP
    OP
    Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    The code works, just want something cleaner. Also, the second code section I post is how the code for both Registration and ItemManagement look like.
     
  5. mikeyyy

    mikeyyy Member

    Joined:
    Apr 7, 2005
    Messages:
    590
    Location:
    Sydney
    I hardly ever use OO, can you explain how you could avoid using a switch/ifelses in this case?
     
  6. SaTaN

    SaTaN Member

    Joined:
    Jun 18, 2002
    Messages:
    4,790
    Location:
    Caulfield-ish
    Well you can obviously never get ri of it completly but in this case moving it into a factory class would at least hide the mess from Main().

    The way to more OO-ly do it is having an interface IMenuable (for example) and create a class for each menu item, then Main() would look something like:

    Code:
    string myChoice;
    Menu m = new Menu();
    while (true)
    {
        IMenuable item = m.Create(m.getChoice())
        if (item == null)
            return 0;
        item.run();
    }
    
    Menu.Create() then has the mess of figuring out which subitem you want to create.
    To do nested menus here you'd probably add a menu factory interface to the Imenuable interface so each knows exactly what each can do... ie... (C# is rusty but..)
    Code:
    public interface IMenuable {
        int run();
        Imenuable Create(string submenu);
    }
    

    or maybe you done even need Create there because not all menus have submenus...
     
  7. TheChemist

    TheChemist R.I.P

    Joined:
    Apr 5, 2007
    Messages:
    1,035
    Wouldn't your m.Create(m.GetChoice()); have a switch in it?
     
  8. ascl

    ascl Member

    Joined:
    Aug 13, 2009
    Messages:
    463
    Location:
    Sydney
    I'm not really clear if the question was asking:
    a) Whats a better way of coding the option parsing?
    or
    b) Whats a better way of structuring the menu/submenu code?

    ie the use of a case statement falls under "a", and I think SaTaN is answering "b"


    For "a", my suggestion would be to use a lookup table, something like this (there are likely better ways of doing this in C#):
    Code:
        class Menu
        {
            public delegate void Option();
            public Dictionary<String, Option> options = new Dictionary<string, Option>();
        }
    
        class Program
        {
            
            static public void FOption()
            {
                Console.WriteLine("F picked\n");
            }
          
            static void Main(string[] args)
            {
    
                Menu menu = new Menu();
                menu.options.Add("F", FOption);
    
                String myChoice = MyChoice().ToUpper();
    
                if (menu.options.ContainsKey(myChoice))
                    menu.options[myChoice]();
                else 
                     Console.WriteLine("Invalid Option\n");
                Console.ReadKey();
            }
    
    Basically you create a hash or Dictionary using the choices as a key and the value as a function pointer or delegate.

    This doesn't help how you structure the menu/submenu code, but is an alternative to a huge switch/case statement.

    EDIT: Please excuse any blatant C# failures, its not my language of choice.
     
  9. mikeyyy

    mikeyyy Member

    Joined:
    Apr 7, 2005
    Messages:
    590
    Location:
    Sydney
    I still prefer the switch, it's simpler, easier to read and understand, and doesn't unnecessarily obfuscate the code. If it's too big you can always just move it into its own function.
     
  10. ascl

    ascl Member

    Joined:
    Aug 13, 2009
    Messages:
    463
    Location:
    Sydney
    Very large switch tables are considerably harder to read than a lookup table is... it depends on the application, but lookup tables are worth considering (although its a little clunky in C#, maybe there is a better way of doing it).

    Especially if you expect to add/remove options, which is much easier with a LUT.

    Anyway, as always there are multiple ways of solving these problems, this is just one.
     
  11. SaTaN

    SaTaN Member

    Joined:
    Jun 18, 2002
    Messages:
    4,790
    Location:
    Caulfield-ish
    My point was more to move that out if main() because it always good to make that clean. Once you move the switch into another class you ate free to actually implement it any way you want. A LUT like the others suggested is a good way to go if there are more than a few options.
     
  12. OP
    OP
    Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    Thanks for the suggestions guys. I will have a play around with the suggestions and will hopefully learn something while doing it :p

    The only thing I am really worried about is having a huge chunk of code repeated in different classes.

    So I might move the menu stuff to a GUI class and have different methods with the various menus needed.

    e.g

    Code:
    class GUI
    {
           public mainMenu()
           {   
                   blah blah blah
           }
    
          public subMenu1()
          {
                    blah blah blah
          }
    
         public subMenu2()
         {
                   blah blah blah
         }
    }
    
     
  13. Mikos

    Mikos Member

    Joined:
    Mar 12, 2004
    Messages:
    2,881
    Location:
    Cydonia
    You never get rid of the if's/switches in this case, it just gets buried into another class to neaten it up a bit.

    My memory is a little hazy but as mentioned the ultimate way to do it would be using a combination of abstract factory pattern + factory method to completely abstract away the menu creation from the main code. So in the end your main function would be something like

    Code:
    using System;
    
    namespace Example
    {
      public class Example
      {
        public static void Main()
        {
          MainMenu menu = MainMenu.GetMainMenu(MainMenuType.Console);
          menu.Display();
          menu.Goodbye();
        }
      }
    
    
      public enum MainMenuType { Console, GUI };
    
      public abstract class MainMenu
      {
        public abstract void Display();
        public abstract void Goodbye();
    
        public static MainMenu GetMainMenu(MainMenuType type)
        {
          switch (type)
          {
            case MainMenuType.Console:
              return new ConsoleMainMenu();
            case MainMenuType.GUI:
              return new GuiMainMenu();
            default:
              throw new ArgumentException("Unknown menu type");
          }
        }
      }
    
      public class ConsoleMainMenu : MainMenu
      {
        public ConsoleMainMenu()
        {
        }
    
        public override void Display()
        {
          // Display your main menu in console, get inputs, etc.
        }
    
        public override void Goodbye()
        {
          Console.WriteLine("Goodbye!");
        }
      }
    
      public class GuiMainMenu : MainMenu
      {
        public GuiMainMenu()
        {
        }
    
        public override void Display()
        {
          // Display a form version of the main menu, etc.
        }
    
        public override void Goodbye()
        {
          MessageBox.Show("Goodbye!");
        }
      }
    }
    
    
    So then switching from a GUI to Console UI is just a simple flick in your main method.

    Anyway I will stop derailing
     
  14. OP
    OP
    Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    ascl after reading your post I had some questions as to what is actually happening as the code appears to be interesting and I wanted to learn some more about the language.

    Code:
    public delegate void Option();
            public Dictionary<String, Option> options = new Dictionary<string, Option>();
    Can you explain to me what is happening here.
     
  15. ascl

    ascl Member

    Joined:
    Aug 13, 2009
    Messages:
    463
    Location:
    Sydney
    Sure, although I may slip into C when discussing it :)

    Code:
    public delegate void Option();
    
    Here we are defining a type, and the type is a function (pointer).


    Code:
    public Dictionary<String, Option> options = new Dictionary<string, Option>();
    
    This is a dictionary, or hash table, in which the key is a string, and the value is the type we defined earlier, ie a function (pointer in C terms). This means that when we index the Dictionary (hash table) with an appropriate string, we get returned a function.

    So, if we declare a function that matches the signature of 'Option':
    Code:
    public void ThisIsOkay();
    
    public int ThisWillNotWork(int foo);
    
    We can then store a reference to that function in the Dictionary:
    Code:
    options.Add("SomeString", ThisIsOkay);
    
    And we can then call the function by indexing the Dictionary:
    Code:
    options["SomeString"]();
    
    There is no limitation on the type of function we could define as the delegate, so this would be fine as well:
    Code:
    public delegate int OtherOption(float);
    public Dictionary<String, OtherOption> options = new Dictionary<string, OtherOption>();
    
    public int DoesStuff(float foo);
    
    # Add to the dict
    options.Add("DoesStuff", DoesStuff);
    
    # call the function
    int returnvalue = options["DoesStuff"](1.234);
    
    Delegates can be used as function arguments as well, which might be useful if you want a function to provide updates while its doing something (say, copying files), but you don't want to do the update in that function. This is handy, because it allows your copying files function to do one thing (copy files), and the optional delegate you pass in can be used to, say, update the Window to give a progress bar (the windows API function CopyFileEx() provides exactly this mechanism).

    Does this help?

    EDIT: FWIW a delegate is analogous to a function pointer in C
     
  16. OP
    OP
    Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    Thanks for the explanation. It clears a lot of stuff up :p

    One last thing. I am trying to format the output on the console to look like a table but the output is all over the place.

    [​IMG]

    Uploaded with ImageShack.us

    How can I go about this??? Should I maybe check the length of the strings and add spaces thus padding the extra spaces needed?
     
  17. SaTaN

    SaTaN Member

    Joined:
    Jun 18, 2002
    Messages:
    4,790
    Location:
    Caulfield-ish
    Pad with spaces might work. Or use \t which will put the tab character into the output
     
  18. Mikos

    Mikos Member

    Joined:
    Mar 12, 2004
    Messages:
    2,881
    Location:
    Cydonia
    http://www.csharp-examples.net/align-string-with-spaces/

    Google is your friend ;)
     
  19. OP
    OP
    Sarquiss

    Sarquiss Member

    Joined:
    Jan 2, 2006
    Messages:
    568
    Location:
    Sydney
    Thanks for the help so far guys :)

    Anyways, I need some more assistance.

    I have a search method which is used to look through a list and return the object that is found.

    Now the only problem is that I have a delete method which will delete whatever the user enters as a search criteria. However, I wanted to make it so if there are multiple objects matching the criteria that results would be printed and the user would be prompted to select the correct one to delete.

    What I am thinking is have a main list which hold all of the data and another list which will hold the objects which match the search criteria and if there were more than 1 in the list it would print the list and the user would select which they wanted to delete.

    Can I get some ideas on how to accomplish this??? Could it be better to have a separate search or incorporate it with the delete method?
     
  20. SaTaN

    SaTaN Member

    Joined:
    Jun 18, 2002
    Messages:
    4,790
    Location:
    Caulfield-ish
    Depends on what you are actually searching on, have a look into LINQ.
    That will let you use any collection a bit like a sql database (of course it depends how your lists are structured).

    You probably want to have a seperate search method (that returns a collection of found objects which can then be removed), or if you want to make slightly messy code use a delegate callback function on your search function...

    The reason for splitting search and delete out is because if you want to do delete, there is a good change you probaly want to allow updating/simple finding later anyway.
     

Share This Page

Advertisement: