Using Linq to loop through all controls only get the first control - c#

I have a form with 5 text boxes and a button, I want to check all these text boxes for empty or null user input.
Using this simple method:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (verifyUI() == true)
MessageBox.Show("user input for all textboxes was correct!");
else
MessageBox.Show("user input for all textboxes was missing!");
}
private bool verifyUI()
{
bool userInputOk = false;
foreach (Control cTxt in Controls.OfType<TextBox>())
{
if (string.IsNullOrWhiteSpace(cTxt.Text) || cTxt.Text == "")
{
userInputOk = false;
}
else
{
userInputOk = true;
}
}
return userInputOk;
}
}
When I enter a value in text box 1 , the method is checking only the first text box and returning true, and ignoring all other text boxes.
I am sure something is wrong in the logic of the method which I am using.

It seems you want to know if any input is wrong (or all the input is correct):
private bool verifyUI() {
return !Controls
.OfType<TextBox>()
.Any(cTxt => string.IsNullOrWhiteSpace(cTxt.Text));
}
or (equivalent All() implementation)
private bool verifyUI() {
return Controls
.OfType<TextBox>()
.All(cTxt => !string.IsNullOrWhiteSpace(cTxt.Text));
}
In your current code you constantly re-write userInputOk and so return the last value

The easiest way would be to use the All method:
private bool verifyUI()
{
return Controls.OfType<TextBox>().All(tb => !string.IsNullOrWhiteSpace(tb.Text));
}
You could also invert the logic and use the Any method as well. I prefer All in this case simply because it conveys the intention better and makes the logic more clear for the next person.

I think just for checking whether any textbox is not filled. following code is enough.
private bool verifyUI()
{
bool alluserInputsOk = true;
foreach (Control cTxt in Controls.OfType<TextBox>())
{
if (string.IsNullOrWhiteSpace(cTxt.Text))
{
userInputOk = false;
break;
}
}
return userInputOk;
}
or You can use .any() method with your list

Your code is actually only checking whether the last control in the list is blank or not, since that's where your iteration ends.

Your code checks only the last textbox it finds. Try this All() statement instead:
bool userInputOk = Controls.OfType<TextBox>()
.All(tb => !string.IsNullOrWhiteSpace(tb.Text));
Note that string.IsNullOrWhiteSpace() is true for string.Empty, too. So you don't need that extra check for string.Empty.

Actually, it's probably looping though all controls, but only returning the result of the last one, since you set the value of userInputOk back to true if the control has valid text. So the end result is the result for the last control. Now it's possible that textBox1 is the last control in the collection, depending on how they were added. You can either just remove the else block, which will flag userInputOk only if a control has an invalid value, or use Linq:
bool userInputOk =
!Controls.OfType<TextBox>()
.Any(cTxt => string.IsNullOrWhiteSpace(cTxt.Text))

Related

Can the ErrorProvider be queried to see if it has set any errors?

I have this code to do some basic sanity checking before posting a record:
if (string.IsNullOrWhiteSpace(textBoxFirstName.Text))
{
errorProvider.SetError(textBoxFirstName, "Enter a first name");
}
if (string.IsNullOrWhiteSpace(textBoxLastName.Text))
{
errorProvider.SetError(textBoxLastName, "Enter a last name");
}
...but I want to then do something like this to exit the handler if either of those conditions has been met:
if (errorProvider.SetErrorCount > 0) then return;
...but I see no way to do that. I don't want to have to write an "OR" statement to see if either of the textBoxes I'm checking are empty and then short circuit the handler that way.
Is there a way to tell whether the errorProvider is "dirty" to avoid cluttery code?
Write a method and pass it the error message and the control. Have a counter variable and increase the counter within the method. Here is some pseudocode:
private int errorCount;
SetError(Control c, string message)
{
errorProvider.SetError(c, message);
errorCount++;
}
One option would be to use the GetError method off of the ErrorProvider.
// possibly use a backing field for all controls to evaluate
private readonly Control[] textBoxes = new[] { textBoxFirstName, textBoxLastName };
// helper property to evaluate the controls
private bool HasErrors
{
get { return textBoxes.Any(x => !string.IsNullOrEmpty(errorProvider.GetError(x)); }
}

Use a "null" switch statement to determine if a range of TextBoxes are null, instead of using if-else statements

At the moment, I am trying to write a method that has inside it a switch statement which uses as the "trigger" the reserved word null. This is because I want all the TextBoxes, in this switch statement to be tested for the state of being null, and if they are, a MessageDialog (which I've coded for each box) will appear saying that 'You cannot leave "insert name of TextBox here", blank. You must enter text'
In short, is it possible to use a switch statement to test if a bunch of TextBoxes are null, rather than using a whole sequence of (unwieldy) if-else statements? If so, how? (please)
I should add that I've been at this problem for the last hour and a half, with no success.
Any help is gratefully accepted.
If you specifically want to use a Switch:
Make them into a List or an Array, if it is not that already.
List<string> TextBoxCollection = new List<string>();
TextBoxCollection.Add(yourTextBoxStringValue); //Do this for each of the boxes.
Then just iterate through the TextBoxCollection.
foreach (string textContent in TextBoxCollection)
{
switch (textContent)
{
case null:
{
//Do the message error here.
break;
}
case "otherRequirement":
{
//Do other stuff here.
break;
}
case "anotherRequirement":
case "oneMoreRequirement":
{
//Do different things here, maybe?
break;
}
}
}
This will iterate through the collection, and then test each string from it.
I would not recommend doing this, unless all the text boxes' values have to be validated for multiple conditions, all the same, in which case this will look more tidy than an If Else for each string value.
If you want a quick one line solution to check this for each one, you can just use:
if (string.IsNullOrWhiteSpace(yourTextBox.Text)) { //Throw error here. }
You can do the following:
Create a method, which will loop over Controls recursively:
public static void ForAllControls(this Control parent, Action<Control> action)
{
foreach (Control c in parent.Controls)
{
action(c);
ForAllControls(c, action);
}
}
Create a method, which will validate the TextBox:
public void validateTextBoxes(Control c)
{
if (c.GetType() == typeof(TextBox))
if(TextBox.Text == "") MessageBox.Show("insert name of TextBox here")
}
Call the method, like this:
this.ForAllControls(c => {
if (c.GetType() == typeof(TextBox)) validateTextBoxes(c);
});
Or use ErrorProvider.SetError();
This may not be using switch but it will save you some lines.
public static void IsTxtNull(Textbox xTxt, string msg)
{
if(string.IsNullOrEmpty(xTxt.Text)
MessageBox.Show(msg);
}
Then call it like this
IsTxtNull(YourTxtBox1,"Message for YourTxtBox1");
IsTxtNull(YourTxtBox2,"Message for YourTxtBox2");
IsTxtNull(YourTxtBox3,"Message for YourTxtBox3");
etc...

How do I make it so that a label changes text according to a variable

I have a label and I want it to display either Player or Console depending on what the variable answer is.
private void playerLabel_Click(object sender, EventArgs e)
{
string playerDetail = "Player",
consoleDetail = "Console";
if (Class.Method.Variable == 1)
{
Show.playerDetail();
}
if else (Class.Method.Variable == 0)
{
Show.consoleDetail();
}
}`
I then want to make it so that the label shows the string instead if you get me. I know I am not doing this properly but I can't work out how exactly to do this.
private void playerLabel_Click(object sender, EventArgs e)
{
string labelText = playerLabel.Text;
if (Class.Method.Variable == 1)
{
labelText = "Player";
Show.playerDetail();
}
else if(Class.Method.Variable == 0)
{
labelText = "Console";
Show.consoleDetail();
}
playerLabel.Text = labelText;
}
It would be better if your methods in Show class returned the appropriate string, so that you can do: playerLabel.Text = Show.WhateverDetail();. Additionally its even better if you could tie the Show method with the Variable value so that you don't have to use an if-else logic at all.
a. The Text property of the Label is what you want to set your strings to.
playerLabel.Text = playerDetail;
playerLabel.Text = consoleDetail;
b. Your if/else method should be in the form of:
if (test)
{
}
else if
{
}
else
{
}
You don't need the else if bit in the middle if there are only two branches.
c. I'm not sure about Show.consoleDetail() and Show.playerDetail(). Are 'consoleDetail()' and 'playerDetail()' method calls?

Numeric TextBox - using Double.TryParse

I know this is an age-old question with many an answer, but I haven't found any good, robust answers.
The requirement is a textbox that will always contain a string that Double.TryParse will return true on.
Most of the implementations I have seen do not guard against input such as: "10.45.8". This is a problem.
The preferable way of doing this is entirely with events, such as TextInput and KeyDown (for spaces). The problem with these is that it is quite complicated to get a string representing the new Text before it is changed (or the old Text after it is changed). The problem with TextChanged is that it doesn't provide a way to get the old Text.
If you could somehow get the new Text before it changes, that would be the most helpful, since you could test it against Double.TryParse. There may be a better solution though.
What is the best way to do this?
The best answer to this question is one that has several approaches and compares them.
Approach 1
Use a combination of the TextChanged and KeyDown events for a TextBox. On KeyDown you could save the current text in the textbox and then do your Double.TryParse in the TextChanged event. If the text entered is not valid, then you would revert to the old text value. This would look like:
private int oldIndex = 0;
private string oldText = String.Empty;
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
double val;
if (!Double.TryParse(textBox1.Text, out val))
{
textBox1.TextChanged -= textBox1_TextChanged;
textBox1.Text = oldText;
textBox1.CaretIndex = oldIndex;
textBox1.TextChanged += textBox1_TextChanged;
}
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
oldIndex = textBox1.CaretIndex;
oldText = textBox1.Text;
}
The CaratIndex is useful in not annoying your user to death with moving the cursor to the first position on failed validation. However, this method doesn't catch the SpaceBar key press. It will allow text to be entered like this "1234.56 ". Also, pasting text will not be properly validated. Beyond this, I don't like messing with the event handlers during text updating.
Approach 2
This approach should meet your needs.
Use the PreviewKeyDown and PreviewTextInput event handlers. By watching these events and handling accordingly, you don't need to worry about reverting to a previous text value in your text box. PreviewKeyDown can be used to watch for and ignore your SpaceBar key press and PreviewTextInput can be used to test your new textbox value before it is assigned.
private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
e.Handled = true;
}
}
private void textBox1_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
//Create a string combining the text to be entered with what is already there.
//Being careful of new text positioning here, though it isn't truly necessary for validation of number format.
int cursorPos = textBox1.CaretIndex;
string nextText;
if (cursorPos > 0)
{
nextText = textBox1.Text.Substring(0, cursorPos) + e.Text + textBox1.Text.Substring(cursorPos);
}
else
{
nextText = textBox1.Text + e.Text;
}
double testVal;
if (!Double.TryParse(nextText, out testVal))
{
e.Handled = true;
}
}
This approach does a better job of catching invalid input before it gets into the textbox. However, setting the event to be Handled I suppose could get you into trouble depending on the rest of the destinations in the routing list for the message. A last piece that isn't handled here is the ability of the user to paste invalid input into the text box. This can be handled with the addition of this code, which is built off of Paste Event in a WPF TextBox.
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
double testVal;
bool ok = false;
var isText = e.SourceDataObject.GetDataPresent(System.Windows.DataFormats.Text, true);
if (isText)
{
var text = e.SourceDataObject.GetData(DataFormats.Text) as string;
if (Double.TryParse(text, out testVal))
{
ok = true;
}
}
if (!ok)
{
e.CancelCommand();
}
}
Add this handler with this code after the InitializeComponent call:
DataObject.AddPastingHandler(textBox1, new DataObjectPastingEventHandler(OnPaste));
Its really annoying that TextBox does not provide PreviewTextChanged event and everybody should invent the wheel every time to emulate it. I solved exactly the same issue recently and even published my solution on github as WpfEx project (take a look at TextBoxBehavior.cs and TextBoxDoubleValidator.cs).
Adam S's answer is very good, but we should consider few other corner cases as well.
Selected text.
During coputing resulting text in our textBox_PreviewTextInput event handler we should consider that user can select some text in text box and new input will replace it. So we should use something like:
private static void PreviewTextInputForDouble(object sender,
TextCompositionEventArgs e)
{
// e.Text contains only new text and we should create full text manually
var textBox = (TextBox)sender;
string fullText;
// If text box contains selected text we should replace it with e.Text
if (textBox.SelectionLength > 0)
{
fullText = textBox.Text.Replace(textBox.SelectedText, e.Text);
}
else
{
// And only otherwise we should insert e.Text at caret position
fullText = textBox.Text.Insert(textBox.CaretIndex, e.Text);
}
// Now we should validate our fullText, but not with
// Double.TryParse. We should use more complicated validation logic.
bool isTextValid = TextBoxDoubleValidator.IsValid(fullText);
// Interrupting this event if fullText is invalid
e.Handled = !isTextValid;
}
And we should use the same logic when we'll handle OnPaste event.
Validating the text
We can't use simple Double.TryParse, because user can type '+.' to type '+.1' ('+.1' - is absolutely valid string for double), so our validation method should return true on '+.' or '-.' strings (I even created separate class called TextBoxDoubleValidator and the set of unit tests because this logic is so important).
Before dig into implementation lets take a look at set of unit tests that will cover all corner cases for validation method:
[TestCase("", Result = true)]
[TestCase(".", Result = true)]
[TestCase("-.", Result = true)]
[TestCase("-.1", Result = true)]
[TestCase("+", Result = true)]
[TestCase("-", Result = true)]
[TestCase(".0", Result = true)]
[TestCase("1.0", Result = true)]
[TestCase("+1.0", Result = true)]
[TestCase("-1.0", Result = true)]
[TestCase("001.0", Result = true)]
[TestCase(" ", Result = false)]
[TestCase("..", Result = false)]
[TestCase("..1", Result = false)]
[TestCase("1+0", Result = false)]
[TestCase("1.a", Result = false)]
[TestCase("1..1", Result = false)]
[TestCase("a11", Result = false)]
[SetCulture("en-US")]
public bool TestIsTextValid(string text)
{
bool isValid = TextBoxDoubleValidator.IsValid(text);
Console.WriteLine("'{0}' is {1}", text, isValid ? "valid" : "not valid");
return isValid;
}
Note, that I'm using SetCulture("en-US') attribute, because decimal separator "local-specific".
I think I cover all corner cases with those tests but with this tool in your hands you can easily "emulate" user imput and check (and reuse) whatever cases you want. And now lets take a look at TextBoxDoubleValidator.IsValid method:
/// <summary>
/// Helper class that validates text box input for double values.
/// </summary>
internal static class TextBoxDoubleValidator
{
private static readonly ThreadLocal<NumberFormatInfo> _numbersFormat = new ThreadLocal<NumberFormatInfo>(
() => Thread.CurrentThread.CurrentCulture.NumberFormat);
/// <summary>
/// Returns true if input <param name="text"/> is accepted by IsDouble text box.
/// </summary>
public static bool IsValid(string text)
{
// First corner case: null or empty string is a valid text in our case
if (text.IsNullOrEmpty())
return true;
// '.', '+', '-', '+.' or '-.' - are invalid doubles, but we should accept them
// because user can continue typeing correct value (like .1, +1, -0.12, +.1, -.2)
if (text == _numbersFormat.Value.NumberDecimalSeparator ||
text == _numbersFormat.Value.NegativeSign ||
text == _numbersFormat.Value.PositiveSign ||
text == _numbersFormat.Value.NegativeSign + _numbersFormat.Value.NumberDecimalSeparator ||
text == _numbersFormat.Value.PositiveSign + _numbersFormat.Value.NumberDecimalSeparator)
return true;
// Now, lets check, whether text is a valid double
bool isValidDouble = StringEx.IsDouble(text);
// If text is a valid double - we're done
if (isValidDouble)
return true;
// Text could be invalid, but we still could accept such input.
// For example, we should accepted "1.", because after that user will type 1.12
// But we should not accept "..1"
int separatorCount = CountOccurances(text, _numbersFormat.Value.NumberDecimalSeparator);
// If text is not double and we don't have separator in this text
// or if we have more than one separator in this text, than text is invalid
if (separatorCount != 1)
return false;
// Lets remove first separator from our input text
string textWithoutNumbersSeparator = RemoveFirstOccurrance(text, _numbersFormat.Value.NumberDecimalSeparator);
// Second corner case:
// '.' is also valid text, because .1 is a valid double value and user may try to type this value
if (textWithoutNumbersSeparator.IsNullOrEmpty())
return true;
// Now, textWithoutNumbersSeparator should be valid if text contains only one
// numberic separator
bool isModifiedTextValid = StringEx.IsDouble(textWithoutNumbersSeparator);
return isModifiedTextValid;
}
/// <summary>
/// Returns number of occurances of value in text
/// </summary>
private static int CountOccurances(string text, string value)
{
string[] subStrings = text.Split(new[] { value }, StringSplitOptions.None);
return subStrings.Length - 1;
}
/// <summary>
/// Removes first occurance of valud from text.
/// </summary>
private static string RemoveFirstOccurrance(string text, string value)
{
if (string.IsNullOrEmpty(text))
return String.Empty;
if (string.IsNullOrEmpty(value))
return text;
int idx = text.IndexOf(value, StringComparison.InvariantCulture);
if (idx == -1)
return text;
return text.Remove(idx, value.Length);
}
}
A comment rather than an answer, but...
I would beware of validating input on each keypress as it can have unintended consequences and annoy the end user.
For example, I remember being annoyed by a datepicker control which would not allow dates in the future, and was initialized to today's date. It performed validation after entering the day, month or year, so that it was impossible to enter a month/day later than the current date without first changing the year.
In the case of doubles, you could have a similar problem, for example your proposed validation would prevent the user from entering the perfectly valid values "-1", ".12", "1e+5":
- - invalid
-1 - valid
. - invalid
.1 - valid
1 - valid
1e - invalid
1e+ - invalid
1e+5 - valid
I would recommend validating as normal when the user leaves the textbox or explicitly validates by clicking a button.

How to insist that a textbox has a value

I have a textbox on a form and a button on the same form. On the button click event I want to insist that the textbox has a value in the form of a Double. What I have so far is this -
public double getUnitStake(Form frontpage)
{
double doubleresult=0;
bool unitStake;
foreach (Control c in frontpage.Controls)
{
if (c.Name == "tbUnitStake")
{
unitStake = double.TryParse( (c as TextBox).Text, out doubleresult);
if (!unitStake)
{
}
else
{
doubleresult=double.Parse((c as TextBox).Text);
}
}
}
return doubleresult;
}
But for the life of me I can't figure out what to do if the double.tryparse method is false. What I want is the program execution to halt until a suitable value has been entered into the textbox. How can I achieve that please? Thanks for all and any help.
It seems to me you are validating the contents of a textbox. Therefore you can use the validating event of a TextBox Control. Optionally connect an errorProvider control to it to give special focus on the error (blinking exclamation mark). The "e.Cancel" statement will prevent you from doing anything else with your form until a double is entered.
private void textBox1_Validating(object sender, CancelEventArgs e)
{
double doubleresult = 0;
bool result = Double.TryParse(textBox1.Text, out doubleresult);
if (result)
{
errorProvider1.SetError(textBox1, string.Empty);
}
else
{
errorProvider1.SetError(textBox1, "Must be a Double");
e.Cancel = true;
}
}
I am not aware of the complete requirements of your program.
You could disable the button by default. In the text change event of the text box, check if the value entered is a double.
Once you have verified that the value is indeed a double, then you could enable the button.
This way the program execution is halted by default, unless a double is entered.
Your code is fine except the following change
public double getUnitStake(Form frontpage)
{
double doubleresult=0;
bool unitStake;
foreach (Control c in frontpage.Controls)
{
if (c.Name == "tbUnitStake")
{
unitStake = double.TryParse( (c as TextBox).Text, out doubleresult);
}
}
return doubleresult;
}
you need not to parse the "doubleresult" again as the tryparse has already done it. You can use unitStake variable for further use.
you just need TryParse yor value if your value not a double this method return you 0.0:
public double getUnitStake(Form frontpage)
{
double doubleresult=0;
foreach (Control c in frontpage.Controls)
{
if (c.Name == "tbUnitStake")
{
double.TryParse( (c as TextBox).Text, out doubleresult);
}
}
return doubleresult;
}

Categories

Resources