Foreach button in my panel containing other panels - c#

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
}

Related

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

Accessing programmatically created controls

I've created a series of Panels within a placeholder, inside each panel is 5 textboxes.
On a button press, I want to get the .Text from each Textbox, and the ID of the Panel. I planned to use a JaggedArray to keep track of all Panels and their internal TextBoxes.
string[][] jaggesaddress = new string[stop][]; //stop is an int and equal to amount of panels on screen.
String[] numbers = new String[stop];
int a = 0;
//Control ctrl;
foreach (Control ctrl in BookingPlaceholder.Controls) //BookingPlaceholder is placehodler ID
{
if (ctrl is Panel)
{
numbers[a] = ctrl.ID;
a++;
//ctrl.ID = numbers[a];
foreach(TextBox tb in ctrl.ID) // error here
{
}
}
}
However the second foreach loop has an error, and I am not 100% sure how to overcome this.
"Cannot convert type 'char' to 'System.Web.UI.WebControls.TextBox'"
I know that the numbers[a] does find the first panel, however it's a string with no extension of ID.
Any suggestions are appreciated.
Josh
Your inner loop is trying to iterate through a string. What you wanted was...
foreach(TextBox tb in ctrl.Controls.OfType<TextBox>())
instead of
foreach(TextBox tb in ctrl.ID)
If for more generic controls available in PlaceHolder then
you need to find textbox controls. so it is safe to check the control type also.
foreach (Control ctr in ctrl.Controls)
{
if (ctr is TextBox)
{
//Do your things
// ((TextBox)ctr).Text
}
}

Iterating through textbox controls in a panel C#

I have seen many others with similar problems but I cannot find the flaw in my logic here. Any help would be greatly appreciated.
I have a Panel which I have added numerous label and textbox controls to, ie:
myPanel.Controls.Add(txtBox);
These controls are created and added in a method called previous to the iteration method.
I want to iterate through each textbox and use its Text property as a parameter in another method but I am not having any luck. Here is my attempt to iterate:
public void updateQuestions()
{
try
{
foreach (Control c in editQuestionsPanel.Controls)
{
if (c is TextBox)
{
TextBox questionTextBox = (TextBox)c;
string question = questionTextBox.Text;
writeNewQuestionToTblQuestions(question);
}
}
}
catch (Exception err)
{
Console.WriteLine(err.Message);
}
}
The problem I am having is that the controls are not in the Panel when I arrive at this updateQuestions() method. Here is the process involved:
A commandButton is clicked and the questions are read from a DB, for each question a method is called which adds 2 labels and a textbox to editQuestionsPanel.Controls. This panel is inside a PlaceHolder which is then made visible.
When a button inside the PlaceHolder is clicked, the updateQuestions() method is called and the editQuestionsPanel.Controls.Count = 1. As there are approx 12 questions in the DB it should be around 36. The one control inside the Panel is of type:
System.Web.UI.LiteralControl
It contains no controls.
I am sure that somwhere in the lifecycle the Panel's controls are being cleared but I do not know how to step thru the life cycle. I have a Page_load method which is called as soon as a button is clicked but once the button which calls updateQuestions() is clicked the editQuestionsPanel.Controls.Count is already back to 1 so it must be cleared before this but I do not know how to correct this...
Any help you can give to help me solve this would be greatly appreciated - its killing me!
This selects from collection controls only that which are of type TextBox.
(the same as control is TextBox or (control as TextBox) != null)
If controls are contained in editQuestionsPanel.Controls:
using System.Linq;
IEnumerable<TextBox> textBoxes = editQuestionsPanel.Controls.OfType<TextBox>();
foreach (TextBox textBox in textBoxes)
{
// do stuff
}
To select all child controls use next extension method:
public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
var children = control.Controls.OfType<T>();
return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}
Using:
IEnumerable<TextBox> textBoxes = editQuestionsPanel.GetChildControls<TextBox>();
When you add controls dynamically, you need to do that on every request - asp.net doesn't do that for you!
Add the controls in the Init or Load phase, then they will get populated with the postback values.
A frequently made mistake: Container.Controls only contains the first level child controls in this container. That is: TextBox1 in PanelA, PanelA in PanelB, you can't get TextBox1 in PanelB.Controls.
My solution is to write an extension method:
public static IEnumerable<Control> AllControls(this Control ctl)
{
List<Control> collection = new List<Control>();
if (ctl.HasControls())
{
foreach (Control c in ctl.Controls)
{
collection.Add(c);
collection = collection.Concat(c.AllControls()).ToList();
}
}
return collection;
}
Now TextBox1 is in PanelB.AllControls(). To filter all controls with type, using PanelB.AllControls().OfType<TextBox>()
If the other answers don't help, try doing your code but add recursivity. Your code would not work if it's editQuestionsPanel => Panel => Textbox
You can do something like this instead:
var questions = from tb in editQuestionsPanel.Controls.OfType<TextBox>()
select tb.Text;
foreach(var question in questions)
{
writeNewQuestionToTblQuestions(question);
}
Try this
int Count = 0;
foreach (Control ctr in Panel1.Controls)
{
if (ctr is TextBox)
Count++;
}

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.

Loop through controls in TabControl

I am looking for a way to loop through controls on a particular tab of a tabcontrol. For example, I have a tabcontrol with the following tabs:
Cars,
Pets,
Admin
On each of these tabs are several controls to display/edit/save data, etc. On the "Save" button, I would like to loop through the controls for that particular tab to check whether all required fields have been filled in.
So, if I am on the Cars tab and click "Save," I want to loop ONLY through the controls on the Cars tab and NOT the Pets or Admin tabs.
How can achieve this result?
As for looping through a TabControl's controls, you need to use the Controls property.
Here's an MSDN article on the TabControl.
Example:
TabPage page = aTabControl.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
//do stuff
}
I feel it's important to note that, in general, you should take a more structured approach to your application. E.g., instead of having all the controls on three tab pages, include exactly one UserControl on each tabpage. A CarUserControl, PetUserControl, and AdminUserControl e.g. Then each user control knows how to create the proper respective data structure so you don't have to manually munge it all together at the same level of abstraction using inter-tab loops and whatnot.
Such a separation of concerns will make it much easier to reason about your program and is good practice for writing maintainable code for your future career.
Example where I wanted to get the DataGridView in a particular tab for an application I wrote.
TabPage pg = tabControl1.SelectedTab;
// Get all the controls here
Control.ControlCollection col = pg.Controls;
// should have only one dgv
foreach (Control myControl in col)
{
if (myControl.ToString() == "System.Windows.Forms.DataGridView")
{
DataGridView tempdgv = (DataGridView)myControl;
tempdgv.SelectAll();
}
}
The Controls property is the way to go...
foreach(Control c in currentTab.Controls)
{
if(c is TextBox)
// check for text change
if(c is CheckBox)
//check for check change
etc...
}
TabControl has a SelectedTab property, so you'd do something like this:
foreach(Control c in tabControl.SelectedTab.Controls)
{
//do checks
}
foreach (Control c in this.tabControl1.SelectedTab.Controls)
{
// Do something
}
I had the need to disable or enable controls of a tab as well. I had to go a bit more generic though. Hope it helps people and I didn't make a mistake
private void toggleControls(Control control, bool state)
{
foreach (Control c in control.Controls)
{
c.Enabled = state;
if (c is Control)
{
toggleControls(c, state);
}
}
}

Categories