Adding Input data validation to accept only integer values greater than 0 - c#

Im simulating a vending machine and would like to set the product quantity text box to only accept values greater than 0. when i enter -1 my program accepts this value and displays it which i dont want.can someone help please
code is:
//create a new Employee object
try // Exception handling to ensure that incorrect data type cannot be entered into text box creating a new product
{
Products newProd = new Products(this.textProdID.Text);
newProd.ProductName= this.textProdName.Text;
newProd.ProductQuantity= Convert.ToInt32(this.textProdQuantity.Text);
newProd.ProductPrice= Convert.ToDouble(this.textProdPrice.Text);
ProductList.Add(newProd);
MessageBox.Show(newProd.ProdName + " has been added to the product list");
}
catch
{
MessageBox.Show("Format entered into text box Is incorrect please check and try again");
}

You should add the Quantity range validation as per your specification - see the code snippet shown below:
//create a new Employee object
try // Exception handling to ensure that incorrect data type cannot be entered into text box creating a new product
{
Products newProd = new Products(this.textProdID.Text);
newProd.ProductName= this.textProdName.Text;
newProd.ProductQuantity= Convert.ToInt32(this.textProdQuantity.Text);
// add the input range validation
if (newProd.ProductQuantity<=0) throw new ArgumentException ("Quantity must be a positive number.");
newProd.ProductPrice= Convert.ToDouble(this.textProdPrice.Text);
ProductList.Add(newProd);
MessageBox.Show(newProd.ProdName + " has been added to the product list");
}
catch
{
MessageBox.Show("Format entered into text box Is incorrect please check and try again");
}
Another solution is just to show a MessageBox with error message if validation fail and return. Further performance optimization can be achieved by using TryParse() instead of Convert method, but considering relatively simple task, both solutions pertinent to this case will suffice the purpose. As a general recommendation, consider adding the input validation to the Control event (e.g. TextBox.TextChanged +=(s,e)=>{ // validation};
Also, pertinent to your case, consider setting the object to null in validation fail.
Hope this will help. Best regards,

Related

How can i solve "System.FormatException: 'Input string was not in a correct format.'" problem? [duplicate]

I added a textbox to my form and I'm trying to get the text from it but it comes up with an exception saying
"Input string was not in the correct format."
This is my code:
deleteQuestion = Convert.ToInt32(textBox6.Text);
addQuestion = Convert.ToInt32(textBox7.Text);
listOfQuestions.RemoveAt(deleteQuestion - 1);
foreach (RichTextBox box in boxForQuestions)
{
if (Convert.ToInt32(box.Tag) == deleteQuestion - 1)
{
boxForQuestions.Remove(box);
}
}
In the second part of the code my intention is to delete dynamically added rich text box.
Use Int32.TryParse if you are not in control of what your user types in those textboxes
int deleteQuestion;
if(!Int32.TryParse(textBox6.Text, out deleteQuestion))
{
MessageBox.Show("Not a valid number to delete a question!");
return;
}
int addQuestion;
if(!Int32.TryParse(textBox7.Text, out addQuestion))
{
MessageBox.Show("Not a valid number to add a question!");
return;
}
Of course, the same should be considered for the Tag property used inside the loop but this is set by your code so I presume that it is safe to consider it a valid integer
Another problem in your code is the remove inside the foreach loop. This cannot be done, you cannot change a collection while iterating over it. If you really need to remove an element from that collection you should consider to use a normal for... loop and looping from the last element towards the first one

Implementing a while loop to check for multiple exceptions in input validity

Sorry if this is a simple question; this is my first language and I'm trying my best to seek out and follow examples and explanations on this site and otherwise.
I've been trying to expand on a Microsoft C# tutorial program that creates "bank accounts." I'm trying to work on catching and handling exceptions, specifically by prompting the user to try again for a valid input.
I've come across this thread and many similar threads about running a loop while the input is invalid, and this example specifically using try/catch, which if I'm understanding correctly, is what I want to use here because I have a few lines of code that could throw multiple exceptions (it could be non-numerical or it could be negative). Following those and other examples, I can't figure out how to assign the initial balance input to a value that I can reference outside the loop (but still only within the CreateAccount method) once the input is valid.
I'm not sure what I have currently is working otherwise, but currently this code produces an error because initBalInput is left unassigned after the while loop, even though it's declared outside the loop and assigned in the try block.
public static void CreateAccount()
{
// Prompt for BankAccount constructor parameter {name} which is passed to BankAccount.Owner in constructor
Console.WriteLine("Name on new account: ");
string nameInput = Console.ReadLine();
decimal initBalInput;
bool valid = false;
while (valid == false)
{
try
{
Console.WriteLine("How much to deposit for initial balance: ");
initBalInput = Convert.ToDecimal(Console.ReadLine());
}
catch (ArgumentOutOfRangeException)
{
Console.WriteLine("Initial balance must be positive!");
valid = false;
continue;
}
catch (FormatException)
{
Console.WriteLine("Initial balance must be a number!");
valid = false;
continue;
}
valid = true;
}
// Create new instance "account" of type BankAccount and set its parameters
BankAccount account = new BankAccount(nameInput, initBalInput);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");
}
Instead of catching the exceptions, write the code that handles the invalid input.
public static void CreateAccount()
{
// Prompt for BankAccount constructor parameter {name} which is passed to BankAccount.Owner in constructor
Console.WriteLine("Name on new account: ");
string nameInput = Console.ReadLine();
string initBalInput = Console.ReadLine();
// try parse will check for invalid decimal values and also, positive values can be checked
if(decimal.TryParse(initBalInput, out decimal initBal) && initBal > 0) {
// Create new instance "account" of type BankAccount and set its parameters
BankAccount account = new BankAccount(nameInput, initBal);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");
} else {
Console.WriteLine("Invalid initial balance");
}
}
but currently this code produces an error because initBalInput is left unassigned after the while loop, even though it's declared outside the loop and assigned in the try block
The problem is that the compiler doesn't know if execution will ever reach the try block:
while (valid == false)
is evaluated at runtime. You and me both know that execution will enter at least once the while loop because valid is initially false but the compiler doesn't go into that type of analysis where variables are involved and therefore assumes execution might never enter the while loop and an unitialized initBalInput can be read.
That said, you should not get into the habit of using exepctions as control flow mechanisms. Exceptions should be exceptions, don't base the logic of your programs around exceptions. In your case, you should look into the method decimal.TryParse.
Also, always break up your problem into smaller problems. At the beginning, start small, make one liner methods that are obviously correct. It's very hard to write a bug in methods that are one or two lines long.
So what do you need?
A method that prompts the user for an input.
A method that validates the input
Something that asks the user to try again if the input is wrong.
Ok, numer one:
static string RequestUserInput(string message)
{
Console.Write(message);
return Console.ReadLine();
}
Number two: We already have it with decimal.TryParse(string, out decimal d). This method will return true if the input string can be parsed into a valid decimal number which will be assigned to d and false otherwise.
Number three:
public static decimal GetDecimalInput(string message)
{
decimal d;
while (true)
{
if (!decimal.TryParse(RequestUserInput(message), out d))
//tryparse failed, input is not a valid decimal number
Console.WriteLine("Initial balance must be a number!");
else if (d < 0) //try parse succeeded, we know input is a valid
// decimal number d but it might be negative.
Console.WriteLine("Initial balance must be positive!");
else
//we know inout is a valid non negative decimal number.
//break out of the loop, we don't need to ask again.
break;
}
return d;
}
And now, you put it all together:
var accountBalance = GetDecimalInput("How much to deposit for initial balance: ");
First, I have two articles on Exception handling that I consider required reading:
This one helps to classify the 4 common exception types - and if you should even consider catching them.
While this one goes into more details for good practices.
You should not be using convert, but parse. Or even better TryParse(). The exceptions on the string -> number conversion are the examples for vexing exceptions.
If there is no TryParse, I did once wrote a custom implementation of Int.TryParse() for someone still on Framework 1.1:
//Parse throws ArgumentNull, Format and Overflow Exceptions.
//And they only have Exception as base class in common, but identical handling code (output = 0 and return false).
bool TryParse(string input, out int output){
try{
output = int.Parse(input);
}
catch (Exception ex){
if(ex is ArgumentNullException ||
ex is FormatException ||
ex is OverflowException){
//these are the exceptions I am looking for. I will do my thing.
output = 0;
return false;
}
else{
//Not the exceptions I expect. Best to just let them go on their way.
throw;
}
}
//I am pretty sure the Exception replaces the return value in exception case.
//So this one will only be returned without any Exceptions, expected or unexpected
return true;
}
But that code looks like you want to have detailed information why it failed. At wich point you may have to write a detailed list of catch blocks.

Prompt user that input is out of range, based on comparing to sql query

Simply i have a textbox that my users will enter in a Fan rpm.
I need my program to take the entered fanrpm and compare it with a database item called classrpm. My query from the database Will only return 1 item ever.
I need fanrpm to be > classrpm, and if fanrpm >= class rpm a msg box to pop-up and state "Fan RPM exceeds ClassRPM(which is x), please enter a value under ClassRPM.
The code below was my test with fake variable names. I attempted to take and convert the user entered text and the database retrieved item, then using the converted values to do the above if statement, and then output a msg box to inform me if it succeeded.
private void textBox1_TextChanged(object sender, EventArgs e)
{
txfanrpm.MaxLength = 4
string classrpm;
string fanrpm;
using (Fanrpm ds = new Fanrpm(cbdesigntype.SelectedValue.ToString(), cbfansize.SelectedValue.ToString(), cbfanclass.SelectedValue.ToString()))
{
DataTable dt = ds.dataset.Tables[0];
List<string> coolList = new List<string>();
foreach (DataRow row in dt.Rows)
{
coolList.Add(row[0].ToString());
}
classrpm = coolList.ToString();
fanrpm = txfanrpm.Text.ToString();
int classrpmInt;
int fanrpmInt;
classrpmInt = Convert.ToInt32(classrpm);
fanrpmInt = Convert.ToInt32(fanrpm);
if (fanrpmInt >= classrpmInt)
{
MessageBox.Show(this, "user entered fanrpm higher then class rpm which is yadda yadda");
}
else if (fanrpmInt < classrpmInt)
{
MessageBox.Show(this, "Fanrpm is less then Classrpm");
}
}
}
I believe my problem stems in converting my database item into something usable to compare too. Also i think that if my setup works as intended it may tell a user entering 500 at each entry the textbox will refresh and redo the query leading to potential wasted processing power?
-Edit my sql statement works, Though putting the data into a list may be one of my problems.
No way that you could convert a List(Of String) to a single string in that way.
And what if your user types not a number in your textbox?
However, if, as you have said, your DataTable contains always only one row then you could simplify your code to something like this
private void textBox1_TextChanged(object sender, EventArgs e)
{
int classRPM;
int fanRPM;
using (Fanrpm ds = new Fanrpm(....)
{
DataTable dt = ds.dataset.Tables[0];
// Get and convert the value in the field named ClassRPM (assuming is a string)
classRPM = Convert.ToInt32(dt.Rows[0].Field<string>("ClassRPM"));
// Now check if the textbox contains a valid number
if(!Int32.TryParse(txfanrpm.Text, out fanRPM))
{
MessageBox.Show("Not a number....");
return;
}
// Start your logic
if (fanRPM >= classRPM)
{
MessageBox.Show(.....);
}
else if (fanRPM < classRPM)
{
MessageBox.Show(.......);
}
}
}
I think that a TextChanged event is not the proper place for this kind of code. This will trigger everytime your user presses a key and result in a trip to the database for every key press. So if your user wants to type 500 you end asking the db first for "5", then for "50" and finally for "500".
I would prefer to let the user types freely its values and then check when a verify button is pressed or, if this is not possible, to check in the Control.Validating event.
Calling List.ToString() just returns "System.Collections.Generic.List`1[System.String]" rather than any items in the string. (The "ToString()" method is actually inherited from Object, and just returns a string that represents what type of object the object is, except in special cases like StringBuilder.)
I would avoid the TextChanged event because that event will cause your code to execute ever keystroke, which might cause errors when a value is partially entered, or when a user accidentally enters and invalid value and has to backspace.

Textbox Contains Only Certain Characters C#

I have a simple WinForms app that generates random numbers based on the maximum number entered. For the maximum number text box I already have code that checks the 'key press' input so that numbers and "." are the only characters input. Hence, decimal numbers are allowed. However, I also want to check that the text box doesn't just contain 0's and "." An outline of my code is shown below:
if(txbInput.Text.Length == 0)
{
validation fails
}
else if(txbInput Contains just 0s and .)
{
validation also fails
}
else{
do maths
}
What would be the correct way to do this in the 'else if' statement?
Why don't you use Decimal.TryParse or Double.TryParse instead?
decimal d;
if(!decimal.TryParse(txbInput.Text, out d))
{
// validation fails, output an appropriate message to the user
}
else if (d == 0m)
{
// validation fails, output an appropriate message to the user
}
Try to use a NumericUpDown control instead of TextBox. This will eliminate validation and parsing in your code, except for comparing its Value property with zero.
Remark: To use this control for editing a real numbers, set an appropriate values for the properties DecimalPlaces and Increment.
you could use the KeyPressEvent for that...
like
private void tb1_KeyPressed(object o,KeyPressEvents e)
{
if(/*insert your validation here*/)
{//valid case
e.Handled=false;
}
else
{//false case
e.Handled=true;
//inform user about mistake
}
}
if you set Handled =true then nothing will happen after a key got pressed. Through that you can surpress keys in a textbox

Text box validation in c#

I have a form with a text box that should only accept 4 digit year values between 1980-2011.
I'm sure there should be a simple c# validation control to implement this validation check, but I can't seem to find it.
Any suggestions?
Catch Validating event and add you validation code in there.
For a complete example check MSDN page.
For simple validation you can also use a MaskedTextBox.
First I'd say, use the max length property set to 4 so that no extra characters can be entered
Beyond that you would have to hook up your own controls to validate it (could be a on text changed validation, on lost focus, etc) that would check that only digits are entered and they are between your specified values
A MaskedTextBox would do the trick. Set the mask to your needs: msdn.
But I doubt it will check if the value is between a range. It probably only checks if the value is a integer.
Ok, I'm not going to write all the code here but here's what I'd do:
In textchanged event of your textbox;
Check if entered value is numeric
Check if it meets the pattern (compare chars with what you want)
For this one, you need to compare each number one by one. I'd suggest you to write a method which parses the text and compares them with your expected values. Something like this:
private bool IsNumberValid(string text)
{
String min = "1980",max=2011;
try
{
int minNumber = Convert.ToInt32(min.Substring(0,text.length));
int maxNumber = Convert.ToInt32(max.Substring(0,text.length));
int myNumber = Convert.ToInt32(text);
if(myNumber <= max && myNumber >= min)
return true;
}
catch(Exception ex)
{
return false; // number is not numeric
}
return false;
}
There may be small errors, didn't write it in VS. You'd need to check the length of the text and not call this method if it is 0.

Categories

Resources