How can we change the ReadOnly property of all textBoxes in a winform that is true to false i'm trying using this code but this prompt me object null reference error...
private void TextBoxesReadOnlyTrue(Control.ControlCollection cc)
{
foreach (Control ctrl in cc)
{
TextBox tb = ctrl as TextBox;
if (tb.ReadOnly)
{
tb.ReadOnly = false;
}
}
}
That's because not all the controls in cc are TextBoxes. So when you try converting them to a TextBox, the variable is null. When a variable is null, you cannot access any properties on that variable, or you'll get an error. So anytime a variable can be null, you MUST first test whether it is null.
Here's the modified if command that you'll want to use to fix your problem:
if (tb != null && tb.ReadOnly) { tb.ReadOnly = false; }
So i appologize that i overlooked that your TextBoxes can be contained in other container controls. Yes, that means you need to do 1 of 2 things: 1: You can move the TextBoxes outside the GroupBox. haha. I'm just joking. Yes, that can solve that problem but then you have worse problems. The correct way is to recursively call your method for every control that has controls in its Controls property. Every control has this property but it seems it is empty (but not null) in controls that are not containers. (I just learned today that every control has this Controls property, so i've updated my code to reflect this.)
So for this real solution, i suggest something similar to this:
private void TextBoxesReadOnlyTrue(Control.ControlCollection cc)
{
foreach (Control ctrl in cc)
{
TextBox tb = ctrl as TextBox;
if (tb != null && tb.ReadOnly)
{ tb.ReadOnly = false; continue; }
if (ctrl.Controls != null && ctrl.Controls.Count > 0)
{ TextBoxesReadOnlyTrue(ctrl.Controls); }
// this recursively calls this same method for every control ...
// that is a container control that contains more controls, ...
// such as GroupBoxes, Panels, etc.
}
}
first you would like to use a function like this:
Recursive get controls
then you do the following
private IEnumerable<T> GetControls<T>(Control.ControlCollection ctrls)
{
foreach (object ctrl in ctrls)
{
foreach (var item in GetControls<T>(((Control)ctrl).Controls))
{
yield return item;
}
if (ctrl is T)
yield return (T)ctrl;
}
}
foreach(var txtbox in GetControls<TextBox>(form.Controls)
{
txtbox.ReadOnly = false;
}
Related
I wrote this method to disable the autosize property of all labels whose name doesn't contain the string "Label".
private void DisableAutoSize()
{
foreach (Control control in Controls)
{
if (!control.Name.Contains("Label"))
{
(control as Label).AutoSize = false;
}
}
}
The line below is what causes the problem:
(control as Label).AutoSize = false;
The error I get says:
System.NullReferenceException: 'Object reference not set to an instance of an object.' (... as System.Windows.Forms.Label) returned null.
How can I access that AutoSize property?
filter Controls by type and apply search condition using LINQ methods OfType() and Where():
private void DisableAutoSize()
{
foreach (Label control in Controls.OfType<Label>().Where(c => !c.Name.Contains("Label")))
{
control.AutoSize = false;
}
}
You are looping through all of the Controls in the form, at least one of which isn't a Label. Thus, the cast fails. In your loop, check for this condition:
private void DisableAutoSize()
{
foreach (Control control in Controls)
{
Label label = control as Label;
if (label == null || control.Name.Contains("Label"))
{
continue;
}
label.AutoSize = false;
}
}
The issue with your code is that Controls could contain non-Label controls as well. If you were cast a non-Label Control as Control (control as Label), it would return null. This is the reason you end up with NullReferenceException.
You can Filter Label Controls using Enumerable.OfType<T> method. For example,
foreach (Label control in Controls.OfType<Label>())
{
if (!control.Name.Contains("Label"))
{
control.AutoSize = false;
}
}
You can further expand the Linq to filter Labels based on the Name property using Enumerable.Where<T>, thus removing the if condition.
foreach (Label control in Controls.OfType<Label>().Where(x=>!x.Name.Contains("Label")))
{
control.AutoSize = false;
}
My current function only finds first Textbox which is not totally correct. Control class doesn't have IsEditable property.
private static Control FindFocusableControl(Control CurrentControl)
{
if (CurrentControl.Visible)
{
if (CurrentControl is TextBox)
{
return CurrentControl;
}
if (CurrentControl.HasControls())
{
foreach (Control CurrentChildControl in CurrentControl.Controls)
{
Control focusableControl = FindFocusableControl(CurrentChildControl);
if (focusableControl != null)
{
return focusableControl;
}
}
}
}
return null;
}
On the first recursion for the first child control, the child control is probably not visible so the routine exits.
if (CurrentControl.Visible)
You might need a different kind of check at this point, perhaps check for textbox first, then if visible.
I've found a few answers around that work fine with modifying .Text, .Checked values and so, but none of them worked when I tried changing the .Value property. I can't get that to work on progress bars.
Last I tried:
foreach (Control c in this.Controls)
{
if (c.Name == "test" && c is ProgressBar)
{
((ProgressBar)c).Value = 23;
}
}
Am I missing a using statement or something?
Assuming that your progressbar control is named "test" (all lowercase letters) and is placed directly on the surface of your form (not inside a groupbox,panel or other control container) then this code should work and simplify your work
foreach (var c in this.Controls.OfType<ProgressBar>().Where(x => x.Name == "test")
{
c.Value = 23;
}
instead if the ProgressBar is placed inside a control container (like a panel) the above code should be changed to loop over the controls collection of the container
foreach (var c in this.panel1.Controls.OfType<ProgressBar>().Where(x => x.Name == "test")
{
c.Value = 23;
}
As pointed out in the comment by KingKing, if you are absolutely sure that a control named "test" exists in your groupbox then a simple lookup in the controls collection should result in your progressbar. Looping is not necessary in this case
ProgressBar pb = this.groupBox1.Controls["test"] as ProgressBar;
if(pb != null) pb.Value = 23;
The trick here is that Controls is not a List<> or IEnumerable but a ControlCollection.
I recommend using an extension of Control. Add this class to your project:
public static class ControlExtensionMethods
{
public static IEnumerable<Control> All(this System.Windows.Forms.Control.ControlCollection controls)
{
foreach (Control control in controls)
{
foreach (Control grandChild in control.Controls.All())
yield return grandChild;
yield return control;
}
}
}
Then you can do :
foreach(var textbox in this.Controls.All())
{
// Apply logic to a control
}
Source: Click
I've used this code before in another program, but now I'm having trouble understanding why it won't run the code after my second line.
foreach (Control c in Controls)
if (c.GetType() == typeof(TextBox)) //doesn't run any further
{
if ((string)c.Tag == "Filled")
{
...
}
...
}
I'm either missing some minor little detail or something else is incorrect. Any ideas?
EDIT: my textboxes are inside a panel.
It might be simpler to do this:
foreach ( TextBox tb in this.Controls.OfType<TextBox>())
{
if ((string)tb.Tag == "Filled")
// .....
}
When you call Control.Controls, it will only return the controls at the outermost level. It won't recursively descend into any container controls that hold other controls.
If your controls are in another container, you will need to use that container's .Controls property instead.
Alternatively you can generalize it by writing a method to recursively return all the controls from the parent and all it's children, like so:
public IEnumerable<Control> AllControls(Control container)
{
foreach (Control control in container.Controls)
{
yield return control;
foreach (var innerControl in AllControls(control))
yield return innerControl;
}
}
You can then use that instead of Control.Controls as follows:
private void test() // Assuming this is a member of a Form other class derived from Control
{
var textboxesWithFilledTag =
AllControls(this).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
As the comment says, I'm assuming that the test() method is a member of your Form or another class derived from Control. If it isn't, you will have to pass the parent control to it:
private void test(Control container)
{
var textboxesWithFilledTag =
AllControls(container).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
The following method has identical results to the one above, for reference (and is more readable IMHO):
private void test(Control container)
{
foreach (var textbox in AllControls(container).OfType<TextBox>())
if ((string)textbox.Tag == "Filled")
Debug.WriteLine(textbox.Text);
}
For your code, your button click handler might look something like this:
void button1_Click(object sender, EventArgs e)
{
foreach (var c in AllControls(this).OfType<TextBox>())
{
if ((string) c.Tag == "Filled")
{
// Here is where you put your code to do something with Textbox 'c'
}
}
}
Note that you also need the AllControls() method, of course.
To get all controls (not only the direct children of the form) you can use this recursive Linq
Func<Control, IEnumerable<Control>> allControls = null;
allControls = c => new Control[] { c }
.Concat(c.Controls.Cast<Control>()
.SelectMany(x=>allControls(x)));
Now you can filter the TextBoxes
var tbs = allControls(this).OfType<TextBox>()
.Where(t=>(string)t.Tag=="Filled")
.ToList();
Better use if (c is TextBox).
Furthermore, if you want to know why your code breaks, use try/catch
I'd recommend to use following syntax:
foreach (Control c in Controls)
if (c is TextBox)
Are you setting tag property from yourself. This is a string type of property.so you can try this:
if (c.Tag == "Filled")
{
Console.WriteLine(c.Name);
}
if you want to check that text box is not empty then you can simply try this :
if (c.Text.Trim().Length == 0)
{
Console.WriteLine(c.Name);
}
I have created a UserControl called AutorControl with a method to clear its textbox:
public void LimpiarAutorTextbox()
{
textBox1.Text = "";
}
Then my intention is from another form with a Panal, using a for loop add X ammount of the above user control. Then I want to call the UserControls method: "LimpiarAutorTextbox" (which is just a method for clearing the text of the textbox) using a foreach loop like this, however it's not working. I'm not sure what to do in this case:
AutorControl usercontrolAutorControl = new AutorControl();
private override void ClearControls()
{
txtTitulo.Text = "";
//Panel1 will only hold controls of the same type: "AutorControl"
foreach (Control X in panel1.Controls)
{
X as AutorControl;//?????? I want to access each created usercontrols' method.
}
}
The panel will always hold a usercontrol of AutorControl, never anything else. How can I achieve this programatically?
Thanks.
Your line here is fine:
X as AutorControl
just add:
(X as AutorControl).LimpiarAutorTextbox()
that should do the trick.
Also, I know you said that there would only be AutorControls in there, but you may want to do something more like this:
AutorControl currentControl == X as AutorControl;
if (AutorControl != null)
{
currentControl.LimpiarAutorTextbox();
}
Or alternatively, you can change your declaration of the for foreach loop to do the cast for you:
foreach(AutorControl currentControl in form.Controls)
{
if (currentControl != null)
{
currentControl.LimpiarAutorTextbox();
}
}
Some alternatives :)