Overview of function.
I have this SaveDetails function within a WinForm, which iterates through all of the controls, to determine whether any errorProviders have been flagged in the form during the users input. If the function returns true,
all of my TextBoxes values need to be stored in my private fields, and display a messsagebox and close the form.
// For about 15 textBoxes, Could this be achieved also by looping? As this looks very cumbersome.
title = cmb_Title.Text;
If returns false and an errorProvider has been found in the iteration, it needs to display an error message for the user, clear private fields and give the user chance to re-enter details, however not clear textBoxes!!
Issue:
the loop iterates through everything, all of the controls regardless if it has found an errorProvider. How can I stop this to just flag when just one has been found? This function is also in a clickEvent.
Code
isValid = true;
foreach (Control c in panel1.Controls)
{
if (errorProvider1.GetError(c).Length > 0)
{
isValid = false;
MessageBox.Show("invalid entry, please revisit the form before proceding");
}
}
if (isValid)
{
title = cmb_Title.Text;
surName = txt_SurName.Text;
dateOfBirth = dateTimePicker1.Text.ToString();
MessageBox.Show("Indi Form Saved");
Hide();
}
you can shorten it using only TextBox controls and Linq.
Something like this:
List<TextBox> textBoxes = panel1.Controls.OfType<TextBox>().ToList();
if (textBoxes.Any(tb => !string.IsNullOrEmpty(errorProvider1.GetError(tb))))
MessageBox.Show("invalid entry, please revisit the form before proceding");
If you don't want to check only TextBox controls but all controls in panel1, you can still use Linq to simplify your code.
var controlsList = panel1.Controls.Cast<Control>().ToList();
if (controlsList.Any(tb => !string.IsNullOrEmpty(errorProvider1.GetError(tb))))
MessageBox.Show("invalid entry, please revisit the form before proceding");
Related
In my Winforms C# application, I have fields with Int data type and they are set to accept null values in SQL Server database (allow nulls).
In the forms I have some textboxes which are bound to those int data type fields. If I don't enter anything while creating a new record, it accepts. If I enter a number in the textbox, it also accepts it, and then if I delete it, it doesn’t accept it anymore and even doesn't allow me to move to the next field.
If I set its value as null or "" through code, it simply ignores and does not even update changes which I made in other non int text fields.
I am using following method to update.
this.Validate();
this.itemsbindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.sBSDBDataSet);
What can I do for the textbox to accept null values?
I have tried following.
IDTextBox.Text = "";
IDTextBox.Text = null;
I have tried following with the help of above solutions (specially Mr. Ivan) and this is how it worked out.
To clear the int field on the form:
IDTextBox.Text = String.Empty;
Then on Designer.cs file of the form, as suggested by Mr. Ivan, I searched for 'IDtextbox.DataBindings.Add' and replaced
this.IDTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.itemsbindingSource, "PictureID", true));
with
this.IDTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.itemsbindingSource, "PictureID", true, System.Windows.Forms.DataSourceUpdateMode.OnValidation, ""));
It took me a whole day to search and finally I posted my problem here, and it got solved in 1 hour.
This seems to be one of the WF data binding bugs. I can't say what exactly is causing it, but in order to make it work one should set Binding.NullValue property to "" (empty string, the default is null).
I couldn't find a way to do that in the designer, and also it would be quite annoying to locate all text boxes needed. So I would suggest you the following quick-and-dirty approach. Create a helper method like this:
public static class ControlExtensions
{
public static void FixTextBoxBindings(this Control control)
{
if (control is TextBox)
{
foreach (Binding binding in control.DataBindings)
if (binding.NullValue == null) binding.NullValue = "";
}
foreach (Control child in control.Controls)
child.FixTextBoxBindings();
}
}
and then simply include the following in your form Load event:
this.FixTextBoxBindings();
TextBox dont accept null value.
You can check if it null and set String.Empty;
If(dbValue == null)
{
IDTextBox.Text = String.Empty;
}
else
{
// here set value to your textbox
}
I have a series of 5 text boxes on a winforms form which are used to collect data as strings. I intend to use these boxes in multiple ways from a method to clear the contents of each, to a method which takes the data from each to check before exporting to a text file.
In order to do this I planned to create a List<Textbox> which would essentially be a list of all 5 boxes so I could later use code such as
foreach(Texbox box in *texboxList*)
{
box.Clear()
}
etc.
My only idea so far is to create a seperate method which adds all of the boxes to a list and then somehow pass the result of the method as a parameter to the relevant methods such as those that clear the boxes. The code I currently have is displayed below.
public List<TextBox> Clear_entered_data()
{
List<TextBox> textBoxes = new List<TextBox>();
textBoxes.Add(tbox1);
textBoxes.Add(tbox2);
textBoxes.Add(tbox3);
textBoxes.Add(tbox4);
textBoxes.Add(tbox5);
return textBoxes;
}
This is the code I'm using to generate the list of textboxes to use. I think the problem I'm having is understanding how this can be passed to other methods through parameters. The method I'd like to make use of the list is shown below here as I have it so far.
private void Clear_button_Click(object sender, EventArgs e, List<TextBox> textBoxes)
{
DialogResult Clear_validation = MessageBox.Show("Are you sure you would like to clear all data from the form?","Clear data?", MessageBoxButtons.YesNo);
if(Clear_validation == DialogResult.Yes)
{
foreach (TextBox box in textBoxes)
{
box.Clear();
}
}
}
With the code above I get the error upon running :
'Error 1 No overload for 'Clear_button_Click' matches delegate 'System.EventHandler''
But I've had no luck, please try and explain whats going wrong and help me find more appropriate solution!
Thanks
create a private class member that of list of textboxes List<TextBox> _textBoxes;
then after you call InitializeComponents(because before that your textboxex doesn't exists if you used the IDE to create them) place your code to add the textboxes into the list
_textBoxes = new List<TextBox>();
_textBoxes.Add(tbox1);
_textBoxes.Add(tbox2);
_textBoxes.Add(tbox3);
_textBoxes.Add(tbox4);
_textBoxes.Add(tbox5);
now you can use in your methods the _textBoxes that contains all your created TextBox
your public List Clear_entered_data() became
public List<TextBox> ClearTextBoxes() {
foreach(var textBox in _textBoxes){
textBox.Clear();
}
}
If you used a naming convention, you could just iterate over the form's control collection like this - in my example, the names all start with "SpecialTextBox":
foreach(Control c in someFormReference.Controls)
{
if (c.GetType() == typeof(TextBox) && c.Name.StartsWith("SpecialTextBox"))
{
// do your thing here
}
}
I have a windows form that just exists to take input from user, for all intents and purposes it is just a label and a corresponding input box (textbox, checkbox, masket textbox etc).
I have programatically placed all the input fields in a TabIndex order that is optimal for cycling through them in a way that fits with their locations (tab down each column of inputs, then to the top of the next column).
The person that I am building this project for has stipulated that only like each textbox to come available one at a time, as the previous one has been filled out. This is a crude way of doing it with no validation, but essentially something like this...
if (String.IsNullOrEmpty(textbox1.Text))
{
textbox2.Enabled = true
}
So this is fine to do with two textboxes in this example, but the form has 28 different inputs on it, so an absurd series of if statements will only be a last resort.
My thoughts has been to put all the inputs in a List, ideally in the same order as is their TabIndexes. I tried to do this using a foreach loop...
List<Control> inputsList = new List<Control>();
public void initialiseControls()
{
//control position to insert control into list at specified index
int cntrlpos = 0;
//for every control in form
foreach (Control cntrl in this.Controls)
{
//not counting labels (not input fields)
if (!(cntrl is Label))
{
//set list position to equal control's TabIndex
cntrlpos = cntrl.TabIndex;
//insert the control into the list at the position reflecting TabIndex
inputsList.Insert(cntrlpos, cntrl); //<---- Error Occurs
//ASK TEXTBOX TO OUTPUT LIST POSITION AS TEST
//foreach (var txtbx in this.Controls.OfType<TextBox>())
//{
// txtbx.Text = Convert.ToString(cntrlpos);
//}
}
}
As soon as the function is called, an exception is thrown stating that "Index must be within the bounds of the list".
When I put a breakpoint into the code, it showed cntrlpos to equal 29, which is more than the 28 total input controls there are on the form.
I don't know where to go from here, if anyone can offer some advice on the code above to place the Controls into the list in the correct order (or point me in the direction of another method to do something like this), then I would really appreciate it.
Thanks,
Mark
To make your list, try this:
List<Control> inputList =
(from Control c in getAllControls(this)
where c.TabStop
orderby c.TabIndex
select c).ToList();
Define the method getAllControls elsewhere in your form class:
IEnumerable<Control> getAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in getAllControls(control))
yield return descendant;
}
}
(Taken and modified slightly from Recursive control search with Linq)
This will make it so that you get even nested controls (such as those in panels or groupboxes).
You can't just use the TabIndex as an index into your list, because even stuff like labels have tab indices, which will mess up your indices.
I think you've over complicated it...
Just use Control.GetNextControl:
Retrieves the next control forward or back in the tab order of child
controls.
For example, with just TextBoxes:
private void textBoxes_TextChanged(object sender, EventArgs e)
{
Control ctl = (Control)sender;
if (!String.IsNullOrEmpty(ctl.Text))
{
Control next = this.GetNextControl(ctl, true);
if (next != null)
{
next.Enabled = true;
}
}
}
Obviously you might need a slightly more complicated check for some other types of controls in a different handler, but you'd still just grab the next control to enable using GetNextControl().
I am having trouble with creating a form that requires the user to enter information in the fields, Confirm an email and password entry, and go onto the next form when all those fields are matched/filled in. Individually, the code I have works, but I cannot seem to find a way to make it so that all the requirements are met before going onto the next form. At the moment it just goes onto the next form if i click the continue button.
some excerpts of the code i have are:
if (string.IsNullOrEmpty(email))
{
lblRequirementsError.Text = ("All required fields have not been filled.");
}
if (txtBoxEmail.Text != txtBoxConfirmEmail.Text)
{
lblEmailError.Text = ("Email reentry does not match. Please reenter.");
}
if (txtBoxPassword.Text != txtBoxConfirmPassword.Text)
{
lblPasswordError.Text = ("Password reentry does not match. Please reenter.");
}
this.Hide();
frmBilling secondForm = new frmBilling();
secondForm.Show();
The problem is the form is created and opened regardless of the if results, because the code for it is outside the ifs. First, check that no fields are empty, and then, check that the validation has been met, THEN open the new window. Something like this should work:
//If both email and password are not empty
if (!string.IsNullOrEmpty(email) && !string.IsNullOrEmpty(password))
{
//if both email and password math the re entry
if (txtBoxEmail.Text == txtBoxConfirmEmail.Text &&
txtBoxPassword.Text == txtBoxConfirmPassword.Text)
{
//execute the code to open the new form
this.Hide();
frmBilling secondForm = new frmBilling();
secondForm.Show();
}
}
if (! txtBoxEmail.Text.Equals( txtBoxConfirmEmail.Text))
{
lblEmailError.Text = ("Email reentry does not match. Please reenter.");
}
if (! txtBoxPassword.Text.Equals( txtBoxConfirmPassword.Text))
{
lblPasswordError.Text = ("Password reentry does not match. Please reenter.");
}
are you using web applications forms in visual studio 2012. You can use field validators inside the .ASPX file for any field that you want to validate before form submission. This is much easier that writing everything in C#.
You can use the DataAnnotation if you are binding or converting your controls to the data objects. Then it will be easy to validate. Please see the link for more details
http://msdn.microsoft.com/en-us/library/dd901590(VS.95).aspx
Try this:
bool validationStatus = default(bool);
if (string.IsNullOrEmpty(email))
{
lblRequirementsError.Text = ("All required fields have not been filled.");
validationStatus = true;
}
if (txtBoxEmail.Text != txtBoxConfirmEmail.Text)
{
lblEmailError.Text = ("Email reentry does not match. Please reenter.");
validationStatus = true;
}
if (txtBoxPassword.Text != txtBoxConfirmPassword.Text)
{
lblPasswordError.Text = ("Password reentry does not match. Please reenter.");
validationStatus = true;
}
if(!validationStatus)
{
Hide();
frmBilling secondForm = new frmBilling();
secondForm.Show();
}
I've got a form with multiple text boxes which are file paths for the program to import data from. Currently they are checked for non-zero length by the following:
//this code imports the files required by the user, as specified in the
//file path text boxes
private void btImport_Click(object sender, EventArgs e)
{
bool hasPath = false;
foreach (TextBox box in this.gbPaths.Controls.OfType<TextBox>().Where(tb => tb.Text.Length > 0))
{
hasPath = true;
//import code
}//end foreach
if (!hasPath)
{
MessageBox.Show("You must enter at least one file path.");
}//end if
}//end import code
What I'm wondering is can I replace the //import code part with something like:
if(tb.Name = "txtAvF") then...
or similar, or do I have to do it outside of the foreach loop? Thanks in advance. Let me know if I need to clarify anything.
If you want to check to see if the TextBox is one of the ones on the form (which I think you are), then you are == which (taken from MSDN)
the operator == tests for reference equality by determining if two references indicate the same object
So this is what you're looking for:
if(box == textBox1 && !string.IsNullOrEmpty(box.Text))
{
// Import Textbox1
}
else if(box == textBox2 && !string.IsNullOrEmpty(box.Text))
{
// Import Textbox2
}
else if (box == textBox3....)
You should do it inside of the loop. Like this:
if (box.Name == "txtAvF")
box.Text = "What you want";
But setting hasPath inside the loop just holds state for your last path. You should also move MessageBox code inside loop.
The hasPath assignment seems correct to me; it's set for any one text box, and if not set at the end of the loop, a message is displayed. This rhymes well with the text so displayed. Moving the MessageBox call into the loop would cause the dialog box to never be displayed (or errenously displayed), at least as the code is implemented now, since the OfType<>().Where() guarantees that all text boxes iterated over have at least some content.
(I would add this as a comment to #Xaqron but don't have the necessary reputation yet.)