Why would I get an "Invalid Cast" with this code? - c#

I have a form which contains the following types of controls (only):
Button
ComboBox
Label
TextBox
I have a "Clear" button that calls this method:
private void ClearControls()
{
foreach (TextBox txtbx in this.Controls)
{
if (txtbx != null)
{
txtbx.Text = string.Empty;
}
}
foreach (ComboBox cmbx in this.Controls)
{
if (cmbx != null)
{
cmbx.SelectedIndex = -1;
}
}
}
...yet when I call it, the app hangs, and the log file says "Invalid cast" for that method. How could that be? It should deal with the TextBoxes and ComboBoxes, and disregard the rest - where could the invalid cast be?

That's not what foreach does.
Specifying a type in a foreach loop does not skip items of other types; instead, it will cast every item to that type.
You can call .OfType<T>() to get the filtered list that you're looking for.

The foreach will try to cast the control to the specified type which will give that invalid cast exception, what you should do is:
foreach(Control ctrl in this.Controls)
{
if(ctrl as TextBox != null)
{
//Textbox logic
}
if(ctrl as ComboBox!= null)
{
//ComboBox logic
}
}

Based on Gunther's starting point, this works:
foreach (Control ctrl in this.Controls)
{
if (ctrl as TextBox != null)
{
ctrl.Text = string.Empty;
}
if (ctrl as ComboBox != null)
{
((ComboBox)ctrl).SelectedIndex = -1;
}
}

Related

How can i create a method type bool and loop over controls to decide on cases?

private bool LoopOverControls(bool reset, bool checkIfEmpty)
{
bool results;
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
if (reset == true)
(ctrl as TextBox).Text = "";
if (checkIfEmpty == true)
results = true;
}
}
return results;
}
I want to use the method in some places in the code. Instead making loop over the controls over again each time i want to make a method i can call.
The method was before:
private void LoopOvercontrols(bool reset, bool checkIfEmpty)
{
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
if (reset == true)
(ctrl as TextBox).Text = "";
if (checkIfEmpty == true)
}
}
}
And this is the places i'm using the loop over the controls in my code the first place is in the constructor: I check if the textBoxes are not empty then do something in this case change btnReset enable true.
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
(ctrl as TextBox).TextChanged += on_TextChanged;
if ((ctrl as TextBox).Text != "")
{
btnReset.Enabled = true;
}
}
}
Then inside another event this time i check if the textboxes are empty and set the btnReset enable to false:
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
(ctrl as TextBox).TextChanged += on_TextChanged;
if ((ctrl as TextBox).Text == "")
{
btnReset.Enabled = false;
}
}
}
So far i'm looping over the textBoxes in two places but i might want to loop over them again later in other places. The problem is how to make the method LoopOverControls so i can decide with a bool and maybe other properties some cases and using buttons or other controls within the decitions ?
You can write a method that receive the action to be executed as parameter.
The work for this new method is to enumerate all the textboxes and call the action method for each one.
public void TextBoxEnumerateAndAction(Action<TextBox> execute)
{
// Get just the TextBoxes, no need of ugly casts...
foreach(TextBox t in this.Controls.OfType<TextBox>())
{
execute?.Invoke(t);
// This part is common to every textbox, so it can stay inside the
// enumeration loop....
btnReset.Enabled = !string.IsNullOrEmpty(t.Text)
}
}
Now define the Action methods to pass to TextBoxEnumerateAndAction
void AddTextChangedHandler(TextBox t)
{
t.TextChanged += on_TextChanged;
}
void RemoveTextChangedHandler(TextBox t)
{
t.TextChanged -= on_TextChanged;
}
So, everywhere you need to add or remove the TextChanged handler you could call
TextBoxEnumerateAndAction(AddTextChangedHandler);
Or if you have more fancy situations, you could simply define another action to pass to TextBoxEnumerateAndAction
I suggest you just add some comfort methods to contain the looping and type selection logic and allow callers to pass in delegate function to control the applied logic.
for example:
public void ActOn<TControl>(Action<TControl> applyFunction)
where TControl : Control
{
if (applyFunction == null) { throw new ArgumentNullException(nameof(applyFunction)); }
var controlsOfChosenType = this
.Controls
.OfType<TControl>();
foreach (var control in controlsOfChosenType)
{
applyFunction(control);
}
}
Then you can use it like this:
ActOn<TextBox>(textbox => textbox.Text = DateTime.Now.ToString());

Unable to cast object of Type1 to type Type2?

I am trying to validate a bunch of controls within a GroupBox in a form based application.
I cannot seem to map the ComboBox to the application for it to recognize and generate the error and it only does so for the TextBox.
private void groupBox1_Validating(object sender, CancelEventArgs e)
{
foreach (Control control in groupBox1.Controls)
{
string controlType = control.GetType().ToString();
var lst = new List<string>() { "System.Windows.Forms.TextBox" ,"System.Windows.Forms.ComboBox"};
//if (controlType == "System.Windows.Forms.TextBox")
if (lst.Contains(controlType, StringComparer.OrdinalIgnoreCase))
{
TextBox txtBox = (TextBox)control;
ComboBox combo = (ComboBox)control;
if (string.IsNullOrEmpty(txtBox.Text) && string.IsNullOrEmpty(combo.Text))
{
MessageBox.Show(txtBox.Name + " Can not be empty");
}
}
}
}
Here is the error I am receiving:
Unable to cast object of type 'System.Windows.Forms.ComboBox' to type 'System.Windows.Forms.TextBox'.
In your code, it cast any textbox and any combo into a textbox AND a combo.
You need to cast it to what it is only.
private void groupBox1_Validating(object sender, CancelEventArgs e)
{
foreach (Control control in groupBox1.Controls)
{
if (control is ComboBox)
{
ComboBox combo = (ComboBox)control;
if (string.IsNullOrEmpty(combo.Text)) MessageBox.Show(combo.Name + " Can not be empty");
}
else if (control is TextBox)
{
TextBox txtBox = (TextBox)control;
if (string.IsNullOrEmpty(txtBox.Text)) MessageBox.Show(txtBox.Name + " Can not be empty");
}
}
}
If the groupbox only have textbox and combobox you can also do:
private void groupBox1_Validating(object sender, CancelEventArgs e)
{
foreach (dynamic control in groupBox1.Controls)
{
if (string.IsNullOrEmpty(control.Text)) MessageBox.Show(control.Name + " Can not be empty");
}
}
Let's summarize what you want to do:
For each control inside groupBox1
... of type TextBox or ComboBox
Validate that the control is not empty, if it is, show a message box
Here's some important points:
Every control that inherits from Control has a public Text property, you don't really need to know if it is a textbox or a combobox for this part
No control is both a textbox and a combobox (that is, no control class inherits from both TextBox and ComboBox), so one of those casts will fail every time
You could use as instead of the hard cast, which would return null in the cast of a cast that cannot be done, but with point 1. above this is not necessary
So here is the rewritten code with the above knowledge:
private void groupBox1_Validating(object sender, CancelEventArgs e)
{
foreach (Control control in groupBox1.Controls)
{
if (!(control is TextBox || control is ComboBox))
continue;
if (!string.IsNullOrEmpty(control.Text))
continue;
MessageBox.Show(control.Name + " Can not be empty");
}
}
Note that if you want to do other things as well, which would need to know if the control is a textbox or a combobox, I would instead rewrite it as follows:
private void groupBox1_Validating(object sender, CancelEventArgs e)
{
foreach (Control control in groupBox1.Controls)
{
var textbox = control as TextBox;
if (textbox != null)
{
... do your processing of textboxes here
continue;
}
var combobox = control as ComboBox;
if (combobox != null)
{
... do your processing of comboboxes here
continue;
}
... do your processing of other controls here
}
}
Since you just want to check the Text and Name property and because the both TextBox and ComboBox inherits from Control class you don't need casting here. This should works for you:
foreach (Control control in groupBox1.Controls)
{
if (!lst.Contains(control.GetType().ToString(), StringComparer.OrdinalIgnoreCase)) continue;
if (string.IsNullOrEmpty(control.Text) && string.IsNullOrEmpty(control.Text))
{
MessageBox.Show(control.Name + " Can not be empty");
}
}
Or with Linq:
foreach (Control control in from Control control in groupBox1.Controls
where lst.Contains(control.GetType().ToString(), StringComparer.OrdinalIgnoreCase)
where string.IsNullOrEmpty(control.Text) && string.IsNullOrEmpty(control.Text)
select control)
{
MessageBox.Show(control.Name + " Can not be empty");
}
Use the is operator to check whether you have the right type:
if(control is TextBox)
{
TextBox txtBox = (TextBox)control;
// Do something with txtBox
if (string.IsNullOrEmpty(txtBox.Text))
{
MessageBox.Show(txtBox.Name + " Can not be empty");
}
}
if(control is ComboBox)
{
ComboBox combo = (ComboBox)control;
// Do something with combo
if (string.IsNullOrEmpty(combo.Text))
{
MessageBox.Show(combo.Name + " Can not be empty");
}
}
As #LasseV.Karlsen pointed out, I was taking the long route by individually adding in each control. I simply added control instead of something rather than specific controls.
Here is what my updated code looks like now:
foreach (Control control in groupBox1.Controls)
{
string controlType = control.GetType().ToString();
var lst = new List<string>() { "System.Windows.Forms.TextBox" ,"System.Windows.Forms.ComboBox"};
//if (controlType == "System.Windows.Forms.TextBox")
if (lst.Contains(controlType, StringComparer.OrdinalIgnoreCase))
{
// TextBox txtBox = (TextBox)control;
// ComboBox combo = (ComboBox)control;
if (string.IsNullOrEmpty(control.Text) && string.IsNullOrEmpty(control.Text))
{
MessageBox.Show(control.Name + " Can not be empty");
}
}

How to check multiple combo-box value is Empty or Not

I am having 40 combo-box in my win-form application, I want to check at end on button click that all the combo-box value has been entered i.e no combo-box value has been selected empty
i am trying below code using for each loop but cant find success
foreach (Control c in this.Controls)
{
if (c is ComboBox)
{
ComboBox textBox = c as ComboBox;
if (textBox.SelectedValue==string.Empty)
{
MessageBox.Show("please fill all fields");
}
}
}
so how to achieve this validation in simple lines of codes
Try to use linq and a recursion:
var isAnyEmpty = ScanForControls<ComboBox>(this)
.Where(x => x.SelectedIndex < 0)
.Any();
if (isAnyEmpty)
MessageBox.Show("please fill all fields");
And recursion search:
public IEnumerable<T> ScanForControls<T>(Control parent) where T : Control
{
if (parent is T)
yield return (T)parent;
foreach (Control child in parent.Controls)
{
foreach (var child2 in ScanForControls<T>(child))
yield return (T)child2;
}
}
To make sure that you check each & every ComboBox in your Form you will have to iterate over each control in you Form, Try this for that.
private void button1_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
if (c is ComboBox)
{
ComboBox textBox = c as ComboBox;
if (textBox.SelectedValue == null)
{
MessageBox.Show("please fill all fields");
break;
}
}
else
recursiveComboboxValidator(c);
}
}
void recursiveComboboxValidator(Control cntrl)
{
foreach (Control c in cntrl.Controls)
{
if (c is ComboBox)
{
ComboBox textBox = c as ComboBox;
if (textBox.SelectedValue == null)
{
MessageBox.Show("please fill all fields");
break;
}
}
else
recursiveComboboxValidator(c);
}
}

How do I clear all Textboxes, Comboboxes, and DateTimePickers on a WinForm in C#?

I'm working in VS 2012 using C# and WinForms for my application, and I'm curious what sort of routine I should use to clear all methods of entering data I have, which includes textboxes, comboboxes, and date time pickers. I've Googled and found a few 'answers', but none seem to work or actually prove helpful.
[EDIT]:
I kept researching and actually found a helpful method that I just had to add some ifs to to get what I wanted:
private void ResetFields()
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is TextBox)
{
TextBox tb = (TextBox)ctrl;
if (tb != null)
{
tb.Text = string.Empty;
}
}
else if (ctrl is ComboBox)
{
ComboBox dd = (ComboBox)ctrl;
if (dd != null)
{
dd.Text = string.Empty;
dd.SelectedIndex = -1;
}
}
else if (ctrl is DateTimePicker)
{
DateTimePicker dtp = (DateTimePicker)ctrl;
if (dtp != null)
{
dtp.Text = DateTime.Today.ToShortDateString();
}
}
}
}
Something like this:
void ClearThem(Control ctrl)
{
ctrl.Text = "";
foreach (Control childCtrl in ctrl.Controls) ClearThem(childCtrl);
}
And then:
ClearThem(this);
Another option:
Create a class deriving from Panel, with all of what you need on it, and Dock it in the Form. When you need to "refresh" - simply replace that Panel with a new instance of that Panel.
You could just loop in all the controls of the form and clear depending on the control type
We can clear all Textboxes, Comboboxes but not the DateTimePicker
If you want to clear the DateTimePicker you must set the properties:
Format = Custom, CustomFormat = " " and in time that you want to select a date in DateTimePicker
private void dateTimePicker1_CloseUp(object sender, EventArgs e)
{
dateTimePicker1.Format = DateTimePickerFormat.Short;
}
And this could be the solution:
public static void ClearAll(Control control)
{
foreach (Control c in control.Controls)
{
var texbox = c as TextBox;
var comboBox = c as ComboBox;
var dateTimePicker = c as DateTimePicker;
if (texbox != null)
texbox.Clear();
if (comboBox != null)
comboBox.SelectedIndex = -1;
if (dateTimePicker != null)
{
dateTimePicker.Format = DateTimePickerFormat.Short;
dateTimePicker.CustomFormat = " ";
}
if (c.HasChildren)
ClearAll(c);
}
}
Loop through your form controls, match them to your types and set it to "" or null;

Clear all controls in a ASP.NET page with a Master page

I have a asp.net page which is inherited from a master page .I want to clear all controls in this page .I tried using the bellow method .This is not working if a master page is there. Otherwise its working fine any ideas?
private void ClearControls()
{
foreach(Control c in Page.Controls)
{
foreach (Control ctrl in c.Controls)
{
if (ctrl is TextBox)
{
((TextBox)ctrl).Text = string.Empty;
}
}
}
}
try this:
public void FindAllTextBox(Control ctrl)
{
if (ctrl != null)
{
foreach (Control c in ctrl.Controls)
{
if (c is TextBox)
((TextBox)c).Text = string.empty;
FindAllTextBox(c);
}
}
}
Ex.:
Control ctrl = this.FindControl("content");
FindAllTextBox(ctrl);
You should be able to do this with Page.Form.FindControl("ContentPlaceHolder1").Controls:
foreach (Control item in Page.Form.FindControl("ContentPlaceHolder1").Controls)
{
if (item is TextBox)
{
((TextBox)item).Text = string.Empty;
}
}
This is probably because of your controls are inside of another container when you add a master page. Have you tried adding another foreach before if?
private void ClearControls()
{
foreach(Control container in Page.Controls)
{
foreach (Control c in container.Controls)
{
foreach (Control ctrl in c.Controls)
{
if (ctrl is TextBox)
{
((TextBox)ctrl).Text = string.Empty;
}
}
}
}
}
I wouldn't do it this way though. Sometimes hardcoding is better. This would use a lot of resource when called on a page that contains lots of controls.
Don't hard code:
//Recursively get all the formControls underneath the current one, be it Page, UserControl or whatever.
public static IEnumerable<Control> GetAllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in control.GetAllControls())
{
yield return descendant;
}
}
}
Then you can call it in your webform / control:
var formCtls = this.GetAllControls().OfType<TextBox>();
foreach(TextBox txtbx in formCtls)
{
//do what you gotta do ;)
}
First, use operator as instead of is and cast:
TextBox tb = ctrl as TextBox;
if (tb != null)
{
tb.Text = String.Empty;
}
Second, you can use ITextControl instead of TextBox.
And third, try next extension method:
public static IEnumerable<T> GetChildControls(this Control control) where T : Control
{
var children = (control.Controls != null) ? control.Controls.OfType<T>() : Enumerable.Empty<T>();
return children.SelectMany(c => GetChildControls(c)).Concat(children);
}
Usage:
foreach (var c in this.Page.Controls.GetChildControls<TextBox>())
{
c.Text = String.Empty;
}
I had the same problem but I think I was making it too hard. I'm using an AJAX UpdatePanel control and I just referenced that instead of going all the way up to the MasterPage. This worked for me.
foreach (Control c in UpdatePanel1.Controls)
{
foreach (Control c1 in c.Controls)
{
if (c1 is TextBox)
{
TextBox txtBox = (TextBox)c1;
txtBox.Text = "0";
}
}
}
Just keep the controls in Panel, and try the code below
foreach (Control cntrl in pnl.Controls)//pnl is panel id
{
if (cntrl is TextBox)
{
TextBox txtBox = (TextBox)cntrl;
txtBox.Text = " ";
}
}
Make a method on your .cs like this:
//Where "this" is Page.
ClearInput(this);
private void ClearInput(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.Controls.Count > 0)
ClearInput(c);
else
{
if (c is TextBox)
(c as TextBox).Text = "";
if (c is CheckBox)
(c as CheckBox).Checked = false;
if (c is DropDownList)
(c as DropDownList).SelectedIndex = 1;
}
}
}
private void EnableControls(Control control)
{
var textbox = control as TextBox;
if (textbox != null)
{
textbox.Enabled = true;
}
var dropDownList = control as DropDownList;
if (dropDownList != null)
{
dropDownList.Enabled = true;
}
var radioButton = control as RadioButton;
if (radioButton != null)
{
radioButton.Enabled = true;
}
var checkBox = control as CheckBox;
if (checkBox != null)
{
checkBox.Enabled = true;
}
foreach (Control childControl in control.Controls)
{
EnableControls(childControl);
}
}
public void getAllCtl(ControlCollection ctls)
{
foreach (Control c in ctls)
{
if (c is System.Web.UI.WebControls.TextBox)
{
//TextBox tt = c as TextBox;
////to do something by using textBox tt.
((TextBox)c).Text = string.Empty;
}
if (c is System.Web.UI.WebControls.CheckBox)
{
((CheckBox)c).Checked = false;
}
if (c is System.Web.UI.WebControls.DropDownList)
{
((DropDownList)c).SelectedIndex = -1;
}
if (c.HasControls())
{
getAllCtl(c.Controls);
}
}
}
calling in aspx.cs file as
getAllCtl(this.Form.Controls);
This is OK and tested work for all Master-child page and where ever multiple controls are contains in the page...

Categories

Resources