My control "MyTextBox1" add dynamically on form1 under container1 control. This form1 can be child of form2 and form2 can be child of form3 and so on how can I find my control from multi controls collection?
e.g. MyTextBox1 exists in
form3.form2.form1.Container1.MyTextBox1
how to find my control by name from multi control collections?
I do not want to use recursive foreach control collection. I am looking for an smart/short code like controls.Find().
If you don't want to put it recoursive, you can try BFS (Breadth First Search); let's implement it as an extension method:
public static class ControlExtensions {
public static IEnumerable<Control> RecoursiveControls(this Control parent) {
if (null == parent)
throw new ArgumentNullException(nameof(parent));
Queue<Control> agenda = new Queue<Control>(parent.Controls.OfType<Control>());
while (agenda.Any()) {
yield return agenda.Peek();
foreach (var item in agenda.Dequeue().Controls.OfType<Control>())
agenda.Enqueue(item);
}
}
}
Then you can use it as
// Let's find Button "MyButton1" somewhere on MyForm
// (not necessary directly, but may be on some container)
Button myBytton = MyForm
.RecoursiveControls()
.OfType<Button>()
.FirstOrDefault(btn => btn.Name == "MyButton1");
Related
I want to enable/disable controls in a Windows Forms application according to the user privileges.
Initially I thought of writing a method in each form class that would check the user credentials and then enable/disable its controls. But then I realized I could (maybe) create a static class method which would take the form as a parameter and do the job.
So I started writing it, presuming that sometimes I would like to enable the controls of just one or two panels, instead of the whole form. So, I need the parameters to be:
a varying number of panels and/or
a form class.
My difficulties with this task is that I'm getting an error trying to make the panels argument varying, and I have no idea how to set a parameter that could take any form class. All my form classes obviously inherits from Form generic class, but I don't know how to apply this.
Here's what I got:
public static void Enable(TableLayoutPanel[] containers = null)
{
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (TableLayoutPanel table in containers)
{
foreach (Control control in table.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
If we remember that the Form class derives from Control (indirectly, by deriving from ContainerControl which derives from ScrollableControl, which derives from Control), and the Enabled property belongs to the Control class, we can write a method that will enable any control's children (including the Form or TableLayoutPanel controls), since the Controls collection also belongs to the Control class:
public static void EnableChildren(Control control, bool enabled = true)
{
foreach (Control child in control.Controls)
{
child.Enabled = enabled;
}
}
And then if we also want to be able to use this with a collection of controls (as in your example), we can write an overload that takes a collection:
public static void EnableChildren(IEnumerable<Control> controls = null,
bool enabled = true)
{
if (controls == null) return;
foreach (var control in controls)
{
EnableChildren(control, enabled);
}
}
Now we can use this with a Form or a collection of TableLayoutPanel controls (or any control that has controls in it's Controls collection).
Examples of usage:
var myForm = new Form1();
EnableChildren(this); // 'this' is the current form
EnableChildren(myForm); // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false); // A single TableLayoutPanel control
var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels); // An array of tableLayoutPanel controls
One of the simple ways I can think about what you are trying to do, is this. Let me get away for a sec here. I worked on projects where all form controls were built from Metadata. And meta came with licensing info. So, when control was placed where it should, it also was disabled or set read-only based on Metadata but the whole feature would be hidden if licensing info was restricting access to it. Coming back to your approach, this is not a bad approach and I see that this is can be done. And it can be done in 2 ways, (quickly from my head).
Use user controls as surface for the components you want to enable/disable. Create an interface
public interface IDisableableControl // make your fine name, no methods needed - marker interface
. . . . .
public class MyFineUserControl : UserControl, IDisableableControl
And in your static method that you're going to write pass the form, and find all controls that implement this interface and work them the way you want.
2.
Similarly, you can use property Tag, which is available on each control. With that, you can actually set your complex security object that can come from DB-stored metadata and then you evaluate this object stored in Tag to apply your configuration
Your method needs to be recursive
internal static void SetAllControls(Control parent)
{
// Do something with control, for example parent.Enabled = false
if (parent is IDisableableControl)
{
// here you use your logic, evaluate your parent you're dialing with and
// enable/disable correspondingly
parent.Enabled = false;
return;
}
foreach(var c in parent.Controls)
SetAllControls(c);
}
In real life, your TOP parent will be a form and will not need to be disabled, but it's certain children will. In fact, most of the time, once you found a UserControl which implements IDisableableControl that should be end of line, means, you don't need to go into children controls as they all sit on this parent and all will be disabled
I manage to accomplish what I was trying to do with the code below, which is pretty much a blend of all the helpful answers I got:
public static void EnableContainer(params Control[] containers)
{
if(containers.Count() == 0) { return; }
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (var container in containers)
{
foreach (Control control in container.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
public static void EnableForm<form>(form f) where form : Form
{
if (MyOF.isEnabled)
{
return;
}
else
{
foreach(Control control in f.Controls)
{
control.Enabled = false;
}
}
}
The community is welcome to suggest improvements as I am far from being a professional programmer. Thanks everyone once again.
I have tons of Buttons named like this x0y1
How do I access the variable name dynamically so I could loop all names by xiy1 or so.
in PHP it would be like ${"myString" . $randomvar}
I can't use a list or array because the the button already exist defined through the xaml
You can use:
var textbox =
this.Controls.OfType<TextBox>().Where(txb => txb.Name == "myString").FirstOrDefault();
This assumes you are in the context of your form (this.Controls).
And of course, don't forget to add using System.Linq;...
You can get all the textbox using this method
void AllTextBox(System.Windows.Forms.Control.ControlCollection ctrls)
{
foreach (Control ctrl in ctrls)
{
if (ctrl is TextBox)
{
if (ctrl.Name == "textBox1")
{
// do your stuf with textbox
}
}
}
}
You can create function that return control by name :
Control GetControlByName(string Name)
{
foreach(Control control in this.Controls)
if(c.Name == Name) return control ;
return null;
}
or Function with a specific control like that :
Button GetButtonByName(string Name)
{
foreach (Control c in this.Controls.OfType<Button>())
if (c.Name == Name) return c;
return null;
}
For wpf project...
Let's say you have a grid named MyGrid and there's lot of buttons on it.
You want to refer to the button named x0y1:
var btn = MyGrid.Children.OfType<Button>().Where(x=>x.Name=="x0y1");
Note: above code should work for flat structure (one level deep only).
You can achieve the same by using code provided in this thread: How can I find WPF controls by name or type?
Just call FindName("elementName"). FindName searches through all child elements of a FrameworkElement. To access any button by its name as string in a window, call the FindName() method of the window !
If your code is in a class inheriting from Window, just use:
Button button = (Button)FindName("xiy1");
If you write the code in a class not inheriting from Window but FrameworkElement, which is unlikely, use:
Window window = Window.GetWindow(this);
Button button = (Button)window.FindName("xiy1");
Check the MSDN documentation about Namescopes for more information about limitations.
I have a form in which there are some buttons. I'd like put their references in an array.Is it possible with a foreach ?
I want to do this:
public Form1()
{
InitializeComponent();
Button[] all = new Button[5];
all[0] = button1;
all[1] = button2;
all[3] = button3;
all[4] = button4;
}
I've already tried
int i=0;
foreach (Button p in Form1)
{
all[i]= p;
i++;
}
But I can't use a foreach on a Form.
The same thing if the buttons are in a panel.
What can I do to collect all buttons quickly?
Thanks :)
You're looking for the Controls collection of your form or container, which contains every control directly in it.
Beware that this will also include non-Buttons; call .OfType<Button>() to filter.
So instead of the foreach you can initialize an array like this:
Button[] all = this.Controls.OfType<Button>().ToArray();
Every Control has a Controls property which is a ControlCollection. You can get all Buttons on a Control (as a Form or a Panel) like this:
foreach(var button in control.Controls.OfType<Button>())
{ ... }
But this will only give you the Buttons that are contained directly by this control. If you want to get all Buttons in your Form on all Panels, GroupBoxs etc, you need to recurse through the Controlslike in this example:
public class Form1 : Form
{
// ...
private static IEnumerable<Button> GetAllButtons(Control control)
{
return control.Controls.OfType<Button>().Concat(control.Controls.OfType<Control>().SelectMany(GetAllButtons));
}
private void DoSomethingWithAllButtons()
{
foreach(var button in GetAllButtons(this))
{ // do something with button }
}
}
I'd like to add an event to all TextBoxes on my Form:
foreach (Control C in this.Controls)
{
if (C.GetType() == typeof(System.Windows.Forms.TextBox))
{
C.TextChanged += new EventHandler(C_TextChanged);
}
}
The problem is that they are stored in several GroupBoxes and my loop doesn't see them. I could loop through controls of each GroupBox individually but is it possible to do it all in a simple way in one loop?
The Controls collection of Forms and container controls contains only the immediate children. In order to get all the controls, you need to traverse the controls tree and to apply this operation recursively
private void AddTextChangedHandler(Control parent)
{
foreach (Control c in parent.Controls)
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += new EventHandler(C_TextChanged);
} else {
AddTextChangedHandler(c);
}
}
}
Note: The form derives (indirectly) from Control as well and all controls have a Controls collection. So you can call the method like this in your form:
AddTextChangedHandler(this);
A more general solution would be to create an extension method that applies an action recursively to all controls. In a static class (e.g. WinFormsExtensions) add this method:
public static void ForAllControls(this Control parent, Action<Control> action)
{
foreach (Control c in parent.Controls) {
action(c);
ForAllControls(c, action);
}
}
The static classes namespace must be "visible", i.e., add an appropriate using declaration if it is in another namespace.
Then you can call it like this, where this is the form; you can also replace this by a form or control variable whose nested controls have to be affected:
this.ForAllControls(c =>
{
if (c.GetType() == typeof(TextBox)) {
c.TextChanged += C_TextChanged;
}
});
A few simple, general purpose tools make this problem very straightforward. We can create a simple method that will traverse an entire control's tree, returning a sequence of all of it's children, all of their children, and so on, covering all controls, not just to a fixed depth. We could use recursion, but by avoiding recursion it will perform better.
public static IEnumerable<Control> GetAllChildren(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
Using this we can get all of the children, filter out those of the type we need, and then attach the handler very easily:
foreach(var textbox in GetAllChildren().OfType<Textbox>())
textbox.TextChanged += C_TextChanged;
Try this
AllSubControls(this).OfType<TextBox>().ToList()
.ForEach(o => o.TextChanged += C_TextChanged);
where AllSubControls is
private static IEnumerable<Control> AllSubControls(Control control)
=> Enumerable.Repeat(control, 1)
.Union(control.Controls.OfType<Control>()
.SelectMany(AllSubControls)
);
LINQ is great!
Haven't seen anyone using linq and/or yield so here goes:
public static class UtilitiesX {
public static IEnumerable<Control> GetEntireControlsTree(this Control rootControl)
{
yield return rootControl;
foreach (var childControl in rootControl.Controls.Cast<Control>().SelectMany(x => x.GetEntireControlsTree()))
{
yield return childControl;
}
}
public static void ForEach<T>(this IEnumerable<T> en, Action<T> action)
{
foreach (var obj in en) action(obj);
}
}
You may then use it to your heart's desire:
someControl.GetEntireControlsTree().OfType<TextBox>().ForEach(x => x.Click += someHandler);
As you have stated, you will have to go deeper than just cycling over each element in your form. This, unfortunately, implies the use of a nested loop.
In the first loop, cycle through each element. IF the element is of type GroupBox, then you know you'll need to cycle through each element inside the groupbox, before continuing; else add the event as normal.
You seem to have a decent grasp of C# so I won't give you any code; purely to ensure you develop all the important concepts that are involved in problem solving :)
you can only loop through open forms in windows forms using form collection for example to set windows start position for all open forms:
public static void setStartPosition()
{
FormCollection fc = Application.OpenForms;
foreach(Form f in fc)
{
f.StartPosition = FormStartPosition.CenterScreen;
}
}
I know that this is an older topic, but would say the code snippet from http://backstreet.ch/coding/code-snippets/mit-c-rekursiv-durch-form-controls-loopen/ is a clever solution for this problem.
It uses an extension method for ControlCollection.
public static void ApplyToAll<T>(this Control.ControlCollection controlCollection, string tagFilter, Action action)
{
foreach (Control control in controlCollection)
{
if (!string.IsNullOrEmpty(tagFilter))
{
if (control.Tag == null)
{
control.Tag = "";
}
if (!string.IsNullOrEmpty(tagFilter) && control.Tag.ToString() == tagFilter && control is T)
{
action(control);
}
}
else
{
if (control is T)
{
action(control);
}
}
if (control.Controls != null && control.Controls.Count > 0)
{
ApplyToAll(control.Controls, tagFilter, action);
}
}
}
Now, to assign an event to all the TextBox controls you can write a statement like (where 'this' is the form):
this.Controls.ApplyToAll<TextBox>("", control =>
{
control.TextChanged += SomeEvent
});
Optionally you can filter the controls by their tags.
Since the Question regarding "Adding an Event to your TextBoxes"; was already answered; I'm providing some explanation and adding an iteration alternative using a for loop instead.
Problem:
Being Unable to Get Controls Inside a Container.
Solution:
In order to retrieve the Controls inside a Container you have to specify the Container that Contains the Controls you wish to access to.
Therefore your loop must check the Controls inside a Container.
Otherwise your loop will not find the Controls inside a Container.
i.e:
foreach (Control control in myContainer.Controls)
{
if (control is TextBox) { /* Do Something */ }
}
In case you have several Containers:
Initially iterate the Containers.
Then iterate over the controls inside the container (the container found in the initial iteration).
Pseudo Code Example on How to use a for Loop Instead:
/// <summary> Iterate Controls Inside a Container using a for Loop. </summary>
public void IterateOverControlsIncontainer()
{
// Iterate Controls Inside a Container (i.e: a Panel Container)
for (int i = 0; i < myContainer.Controls.Count; i++)
{
// Get Container Control by Current Iteration Index
// Note:
// You don't need to dispose or set a variable to null.
// The ".NET" GabageCollector (GC); will clear up any unreferenced classes when a method ends in it's own time.
Control control = myContainer.Controls[i];
// Perform your Comparison
if (control is TextBox)
{
// Control Iteration Test.
// Shall Display a MessageBox for Each Matching Control in Specified Container.
MessageBox.Show("Control Name: " + control.Name);
}
}
}
Updated answer:
I needed to disable all the controls in a form, including groupboxes. This code worked:
private void AlterControlsEnable(bool ControlEnabled)
{
foreach (Control i in Controls)
i.Enabled = ControlEnabled;
}
I have 2 methods I tried to iterate through all my textboxes in an asp.net page. The first is working, but the second one is not returning anything. Could someone please explain to me why the second one is not working?
This works ok:
List<string> list = new List<string>();
foreach (Control c in Page.Controls)
{
foreach (Control childc in c.Controls)
{
if (childc is TextBox)
{
list.Add(((TextBox)childc).Text);
}
}
}
and the "not working" code:
List<string> list = new List<string>();
foreach (Control control in Controls)
{
TextBox textBox = control as TextBox;
if (textBox != null)
{
list.Add(textBox.Text);
}
}
Your first example is doing one level of recursion, so you're getting TextBoxes that are more than one control deep in the control tree. The second example only gets top-level TextBoxes (which you likely have few or none).
The key here is that the Controls collection is not every control on the page - rather, it is only the immediate child controls of the current control (and a Page is a type of Control). Those controls may in turn have child controls of their own. To learn more about this, read about the ASP.NET Control Tree here and about NamingContainers here. To truly get every TextBox anywhere on the page, you need a recursive method, like this:
public static IEnumerable<T> FindControls<T>(this Control control, bool recurse) where T : Control
{
List<T> found = new List<T>();
Action<Control> search = null;
search = ctrl =>
{
foreach (Control child in ctrl.Controls)
{
if (typeof(T).IsAssignableFrom(child.GetType()))
{
found.Add((T)child);
}
if (recurse)
{
search(child);
}
}
};
search(control);
return found;
}
Which is used as an extension method, like so:
var allTextBoxes = this.Page.FindControls<TextBox>(true);
You need to recurse. The controls are in a tree structure - Page.Controls is not a flattened list of all controls on the page. You'd need to do something like the following to get all values of TextBoxes:
void GetTextBoxValues(Control c, List<string> strings)
{
TextBox t = c as TextBox;
if (t != null)
strings.Add(t.Text);
foreach(Control child in c.Controls)
GetTextBoxValues(child, strings);
}
...
List<string> strings = new List<string>();
GetTextBoxValues(Page, strings);
you can try this piece of code to get list of all TextBoxes
public partial class _Default : System.Web.UI.Page
{
public List<TextBox> ListOfTextBoxes = new List<TextBox>();
protected void Page_Load(object sender, EventArgs e)
{
// after execution this line
FindTextBoxes(Page, ListOfTextBoxes);
//ListOfTextBoxes will be populated with all text boxes with in the page.
}
private void FindTextBoxes(Control Parent, List<TextBox> ListOfTextBoxes)
{
foreach (Control c in Parent.Controls) {
// if c is a parent control like panel
if (c.HasControls())
{
// search all control inside the panel
FindTextBoxes(c, ListOfTextBoxes);
}
else {
if (c is TextBox)
{
// if c is type of textbox then put it into the list
ListOfTextBoxes.Add(c as TextBox);
}
}
}
}
}