I am trying to make a windows form application that allows users to convert currency. I currently am working on making sure the value passed to the converter is only a positive integer. I have a message that pops up whenever anything other than a positive integer is entered, but when i enter something like "a" it keeps popping up and wont let me enter any other values.
public int USCurren(int value)
{
bool MessageShown;
string input = textBox1.Text;
while(true)
{
if (int.TryParse(input, out value))
{
if (MessageShown = false && value <= 0)
{
MessageShown = true;
MessageBox.Show("Please Input A Positive Integer");
textBox1.Clear();
}
else
return value;
MessageShown = false;
}
else
{
MessageShown = true;
MessageBox.Show("Please Input A Positive Integer");
textBox1.Clear();
}
}
You can do it like this way,
public int USCurren()
{
string input = textBox1.Text;
int value = -1;
try
{
if (int.TryParse(input, out value) && value > 0)
{
//Write your code here to convert the currency;
return value;
}
else
{
MessageBox.Show("Please Input A Positive Integer");
textBox1.Clear();
value = -1;
}
}
catch (Exception ex)
{
//Show exception here
}
return value;
}
Related
I am trying to validate string and integer input
For string input, I am trying to implement a validation that do not accept null or integers and this is what i have for now which doesn't have error handling for integers:
string name = Console.ReadLine().ToLower();
if (name.Length == 0)
{
Console.WriteLine("Name cannot be an empty field, try again!");
name = Console.ReadLine().ToLower();
return; }
and for the integer input, I would like to only accept integers and my problem is that these codes only allow me to enter a wrong input once before it shows me an error handing exception error
Console.Write("Enter exp: ");
int exp = 0;
try
{
exp = int.Parse(Console.ReadLine());
}
catch
{
Console.Write("Invalid exp, please enter a valid numerical value!: ");
exp = int.Parse(Console.ReadLine());
}
How should I handle all these input errors or are there any improvements I can make to my codes that I have for now?
All help is greatly appreciated !
Use a while loop until the user enters an integer.
And you should never use try...catch to check if an integer can be parsed. Always use int.TryParse(...). Everything else is a code smell (and slower in the case when it is not an integer).
var integerEntered = false;
int exp;
while (!integerEntered)
{
Console.Write("Enter exp: ");
var entered = Console.ReadLine();
integerEntered = int.TryParse(entered, out exp);
if (!integerEntered)
Console.WriteLine("That was no valid integer. Please try again.");
}
Console.Write("Enter exp: ");
int exp;
bool result = int.TryParse(Console.ReadLine(), out exp)
if(result)
{
//you have an int value entered
}
else
{
Console.WriteLine("Please enter a valid number")
}
//code continues
I suggest extracting methods, e.g.
For string values input
private static string ReadString(string title,
Func<string, string> errorMessage = null) {
while (true) {
if (!string.IsNullOrWhiteSpace(title))
Console(title);
string input = Console.ReadLine();
string message = errorMessage == null
? null
: errorMessage(input);
if (!string.IsNullOrWhiteSpace(message))
return input;
Console.WriteLine(message);
}
}
For integer values
private static string ReadInt(string title, Func<x, string> errorMessage = null) {
while (true) {
if (!string.IsNullOrWhiteSpace(title))
Console(title);
string input = Console.ReadLine();
if (!int.TryParse(input, out int result)) {
Console.WriteLine("Not valid integer value. Please, try again.");
continue;
}
string message = errorMessage == null
? null
: errorMessage(result);
if (!string.IsNullOrWhiteSpace(message))
return input;
Console.WriteLine(message);
}
}
Then you can use them
string name = ReadString("Please, enter name",
v => string.IsNullOrWhiteSpace(v)
? "Name cannot be an empty field, try again!"
: "");
int exp = ReadInt("Enter exp: ", v => v < 0 ? "Exp must not be negative", "");
I have modified my code to have a function for each of your questions.
//input
get_input_func(){
string str = Console.ReadLine();
}
//validate
validation_func() {
try {
if(str.Length == 0 ) {
Console.WriteLine("Cannot be empty, please enter a num");
}
if(str.All(!Char.IsDigit)) {
Console.WriteLine("You can only enter numbers");
}
//catch multiple exceptions
catch(Exception e) {
switch(e) {
Console.WriteLine(e);
get_input_func();
}
}
I have a method which counts the amount of times a user has withdrawn from the atm(as there is a limit) and also counts up the amount of money the user has withdrawn in the day. However the values in the count var and in the amountWithdrawn variable are both lost upon leaving the method, how do I keep them "saved"? Also as a side note, I have a class called Account which has the balance and such, would it be best to put them there? But would also like to know if it is possible to save the variables in the method for future reference.
public decimal WithDraw()
{
int timesWithdrawn = 9;
decimal amountWithdrawnToday = 0;
decimal money = 0;
bool success = false;
if (timesWithdrawn < 10)
{
do
{
//Console.WriteLine("{0} available to withdraw.", FundsAvailable);
Console.WriteLine("How much would you like to withdraw?");
try
{
money = decimal.Parse(Console.ReadLine());
if (money % 5 == 0 && money <= account.CurrentBalance && money <= 1000)
{
success = true;
}
if (money == 0)
{
bool exit = true;
Console.WriteLine("Do you want to exit? Type \"yes\", or \"no\".");
while (exit == true)
{
string response = Console.ReadLine();
if (response.ToLower() == "yes")
{
break;
}
else
{
exit = false;
}
}
}
}
catch (FormatException)
{
Console.WriteLine("Please enter a number to withdraw.");
}
} while (success == false);
//do while this is true
Console.WriteLine(account.CurrentBalance);
Console.WriteLine("Withdrawing {0} pounds.", money);
Console.WriteLine("You have {0} remaining in your account.", account.CurrentBalance - money);
amountWithdrawnToday += money;
timesWithdrawn += 1;
Console.WriteLine("{0} pounds withdrawn today", amountWithdrawnToday);
return account.CurrentBalance -= money;
}
else
{
Console.WriteLine("You have exceeded daily withdrawls. You have withdrawn {0}", amountWithdrawnToday);
return amountWithdrawnToday;
}
}
I will suggest you need to put those variable in the Account class, also I suggest that you could put the withdraw method itself in the Account class, This could be more OOP friendly.
and to save the timesWithdrawn number, you just need to make it as class instance vairable instead of making it local variable
here is the code
class Account
{
public decimal CurrentBalance { get; set; }
public int timesWithdrawn { get; set; } = 9;
public decimal WithDraw()
{
decimal amountWithdrawnToday = 0;
decimal money = 0;
bool success = false;
if (timesWithdrawn < 10)
{
do
{
//Console.WriteLine("{0} available to withdraw.", FundsAvailable);
Console.WriteLine("How much would you like to withdraw?");
try
{
money = decimal.Parse(Console.ReadLine());
if (money % 5 == 0 && money <= CurrentBalance && money <= 1000)
{
success = true;
}
if (money == 0)
{
bool exit = true;
Console.WriteLine("Do you want to exit? Type \"yes\", or \"no\".");
while (exit == true)
{
string response = Console.ReadLine();
if (response.ToLower() == "yes")
{
break;
}
else
{
exit = false;
}
}
}
}
catch (FormatException)
{
Console.WriteLine("Please enter a number to withdraw.");
}
} while (success == false);
//do while this is true
Console.WriteLine(CurrentBalance);
Console.WriteLine("Withdrawing {0} pounds.", money);
Console.WriteLine("You have {0} remaining in your account.", CurrentBalance - money);
amountWithdrawnToday += money;
timesWithdrawn += 1;
Console.WriteLine("{0} pounds withdrawn today", amountWithdrawnToday);
return CurrentBalance -= money;
}
else
{
Console.WriteLine("You have exceeded daily withdrawls. You have withdrawn {0}", amountWithdrawnToday);
return amountWithdrawnToday;
}
}
}
as you notice from the code, I removed the reference to the account variable and made the CurrentBalance as instance variable and also the timesWithdrawn.
this could preserve the value of the timesWithdrawn even after the method has been finished.
As long as the program is running it is saved there, but once quite it resets, so yes you have to save it somewhere in a database a excel table or in a text document
Saving data to a file in C#
check this out if you need some help how to do stuff like this
You could pass an 'out' parameter to the function, it's basically a variable you send to the function, which holds its value outside the function.
For example:
public void WithDraw(out int c) {
c = something ; //c must receive a value during the call
}
int myvar; // the parameter which will hold the value when the function returns
WithDraw(out myvar); //call to the function
myvar //will now hold a value from the function (something)
You could also consider returning a tuple, or putting the value you wish to save into a 'global variable'.
The title is a bit messy in regards to getting what I want out there and it's my first time on here.
Basically I have converted my string into an int :
string _val = Console.ReadLine();
tempScoreToWin = Convert.ToInt32(_val);
And what I want to know is when user a presses enter without entering a value an error will occur and the application will end.
How can I get around this?
Here is my full code:
while (true)
{ //This will allow the player to manually change the score.
//------------------------------------------------------------------------------------------
string _val = "";
ConsoleKeyInfo key;
do
{
key = Console.ReadKey(true);
if (key.Key != ConsoleKey.Backspace)
{
double val = 0;
bool _x = double.TryParse(key.KeyChar.ToString(), out val);
if (_x)
{
_val += key.KeyChar;
Console.Write(key.KeyChar);
}
}
else
{
if (key.Key == ConsoleKey.Backspace && _val.Length > 0)
{
_val = _val.Substring(0, (_val.Length - 1));
Console.Write("\b \b");
}
}
}
while (key.Key != ConsoleKey.Enter);
Console.WriteLine();
//-----------------------------------------------------------------
tempScoreToWin = Convert.ToInt32(_val); // Converting the users input (Console.ReadLine()) into an integer.
if (tempScoreToWin > 0) // If the users input is higher than zero ...
{
scoreToWin = tempScoreToWin; // Reset the scoreToWin variable with the value of tempScoreToWin.
Console.WriteLine("The score has been set to {0}.", scoreToWin); // Let the user know that the score has been changed successfully.
break; // Break out of the while loop.
}
else
{ // If the player has not put a correct integer in ...
Console.WriteLine("The score has been set at a default of {0}.", scoreToWin); // then the score will be set to the default value of scoreToWin
break; // Break out of the while loop.
}
}
Console.ReadLine();
Console.Clear();
//-----------------------------------------------------------------
Cheers!
Using TryParse will allow you to parse a string for an integer while also checking if it succeeded.
if(int.TryParse(_val, out tempScoreToWin)
{
//Parse succeeded
}
else
{
//Parse failed
}
Brilliant! That worked MrZander. Thank you guys so much for the quick answer. I really appreciate it :)
if (int.TryParse(_val, out tempScoreToWin))
{
if (tempScoreToWin > 0) // If the users input is higher than zero ...
{
scoreToWin = tempScoreToWin; // Reset the scoreToWin variable with the value of tempScoreToWin.
Console.WriteLine("The score has been set to {0}.", scoreToWin); // Let the user know that the score has been changed successfully.
break; // Break out of the while loop.
}
else
{ // If the player has not put a correct integer in ...
Console.WriteLine("The score has been set at a default of {0}.", scoreToWin); // then the score will be set to the default value of scoreToWin
break; // Break out of the while loop.
}
}
else
{
//Parse failed
}
So, I am trying to create a pizza ordering program for my school project which requires the customer/user to input their details e.g name phone number address and what not. I store all those information into a string array, but how to I only allow numbers to be entered into the phone number section. I tried using a method which I have been provided with but it doesn't seem to work it just errors.
Here is my current code
public static string[] GetCustInfo(string [] custInfo)
{
start:
bool success = false;
Console.Write("\n" + "Please input D for Delivery OR P for Pickup($3 for Delivery): " + "\n");
custInfo[0] = Console.ReadLine();
while (success != true)
{
if (custInfo[0] == "D" || custInfo[0] == "d")
{
custInfo[1] = ReadString("Please enter your name: ");
Console.Write(Readint("Please enter your Ph No. : "));
custInfo[2] = Console.ReadLine();
custInfo[3] = ReadString("Please enter your adress: ");
success = true;
}
else if (custInfo[0] == "P" || custInfo[0] == "p")
{
custInfo[1] = ReadString("Please enter your name: ");
success = true;
}
else
{
goto start;
}
}
return custInfo;
}
Here are the methods I am using to prevent the user from entering a number or letter:
public static int Readint(String prompt)
{
int userInput = 0;
Boolean success = false;
while (!success)
{
Console.Write(prompt);
try
{
userInput = Convert.ToInt32(Console.ReadLine());
success = true;
}
catch
{
Console.WriteLine("Please Enter a VALID NUMBER");
}
}
return userInput;
}
public static string ReadString(String prompt)
{
string userInput = " ";
Boolean success = false;
while (!success)
{
Console.Write(prompt);
try
{
userInput = Console.ReadLine();
if (userInput.Length <= 20 && userInput.Length>1)
{
success = true;
foreach (char charcter in userInput)
{
if (Char.IsNumber(charcter) || Char.IsSymbol(charcter) || Char.IsPunctuation(charcter))
{
success = false;
}
}
}
if (success == false)
{
Console.WriteLine("Please enter a valid input!" + "\n");
}
else
{
success = true;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return userInput;
}
I tried inserting:
custInfo[2] = Readint("Please enter your Ph No.");
but it just gave me an error saying:
"cannot implicate type int to string"
but how to I only allow numbers to be entered into the phone number
You ReadInt method already takes care of this point with the while-loop and the try/catch clause. If you want to store this information into the array you need to get your number into the correct/fitting data type for the array saying: string. You return value of ReadInt is an int and this class has an implementation of the ToString method which in your case would convert the int into a string. So you can simply call it on the returnvalue of ReadInt:
custInfo[1] = ReadString("Please enter your name: ");
custInfo[2] = Readint("Please enter your Ph No.").ToString();
custInfo[3] = ReadString("Please enter your adress: ");
Insted of public static string[] GetCustInfo(string [] custInfo), you might add array of an objects, but keep in mind next : Object array store elements of different types in a single collection. An object reference can point to any derived type instance.
Whatever you could do this :
object[] array1 = new object[5];
array1[0] = new object();
array1[1] = new StringBuilder("Initialized");
array1[2] = "String literal";
array1[3] = 3;
array1[4] = null;
So in your case: this might look like this
public static object[] GetCustInfo(object [] custInfo)
You trying to insert an int value into string array..surely make sense that you convert the value into a string?
Im wondering if theres a way to have the catch statement clear the text box that caused the error. Here's my validation code so far:
private bool ValidateForm()
{
int val1, direction = 0;
double speed = 0.0;
bool validated = false;
if (txtName.Text != "")
{
try
{
// attempts to convert values into their primary data types.
// Any errors will throw an exception that will be reported
// as invalid data
val1 = Convert.ToInt32(txtXPos.Text);
val1 = Convert.ToInt32(txtYPos.Text);
speed = Convert.ToDouble(txtSpeed.Text);
direction = Convert.ToInt32(txtDirection.Text);
validated = true;
}
catch
{
MessageBox.Show(
"You have an invalid value entered. Please check your entry.",
"Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if (speed < 0.0 || speed > (double)newAirplane.PlanePosition.MaxSpeed)
{
MessageBox.Show(
"Speed entered is out of range.",
"Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if (direction < 0 || direction > 359)
{
MessageBox.Show(
"Direction is out of range",
"Invalid values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show(
"Please enter a name.",
"Blank Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return validated;
}
You could declare a variable outside of the try block:
TextBox lastTextBox = null;
Then prior to each conversion, set this to the text box you will be fetching a value from, like so:
lastTextBox = txtXPos;
val1 = Convert.ToInt32(txtXPos.Text);
Then in your catch block:
if (lastTextBox != null)
lastTextBox.Text = "";
Assuming this is Winforms, the best thing to do is probably to use the built-in ErrorProvider rather than try to roll your own validation framework.
Instead of "Try Catch" blocks I will sugest you to use Int32.TryParse Method and Double.TryParse
The important point to note here is that you are using exceptions for non-exceptional circumstances. That is to say, the user entering an invalid number is not exceptional - it's almost expected!
You should be using the TryParse methods:
bool xValValid = Int32.TryParse(txtXPos.Text, out val1);
if(!xValValid){
txtXPos.Text = "";
validated = false;
MessageBox.Show("You have an invalid value entered. Please check your entry.", "Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
You can also extract the logic for a single text box into something like:
private bool Validated<T>(Control tb, out T value)
{
try
{
value = return (T)Convert.ChangeType(tb.Text, typeof(T));
return true;
}
catch
{
value = default(T);
tb.Text = "";
return false;
}
}
Which you could simply use as:
if (!Validated<int>(txtXPos, out val1) ||
!Validated<int>(txtYPos, out val2) ||
!Validated<double>(txtSpeed, out speed) ||
!Validated<int>(txtDirection, out direction))
{
MessageBox.Show("Validation failed");
}
[Edit]
Also, clearing the textboxes is a bad idea from usability point of view, especially inside TextChanged event. I wouldn't like my textbox being cleared if I accidentally type a letter inside. Much better way would be to use an ErrorProvider, or indicate unobtrusively that the input is wrong in some other way.
[Edit2]
Conversion from string to primitive types is possible using Convert.ChangeType, but you can supply any conversion method you like, if you need to parse a more complex string into a class of your own:
private bool Validated<T>(Control tb, Func<string, T> converter, out T value)
{
try
{
value = converter(tb.Text);
return true;
}
catch
{
value = default(T);
tb.Text = "";
return false;
}
}
And then use it like this:
if (!Validated<int>(txtXPos, Convert.ToInt32, out val1) ||
!Validated<int>(txtYPos, Convert.ToInt32, out val2) ||
!Validated<double>(txtSpeed, Convert.ToDouble, out speed) ||
!Validated<int>(txtDirection, Convert.ToInt32, out direction))
{
MessageBox.Show("Validation failed");
}
This way you can pass any delegate with the signature Func<string, T> which will do the actual conversion, or throw an exception if it fails.
I would suggest making a function like,
private bool TryReadClear(TextBox txtBox, out double value)
{
if (double.TryParse(txtBox.Text, out value))
{
txtBox.BackColor = Color.White;
return true;
}
else
{
txtBox.BackColor = Color.Red;
txtBox.Text = string.Empty;
return false;
}
}
and then replace your try/catch block with this simplified code:
validated = validated && TryReadClear(txtXPos, out val1);
validated = validated && TryReadClear(txtYPos, out val1);
validated = validated && TryReadClear(txtSpeed, out speed);
validated = validated && TryReadClear(txtDirection, out direction);
if (!validated)
{
MessageBox.Show("You have an invalid value entered. Please check your entry.", "Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
The benefit of this method is that it will check all text boxes each pass, and clear all the invalid ones. Using #cdhowie's solution will only clear the first failure, not all.
EDIT: You could even do something cool like setting the background color to red to visually highlight the bad textboxes (edited above code to show example).
private bool ValidateForm()
{
int val1, direction = 0;
double speed = 0.0;
bool validated;
if (txtName.Text != "")
{
try
{
//attempts to convert values into their primary data types. Any errors will throw an exception that will be reported as invalid data
int counter = 1;
val1 = Convert.ToInt32(txtXPos.Text);
counter = counter + 1;
val1 = Convert.ToInt32(txtYPos.Text);
counter = counter + 1;
speed = Convert.ToDouble(txtSpeed.Text);
counter = counter + 1;
direction = Convert.ToInt32(txtDirection.Text);
validated = true;
}
catch
{
switch(counter)
case 1:
txtXPos.Text="";
break;
case 2:
txtYPos.Text="";
break;
case 3:
txtSpeed.Text="";
break;
case 4:
txtDirection.Text="";
break;
default:
break;
MessageBox.Show("You have an invalid value entered. Please check your entry.", "Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
validated = false;
}
if (speed < 0.0 || speed > (double)newAirplane.PlanePosition.MaxSpeed)
{
MessageBox.Show("Speed entered is out of range.", "Invalid Values", MessageBoxButtons.OK, MessageBoxIcon.Error);
validated = false;
}
if (direction < 0 || direction > 359)
MessageBox.Show("Direction is out of range", "Invalid values", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
MessageBox.Show("Please enter a name.", "Blank Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
validated = false;
}
return validated;
}
Think this is what you are expecting.