C# LoadControl() (.ascx) and add into "this" rather than sub control - c#

I'm good with Loading the control, using the LoadControl("~/vitrualPath"), so I have:
UserControl ctrl = (UserControl)LoadControl("~/controls/someControl.ascx");
this.Controls.Add(ctrl);
//plcCtrl.Controls.Add(ctrl);
The trouble is that I wish to then loop through all the controls in the usercontrol:
foreach (Label c in this.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == "lblInput")
{
// Do some stuff with the control here
}
}
However, the added controls aren't part of this, but part of ctrl
Is there a way I can add the contents of the loaded control to this or a way to loop through both this and ctrl in one hit?

If you simply want to loop through both top-level labels and labels in ctrl, try this.Controls.Concat(ctrl.Controls).OfType<Label>() in your foreach loop.
You can also move your if into a LINQ Where call:
.Where(l => l.ID.Substring(0, 8) == "lblInput")

By using a recursive function you don't need to worry about controls within sub levels/ containers. Something like this should be OK (all you need to do is to pass the top level control along with the id substring that you are interested in). So if the conditions are met it will do whatever you have intended to do with the control and at any sub level.
public void ProcessControl(Control control, string ctrlName)
{
foreach (Label c in control.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == ctrlName)
{
// Do some stuff with the control here
}
}
foreach (Control ctrl in control.Controls)
{
ProcessControl(ctrl, ctrlName);
}
}

You should write a recursive method that starts looping the controls in this.Controls and goes down the tree of controls. It will then also go in your user control and find your labels.

I don't think there is a way to loop through both like you want.
You can easily create a method that receives a Control as parameter and iterate though its controls. Something like this:
void Function(Control control)
{
foreach (Label c in control.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == "lblInput")
{
// Do some stuff with the control here
}
}
}

You should be able to access the controls inside the user control by accessing the this.Controls[index].Controls I think, however it kind of depends what you are trying to achieve? Their might be a cleaner way of doing what you are trying to do?

Related

How to Dispose() a specific User control from panel control in C#?

I want to dispose a specific type of User control from panel. Right now i am using foreach loop to dispose the User control.
foreach (CTRL.box bx in RightPanel.Controls.OfType<CTRL.box>())
{
bx.Dispose();
}
But it is not working properly. while checking in google i find the below code.
while(tabControlToClear.Controls.Count > 0)
{
var tabPage = tabControlToClear.Controls[0];
tabControlToClear.Controls.RemoveAt(0);
tabPage.Dispose();
// Clear out events.
foreach (EventHandler subscriber in tabPage.Click.GetInvocationList())
{
tabPage.Click -= subscriber;
}
}
I am trying to do this, But for me it is a specific User control i need to dispose. they are other User controls which should be required in my form. Overall i want to dispose box User control from my form.
while (RightPanel.Controls.OfType<CTRL.box>().Count() > 0)
{
var panel = RightPanel.Controls.OfType<CTRL.box>()[0];//Here i am getting error "Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.IEnumerable<Project_Server.CTRL.box>'"
}
Can anyone help me to fix this error.
Error is pretty clear, you cannot apply indexing on IEnumerable
I would suggest use First or FirstOrDefault extension method to retrieve first element and delete it.
var panel = RightPanel.Controls.OfType<CTRL.box>().FirstOrDefault();
if(panel != null)
{
//logic
}
In case, if you would like to remove all controls of type CTRL.box use this.
List<Control> controls= RightPanel.Controls.OfType<CTRL.box>().ToList();
foreach(Control c in controls)
{
RightPanel.Controls.Remove(c);
c.Dispose();
}

Accessing variables dynamically in C#

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.

Checking Multiple textbox if they're null or whitespace

I have a form where I have lots of textboxes and all of them are required to be filled out. In C# how do I actually if check there are group of fields having a null or whitespace?
I am familiar with string.isNullOrWhiteSpace(string here) but I don't want to do multiple if statements of that, it would result in a bad code.
I am trying to avoid something like this
if(string.isNullOrWhiteSpace(string here)
|| string.isNullOrWhiteSpace(string here)
|| string.isNullOrWhiteSpace(string here))
{
// do something
}
Are there fix for this type of bad code?
You can query the controls collection of the form (or relevant container) and filter for textboxes and further query to see if any are empty (none should really have null values). Example:
var emptyTextboxes = from tb in this.Controls.OfType<TextBox>()
where string.IsNullOrEmpty(tb.Text)
select tb;
if (emptyTextboxes.Any())
{
// one or more textboxes are empty
}
You can do effectively the same thing using the fluent syntax.
bool isIncomplete = this.Controls.OfType<TextBox>().Any(tb => string.IsNullOrEmpty(tb.Text));
if (isIncomplete)
{
// do your work
}
For this code, you should be working with at least Visual Studio 2008 / C# 3 / .NET 3.5. Your project needs to have a reference to System.Core.dll (should have one by default) and you need a using System.Linq; directive in the class file.
Based upon your comments, consider another method if you are having trouble understanding or working with the linq version. You can certainly do this in an explicit loop (the Linq code will ultimately be a loop as well). Consider
bool isIncomplete = false;
foreach (Control control in this.Controls)
{
if (control is TextBox)
{
TextBox tb = control as TextBox;
if (string.IsNullOrEmpty(tb.Text))
{
isIncomplete = true;
break;
}
}
}
if (isIncomplete)
{
}
Finally, this code is written as if all of the textboxes are in a single container. That container might be the form, a panel, etc. You will need to point to the appropriate container (eg., instead of this (the form) it might be this.SomePanel). If you are working with controls that are in multiple and perhaps nested containers, you will need to do more work to find them programmatically (recursive searching, explicit concatenation, etc.) or you might just preload the references into an array or other collection. For example
var textboxes = new [] { textbox1, textbox2, textbox3, /* etc */ };
// write query against textboxes instead of this.Controls
You said you have multiple GroupBox controls. If each GroupBox is loaded onto the form and not nested in another control, this may get you started.
var emptyTextboxes = from groupBox in this.Controls.OfType<GroupBox>()
from tb in groupBox.Controls.OfType<TextBox>()
where string.IsNullOrEmpty(tb.Text)
select tb;
That depends on what you consider "bad code." Depending on your requirements what text boxes are required to be filled out can vary. Further, even if all of the fields are required all of the time you still want to give friendly error messages letting people know which field they didn't fill out. There a variety of approaches to solving this issue depending on how you are rendering your form. Since you haven't specified any here's a very direct method for doing so.
var incompleteTextBoxes = this.Controls.OfType<TextBox>()
.Where(tb => string.IsNullOrWhiteSpace(tb.Text));
foreach (var textBox in inCompleteTextBoxes)
{
// give user feedback about which text boxes they have yet to fill out
}
Yet another solution.
This will recursively travel the whole control Tree , and Check for null or empty text in all of the textboxes.
caveat -
If you have some fancy controls not inheriting from the standard Winforms textbox - check will not be performed
bool check(Control root,List<Control> nonFilled)
{
bool result =true;
if (root is TextBox && string.isNullOrEmpty(((TextBox)root).Text) )
{
nonFilled.Add(root);
return false;
}
foreach(Control c in root.Controls)
{
result|=check(c,nonFilled)
}
return result;
}
Usage :
List<Control> emptytextboxes=new List<Control>()
bool isOK=check(form, emptytextboxes);

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

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