Clear controls Dynamically - c#

I have groupbox I want to clear all the control in it , I try
public void ClearPanels(GroupBox control)
{
foreach (Control p in control.Controls)
{
control.Controls.Remove(p);
}
}
but a panel remain it , the problem I create the controls in runtime , and want to remove it in runtime

Better use this which clears all the controls at once without using a loop:
public void ClearPanels(GroupBox control)
{
control.Controls.Clear();
}

Use RemoteAt
while (control.Controls.Count > 0)
{
control.Controls.RemoveAt(0);
}
or Clear
control.Controls.Clear();

Related

Foreach button in my panel containing other panels

I'm using a custom Button which contains other elements and color styles like TopColor and BotColor. I need to handle this Button inside a panel with other panels.
I'm trying this:
foreach(CustomButton btn in panel1.Controls)
{
if(btn is CustomButton)
{
btn.TopColor=Color.Red;
}
Inside panel1 I'm containing other panels too. And the error I'm getting is
it can't be conversion element panel in a button.
One solution is to separate buttons in one panel. But I want to ask if there is some way to avoid other elements. The reason I don't want to use foreach (Control a in this.Controls) is it doesn't recognise my custom color style TopColor and BotColor
Take a look
Loop through all your controls (as Controls), check if it's a button, then cast it before you try and set the colour.
foreach(Control c in panel1.Controls)
{
if (c is CustomButton)
{
(c as CustomButton).TopColor = Color.Red;
}
}
I hope this solution works for you.
private void SetStylesToCustomButtons(Control.ControlCollection controls)
{
foreach (Control control in controls)
{
if (control is CustomButton)
{
(control as CustomButton).TopColor = Color.Red;
}
else if (control is Panel)
{
SetStylesToCustomButtons((control as Panel).Controls);
}
}
}
The reason you're getting an error is that you're trying to cast all your controls to CustomButton, even the panels. You already know the type that you're looking for, so you don't have to loop through every control in your panel.
Assuming all your custom buttons are in panel1 and that you don't need to recurse, you should rather filter the items down to the type that you want and then work with them:
var customButtons = panel1.Controls.OfType<CustomButton>();
foreach (CustomButton customButton in customButtons)
{
//do what you need here
}

Recursively find dynamically created controls inside panel

I know that similar questions have been asked but unfortunately none of them answer my question. These answers either are working with defined number of child controls or are telling to get any specific type of control like checkbox or dropdown.
I have a page which dynamically select values from the database and render controls on the page. Now these controls could be textbox, checkbox, dropdown, listbox, radiobuttons or checkboxes or all. I have been generating different divs for different controls. So textbox control will be nested inside one parent div and the checkbox can be nested within a div which is inside another divs. So the number of parent div is different for each control. Now I want to get the controls which are generated from another function on postback.
I have been thinking to store all the types and names of all controls at the time of rendering in some dictionary object. Or the other way is to loop through controls inside panel. But how I could loop through controls when I don't know the child control will be present at which level. Isn't there are way through which I could select only Wb.UI.Controls and not other Generic Html controls?
This piece of code is not working in my case as it is only limited to two levels, (I am seeking for a way to loop without defining any child control search explicitly.
foreach (Control c in panel.Controls)
{
foreach (Control child in c.Controls)
{
if (child is TextBox)
{
}
}
}
The answer is in your question, you have to use recursion.
private void FindMyControls(ControlCollection controls)
{
foreach (Control control in controls)
{
if (control is TextBox)
{
}
else if (control is Checkbox)
{
}
else if (control.Controls.Count > 0)
{
FindMyControls(control.Controls);
}
}
}
Add additional else if constructs to handle the different types if you need to do something different with each type. If you're just trying to get a collection of every control on the page you could simplify the above code a bit.
private void FindMyControls(ControlCollection controls)
{
foreach (Control control in controls)
{
if (control.Controls.Count > 0)
{
FindMyControls(c.Controls);
}
else
{
// it's a control without children of its own so
// do something with it
}
}
}
You can use a list or a dictionary. Have done this many times before. See class below
public class MyControls
{
List<Control> cntrls = new List<Control>();
List<MyControls> children = new List<MyControls>();
}
​
private void FindMyControls(ControlCollection controls)
{
foreach (Control control in controls)
{
if (control is TextBox)
{
}
else if (control is Checkbox)
{
}
else if (control.Controls.Count > 0)
{
FindMyControls(control.Controls);
}
}
}

Loop through all controls of a Form, even those in GroupBoxes

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;
}

Change all buttons on a form

I have come very close to finding a solution to this one; just missing one minor detail at this point.
What I am trying to do:
I want to change the cursor style of every button on my Form (Form1) through code. I know how to search through all controls on my form using foreach, but I'm not sure how to pass this control as a parameter through the routine that I wrote. I will show an example of what I am doing below.
private void Form1_Load(object sender, EventArgs e)
{
foreach (Button b in this.Controls)
{
ChangeCursor(b); // Here is where I'm trying to pass the button as a parameter. Clearly this is not acceptable.
}
}
private void ChangeCursor(System.Windows.Forms.Button Btn)
{
Btn.Cursor = Cursors.Hand;
}
Might anyone have a tip for me?
Thank you very much
Evan
The only thing I see is that if you have nested controls, this.Controls will not pick those up. you can try this
public IEnumerable<Control> GetSelfAndChildrenRecursive(Control parent)
{
List<Control> controls = new List<Control>();
foreach(Control child in parent.Controls)
{
controls.AddRange(GetSelfAndChildrenRecursive(child));
}
controls.Add(parent);
return controls;
}
and call
GetSelfAndChildrenRecursive(this).OfType<Button>.ToList()
.ForEach( b => b.Cursor = Cursors.Hand);
Change
foreach (Button b in this.Controls)
{
ChangeCursor(b); // Here is where I'm trying to pass the button as a parameter.
// Clearly this is not acceptable.
}
to
foreach (Control c in this.Controls)
{
if (c is Button)
{
ChangeCursor((Button)c);
}
}
Not every control on a form is a button.
Edit: You should also look for nested controls. See Bala R. answer.
Same principle as Bala R's answer but the way I do it is...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace AppName
{
public static class ControlExtensions
{
public static IEnumerable<Control> GetAllCtls(this Control control, Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAllCtls(ctrl, type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
}
}
Then use it like this...
foreach (Control ctl in this.GetAllCtls(typeof(Button)))
{
MessageBox.Show("Found a button on the form called '" + ctl.Text + "'");
}
That looks correct to me; is there a problem I'm not seeing?
EDIT: Ahh yes - if you have non-button controls in the collection, the cast will fail.
You want to only pass in controls that are buttons, so you'll want to add an IF statement.
If any of your controls fail to inherit from button I think your foreach will throw an exception.
try something like this:
foreach (Control b in this.Controls)
{
if (b is Button)
ChangeCursor((Button)b);
}
You could also use for a bit cleaner syntax:
foreach (Control c in this.Controls)
{
if (c is Button)
{
ChangeCursor(c as Button);
}
}

foreach Control ctrl in SomePanel.Controls does not get all controls

I have a panel with a bunch of labeles and textboxes inside of it.
The code:
foreach (Control ctrl in this.pnlSolutions.Controls)
Seems to only be finding html table inside the panel and 2 liternals.
But it does not get the textboxes that are in the html table.
Is there a simple way to get all the controls inside of a panel regardless of the nesting?
thanks!
Here's a lazy solution:
public IEnumerable<Control> GetAllControls(Control root) {
foreach (Control control in root.Controls) {
foreach (Control child in GetAllControls(control)) {
yield return child;
}
}
yield return root;
}
Remember also that some controls keep an internal collection of items (like the ToolStrip) and this will not enumerate those.
You would need to recursively "treewalk" through the controls, think about it like walking through a folder structure.
there is a sample Here
As far as I know your have to implement the recursion yourself, but its not really difficult.
A sketch (untested):
void AllControls(Control root, List<Control> accumulator)
{
accumulator.Add(root);
foreach(Control ctrl in root.Controls)
{
AllControls(ctrl, accumulator);
}
}
I had exactly the problem stated in the question so this may help somebody. I was attempting to clear a control collection prior to rewriting it.
private void clearCollection(Control.ControlCollection target)
{
foreach (Control Actrl in target)
{
if (Actrl is Label || Actrl is Button)
{
target.Remove(Actrl);
}
}
}
By removing the control inside the foreach loop it must mess with the internal pointers and the result is controls in the collection are missed.
My solution was to find all the controls then remove in a separate loop.
private void clearCollection(Control.ControlCollection target)
{
List<Control> accumulator = new List<Control>();
foreach (Control Actrl in target)
{
if (Actrl is Label || Actrl is Button)
{
accumulator.Add(Actrl); // find all controls first.
}
}
for (int i = 0; i < accumulator.Count; i++)
{
target.Remove(accumulator[i]);
}
}
The reason is because the only controls that are direct children of your panel are the table and the literals you mention, and it is only these that this.pnlSolutions.Controls returns.
The text boxes an labels are child controls of the table, making them grandchildren of the panel.
As #Yoda points out you need to recursively walk the controls to find them all.

Categories