I want to make a simple menu in C# like :
something like this should be printed out of console :
FirstOption
SecondOption
Exit
So far here is my code (there are problems with naming and encapsulation, but this is all just quick prototype, spent ~30 minutes):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Menu StartGame = new Menu("Start Game");
Menu EndGame = new Menu("End Game");
Console.WriteLine(StartGame);
Console.WriteLine(End Game);
EndGame.isChecked = false;
}
}
class Menu
{
private string Content;
public bool isChecked = true;
public Menu(string Content)
{
this.Content = Content;
}
public void CheckCondition()
{
if (isChecked)
{
Console.BackgroundColor = ConsoleColor.White;
Console.ForegroundColor = ConsoleColor.Black;
}
else
{
Console.ResetColor();
}
}
public override string ToString()
{
this.CheckCondition();
return this.Content;
}
}
}
The idea is when a button is clicked the menu item is highlighted. When one come to the last menu item he can't press DownArrow again, the same for the first item and UpArrow.
I'm completely stuck with this.
I am not completely sure.. May be this could help you to get started.
while (true)
{
var ch = Console.ReadKey(false).Key;
switch (ch)
{
case ConsoleKey.UpArrow:
HighlightStartGame();
break;
case ConsoleKey.DownArrow:
HighlightEndGame();
break;
}
}
static void HighlightStartGame()
{
Console.Clear();
Console.ResetColor();
StartGame.isChecked = true;
Console.WriteLine(StartGame);
EndGame.isChecked = false;
Console.WriteLine(EndGame);
}
static void HighlightEndGame()
{
Console.Clear();
Console.ResetColor();
StartGame.isChecked = false;
Console.WriteLine(StartGame);
EndGame.isChecked = true;
Console.WriteLine(EndGame);
}
No you can't just do that because Win32 console doesn't support those methods. You can however use GDI to draw on the console window.
The problem is that the console cannot process any mouse events. How do you want to click on the menu? You will have to do everything with keys. The options you have are to either define keystrokes (like Ctrl-F or Alt-F for "FirstEntry") in order to activate menu entries, or to implement a navigation with arrow keys, allowing you to move around fields (button or menu fields and text fields). This is not built in, so you will have to do everything in code. You will have to use the SetCursorPosition and the ReadKey methods of the console in order to achieve this. I remember having done this on a VT100 terminal eons ago.
I've written a console menu library for C#. It has no mouse support, but it might still be a starting point for you?
CMenu is a lightweight, low-ceremony framework for building console
menus in .Net. Instead of manually prompting the user for input and
parsing it, you define commands in a short, structured and
comprehensive way, and let CMenu handle the rest.
CMenu aims for low overhead - simple stuff should be simple to
implement. If you don't use a feature, you don't need to know anything
about it.
At the same time, complex scenarios are supported. Large menus can
easily be split into several classes. Background self-configuration.
You do not have to worry about all the annoying details involved in
larger menus, it will just work.
Most importantly, it is very simple and fast to use. Commands can be
abbreviated, a smart parser enables even partial matching. A help
command is integrated.
Related
MCVE is below. How do I avoid recursively calling StartGame() and DisplayEndScreen? One way is to loop over StartGame(), but this doesn't seem like an extensible solution, more of a hack. Another solution might be: when the user hits R to restart, return to the main menu and pass in the Enter key as input into the switch statement, somehow.
What's a good solution?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main Menu: Press Enter to start the game >>");
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.Enter:
StartGame();
break;
// Other Menu Items
}
}
private static void StartGame()
{
// Do Game
DisplayEndScreen(); // NB: This causes recursion! I don't want this.
}
private static void DisplayEndScreen()
{
Console.WriteLine("Game Over! Select an option >> \n\n" +
"R:\tPlay again\n" +
"M:\tReturn to Main Menu\n" +
"Q:\tQuit");
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.R:
StartGame(); // NB: This causes recursion! I don't want this.
break;
// Other Menu Items
}
}
}
}
Calling StartGame() inside StartGame() causes the recursion. The way to avoid it is simply not calling a method inside of itself.
I think the name of the functions are a bit confusing for your program, are you sure 'StartGame' is correctly describing what the function is doing? It seems more likely to be the actual game (game()?) itself. If the name instead was something that tells you that the game is actually running (as it looks like its doing), calling it again would make less sense for you.
Considering running a game, most basic solutions usually looks like this:
bool game = true;
while(game)
{
//game running
if(exit)
{
game = false;
}
}
I'm trying to make a KeyDown statement work. So I'm writing:
private void KeyDown(object sender, System.Windows.Forms.KeyEventArgs e);
System.Windows.Forms won't work I read that its a Visual Studio Code thing you have to go to Project.json and add System.Windows.Forms as a dependency. I don't know what to write to add it. I searched the web and searched stock overflow. I can't find anything.
I don't know what to type in to add it.
I could be wrong, but I don't believe you can use the System.Windows.Forms assembly with a .Net Core project. My Visual Studio is acting up so I wasn't able to try using it via the project.json imports feature. Having said that, it wouldn't provide you with what you want anyway.
Since you are wanting to capture the input from the user, via the console, and change the color based on some conditions - you'll have to do that manually yourself.
The following is a complete application example that shows how to do that. Essentially you have to evaluate each character entered into the console and determine if it's a number. If it is a number, you have to move the cursor back 1 position so you can overwrite the value that was just entered. Prior to overwriting the value, you change the consoles foreground color.
using System;
namespace ConsoleApp2
{
public class Program
{
public static void Main(string[] args)
{
// Set up an infinite loop that will allow us to forever type unless 'Q' is pressed.
while(true)
{
ConsoleKeyInfo pressedKey = Console.ReadKey();
// If Q is pressed, we quit the app by ending the loop.
if (pressedKey.Key == ConsoleKey.Q)
{
break;
}
// Handle the pressed key character.
OnKeyDown(pressedKey.KeyChar);
}
}
private static void OnKeyDown(char key)
{
int number;
// Try to parse the key into a number.
// If it fails to parse, then we abort and listen for the next key.
// It will fail if anything other than a number was entered since integers can only store whole numbers.
if (!int.TryParse(key.ToString(), out number))
{
return;
}
// If we get here, then the user entered a number.
// Apply our logic for handling numbers
ChangeColorOfPreviousCharacters(ConsoleColor.Green, key.ToString());
}
private static void ChangeColorOfPreviousCharacters(ConsoleColor color, string originalValue)
{
// Store the original foreground color of the text so we can revert back to it later.
ConsoleColor originalColor = Console.ForegroundColor;
// Move the cursor on the console to the left 1 character so we overwrite the character previously entered
// with a new character that has the updated foreground color applied.
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.ForegroundColor = color;
// Re-write the original character back out, now with the "Green" color.
Console.Write(originalValue);
// Reset the consoles foreground color back to what ever it was originally. In this case, white.
Console.ForegroundColor = originalColor;
}
}
}
The output to the console will look like this:
I tried this:
if(PauseButton.Image != global::GripAIConsole.Icons.Resume)
{
PauseButton.Image = global::GripAIConsole.Icons.Resume;
ToolTipMainWin.SetToolTip(PauseButton, "Resume / Step <F4>");
}
And it doesn't work. I would have thought it was comparing pointers internally to see if they were pointing at the same place.
You are working with resources, so if you want to make sure the reference is right, compare using the static method Object.ReferenceEquals(). As ChrisF said, you should not use this to determine application logic. Using a simple bool variable is much better (and slightly more performance friendly).
I would define the possible states with an enum:
public enum State {
Stopped,
Pausing,
Running
}
And then define a state property or variable:
State _state;
then Change the states as follows:
void ChangeState(State newState)
{
_state = newState;
switch (newState) {
case State.Stopped:
PauseButton.Image = global::GripAIConsole.Icons.Pause;
ToolTipMainWin.SetToolTip(PauseButton, "Start game <F5>");
break;
case State.Pausing:
PauseButton.Image = global::GripAIConsole.Icons.Resume;
ToolTipMainWin.SetToolTip(PauseButton, "Resume / Step <F4>");
break;
case State.Running:
PauseButton.Image = global::GripAIConsole.Icons.Pause;
ToolTipMainWin.SetToolTip(PauseButton, "Pause <F4> / Stop game <F6>");
break;
}
}
... or whatever your logic requires.
This is much cleaner and understandable. A picture has to do with the GUI, not with the logic. Inferring the state of the logic from things displayed on the forms is weird.
I am using the Piccolo 2D ZUI library in a C# Winform application.
One of the examples that the library shows is adding a squiggle (line drawing) handler to the canvas.
The problem is that if you enable the squiggle handler and allow canvas dragging then both events occur at the same time.
What I would like to do is inherit the PDragEventhandler so that it only runs when the CTRL is not pressed down. Then when the CTRL key is pressed down the squiggler will run (I got this figured out).
The code used for the drag handler is:
InitializeComponent();
//add input event listener
pCanvas1.AddInputEventListener(new PDragEventHandler());
Can I inherit the PDragEventhandler and then say only run when CTRL not pressed? Or do I need to recompile the Piccolo library to enable this feature?
For java it is extremely straight foward. In the initialize you will want to make the following changes:
public void mouseDragged(PInputEvent e) {
super.mouseDragged(e);
// Update the squiggle while dragging.
updateSquiggle(e);
}
to
public void mouseDragged(PInputEvent e) {
super.mouseDragged(e);
if (e.isControlDown()) {
updateSquiggle(e);
}
}
Explanantion: This is possible because PInputEvent inherits the java event and therefore has the isControlDown() option. In C# this is not the case and you will need to extend it manually, or add it. There is a description of how to do it for C# (which I am not very familiar with) in Part 3 of the following tutorial.
For C# I would assume the listener should look something like the following:
protected void squiggle_ControlDown(object sender, PInputEventArgs e) {
PNode node = (PNode)sender;
switch (e.KeyCode) {
case Keys.Control:
updateSquiggle();
break;
}
}
I hope this helps, I wish it hadn't been so long since I'd used C# or I could have given you a more specific answer.
You can override acceptsEvent() method to control events dispatch. For example to accept events only with control key modifier:
public class DragHandler extends PDragEventhandler {
#Override
public boolean acceptsEvent(PInputEvent event, int type) {
return super.acceptsEvent(event, type) && event.isControlDown();
}
}
I'm making a simple Guess-The-Number game with a GUI. I need to wait on a loop waiting for the user to input a number in a text box and press "OK". How do I wait for an event inside a loop?
Note: I don't want message boxes. This is done in the main window, hence the need to wait for input.
EDIT: I should have explained myself better. I know that there's a loop inside the GUI. What I want is another loop inside a method. Maybe there's a better way to do this. I could code stuff inside the button's event handler, now that I think about it. Although I'd need global variables. Whataver, I'll think about it, but I hope my question is clearer now.
EDIT 2: Sorry that my question wasn't clear and the edit didn't do much help. First of all, the code is too big to be posted here. I'd probably have to post a screenshot of the GUI, so it wouldn't be of much use. Basically, I have two fields, "Max number" and "Number of allowed guesses". The user enters these two and clicks "Play". A new panel becomes available, with a text box and a "Guess" button. The user enters a guess, and the program checks to see if it's correct.
The purpose of the second infinite loop is to avoid global variables. See, each time the user clicks "Play", the game has to generate a new random number as the correct guess. If everything is done inside a method, no problem. But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
I'd also have to keep track of the remaining number of guesses outside of the method. Again, it's no big deal. I just want to avoid globals if I can.
Again, I'm sorry that my question wasn't too clear. I'm kind of tired, and I didn't feel like writing too much. If this still isn't clear, then don't bother. I'll think of something.
C# automatically loops infinitely waiting for events until your form is closed. You just need to respond to the button click event.
Jason Down's suggestion is wise, create a new GuessingGame class and add it to your project. I know you're worried about "global variables" (which everyone is taught in school never to use unless you absolutely have to), but think about your design specifications for a minute.
But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
As an alternative, store an instance of your GuessingGame class in the form. This is not a global variable! You said so yourself, the point of the game is keep track of the guesses and generate new numbers to guess every time "Play" is clicked. If you store an instance of the game in the form then open another form (e.g. a Help or About box), then the game's instance would not be available (thus, not global).
The GuessingGame object is going to look something like:
public class GuessingGame
{
private static Random _RNG = new Random();
private bool _GameRunning;
private bool _GameWon;
private int _Number;
private int _GuessesRemaining;
public int GuessesRemaining
{
get { return _GuessesRemaining; }
}
public bool GameEnded
{
get { return !_GameRunning; }
}
public bool GameWon
{
get { return _GameWon; }
}
public GuessingGame()
{
_GameRunning = false;
_GameWon = false;
}
public void StartNewGame(int numberOfGuesses, int max)
{
if (max <= 0)
throw new ArgumentOutOfRangeException("max", "Must be > 0");
if (max == int.MaxValue)
_Number = _RNG.Next();
else
_Number = _RNG.Next(0, max + 1);
_GuessesRemaining = numberOfGuesses;
_GameRunning = true;
}
public bool MakeGuess(int guess)
{
if (_GameRunning)
{
_GuessesRemaining--;
if (_GuessesRemaining <= 0)
{
_GameRunning = false;
_GameWon = false;
return false;
}
if (guess == _Number)
{
_GameWon = true;
return true;
}
else
{
return false;
}
}
else
{
throw new Exception("The game is not running. Call StartNewGame() before making a guess.");
}
}
}
This way, all the data related to the game is encapsulated within the class. Hooking up the events is easy in the codebehind of the form:
GuessingGame game = new GuessingGame();
private void btnPlay_Click(object sender, EventArgs e)
{
int numberOfGuesses = Convert.ToInt32(txtNumberOfGuesses.Text);
int max = Convert.ToInt32(txtMax.Text);
game.StartNewGame(numberOfGuesses, max);
}
private void btnGuess_Click(object sender, EventArgs e)
{
int guess = Convert.ToInt32(txtGuess.Text);
bool correct = game.MakeGuess(guess);
if (correct)
lblWin.Visible = true;
if (game.GameEnded)
{
// disable guess button, show loss label
}
}
You should probably look for a book to actually learn windows programming.
The very basics:
1) There is already an infinite loop deep down in the windows code somewhere. Any windows program is constantly looping and scanning for input.
2) Once input is found, this loop fires off an Event.
3) Your mission, should you choose to accept it, is to write event handlers to handle those events.
you are most likely doing it wrong as it has already been pointed out, but you can use this
Application.DoEvents();
to process events when you are on an actual loop
to do it the right way
- don't use a loop
- use an edit box for the input, then a button
- implement the button onclick event
Yes, and What if I am waiting for Speech events, it could happen anytime event when a function is running, I need to handle that without recursively call a function