Better way to find control in ASP.NET - asp.net

I have a complex asp.net form,having even 50 to 60 fields in one form like there is Multiview, inside MultiView I have a GridView, and inside GridView I have several CheckBoxes.
Currently I am using chaining of the FindControl() method and retrieving the child ID.
Now, my question is that is there any other way/solution to find the nested control in ASP.NET.

If you're looking for a specific type of control you could use a recursive loop like this one -
http://weblogs.asp.net/eporter/archive/2007/02/24/asp-net-findcontrol-recursive-with-generics.aspx
Here's an example I made that returns all controls of the given type
/// <summary>
/// Finds all controls of type T stores them in FoundControls
/// </summary>
/// <typeparam name="T"></typeparam>
private class ControlFinder<T> where T : Control
{
private readonly List<T> _foundControls = new List<T>();
public IEnumerable<T> FoundControls
{
get { return _foundControls; }
}
public void FindChildControlsRecursive(Control control)
{
foreach (Control childControl in control.Controls)
{
if (childControl.GetType() == typeof(T))
{
_foundControls.Add((T)childControl);
}
else
{
FindChildControlsRecursive(childControl);
}
}
}
}

Late as usual. If anyone is still interested in this there are a number of related SO questions and answers. My version of recursive extension method for resolving this:
public static IEnumerable<T> FindControlsOfType<T>(this Control parent)
where T : Control
{
foreach (Control child in parent.Controls)
{
if (child is T)
{
yield return (T)child;
}
else if (child.Controls.Count > 0)
{
foreach (T grandChild in child.FindControlsOfType<T>())
{
yield return grandChild;
}
}
}
}

All the highlighted solutions are using recursion (which is performance costly). Here is cleaner way without recursion:
public T GetControlByType<T>(Control root, Func<T, bool> predicate = null) where T : Control
{
if (root == null) {
throw new ArgumentNullException("root");
}
var stack = new Stack<Control>(new Control[] { root });
while (stack.Count > 0) {
var control = stack.Pop();
T match = control as T;
if (match != null && (predicate == null || predicate(match))) {
return match;
}
foreach (Control childControl in control.Controls) {
stack.Push(childControl);
}
}
return default(T);
}

FindControl does not search within nested controls recursively. It does only find controls that's NamigContainer is the Control on that you are calling FindControl.
Theres a reason that ASP.Net does not look into your nested controls recursively by default:
Performance
Avoiding errors
Reusability
Consider you want to encapsulate your GridViews, Formviews, UserControls etc. inside of other UserControls for reusability reasons. If you would have implemented all logic in your page and accessed these controls with recursive loops, it'll very difficult to refactor that. If you have implemented your logic and access methods via the event-handlers(f.e. RowDataBound of GridView), it'll be much simpler and less error-prone.

Action Management On Controls
Create below class in base class.
Class To get all controls:
public static class ControlExtensions
{
public static IEnumerable<T> GetAllControlsOfType<T>(this Control parent) where T : Control
{
var result = new List<T>();
foreach (Control control in parent.Controls)
{
if (control is T)
{
result.Add((T)control);
}
if (control.HasControls())
{
result.AddRange(control.GetAllControlsOfType<T>());
}
}
return result;
}
}
From Database:
Get All Actions IDs (like divAction1,divAction2 ....) dynamic in DATASET (DTActions) allow on specific User.
In Aspx:
in HTML Put Action(button,anchor etc) in div or span and give them id like
<div id="divAction1" visible="false" runat="server" clientidmode="Static">
<a id="anchorAction" runat="server">Submit
</a>
</div>
IN CS:
Use this function on your page:
private void ShowHideActions()
{
var controls = Page.GetAllControlsOfType<HtmlGenericControl>();
foreach (DataRow dr in DTActions.Rows)
{
foreach (Control cont in controls)
{
if (cont.ClientID == "divAction" + dr["ActionID"].ToString())
{
cont.Visible = true;
}
}
}
}

Recursively find all controls matching the specified predicate (do not include root Control):
public static IEnumerable<Control> FindControlsRecursive(this Control control, Func<Control, bool> predicate)
{
var results = new List<Control>();
foreach (Control child in control.Controls)
{
if (predicate(child))
{
results.Add(child);
}
results.AddRange(child.FindControlsRecursive(predicate));
}
return results;
}
Usage:
myControl.FindControlsRecursive(c => c.ID == "findThisID");

I decided to just build controls dictionaries. Harder to maintain, might run faster than the recursive FindControl().
protected void Page_Load(object sender, EventArgs e)
{
this.BuildControlDics();
}
private void BuildControlDics()
{
_Divs = new Dictionary<MyEnum, HtmlContainerControl>();
_Divs.Add(MyEnum.One, this.divOne);
_Divs.Add(MyEnum.Two, this.divTwo);
_Divs.Add(MyEnum.Three, this.divThree);
}
And before I get down-thumbs for not answering the OP's question...
Q: Now, my question is that is there any other way/solution to find the nested control in ASP.NET?
A: Yes, avoid the need to search for them in the first place. Why search for things you already know are there? Better to build a system allowing reference of known objects.

https://blog.codinghorror.com/recursive-pagefindcontrol/
Page.FindControl("DataList1:_ctl0:TextBox3");
OR
private Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}

The following example defines a Button1_Click event handler. When invoked, this handler uses the FindControl method to locate a control with an ID property of TextBox2 on the containing page. If the control is found, its parent is determined using the Parent property and the parent control's ID is written to the page. If TextBox2 is not found, "Control Not Found" is written to the page.
private void Button1_Click(object sender, EventArgs MyEventArgs)
{
// Find control on page.
Control myControl1 = FindControl("TextBox2");
if(myControl1!=null)
{
// Get control's parent.
Control myControl2 = myControl1.Parent;
Response.Write("Parent of the text box is : " + myControl2.ID);
}
else
{
Response.Write("Control not found");
}
}

Related

Find all user controls that derives a specific base class, regarding of typeparam?

I´ve a base class for all user controls: SiteUserControlBase. It also takes a typeparam: SiteUserControlBase<T>. How do I best find all controls on the page that derives from SiteUserControlBase regarding of the typeparam?
I use the extension method below to find all controls of type SiteUserControlBase, but it will exclude all controls that also uses the type param.
public static IEnumerable<T> FindControlsOfType<T>(this ControlCollection Controls)
where T : class
{
T control;
foreach (Control ctrl in Controls)
{
if ((control = ctrl as T) != null)
{
yield return control;
}
foreach (T child in FindControlsOfType<T>(ctrl.Controls))
{
yield return child;
}
}
}

How to Find Control in Datalist1?

Hi can anybody tell how i can find a DataList which is inside the DataList control?Its giving the Error Object not set to an Instance.
i am finding the control this way :
DataList dl =((DataList) (DataList1.FindControl("DataList2")));
is it the right way?
It depends when you want to find the control and which control. If you just want to get all of them you loop through the DataList Items like below. Say you want to access a CheckBox inside a DataList.
foreach (DataListItem item in DataList1.Items)
{
if (item.ItemType == ListItemType.Item ||item.ItemType == ListItemType.AlternatingItem)
{
CheckBox chb=(CheckBox) item.FindControl("CheckBox1");
if (chb!= null)
{
//you can access chb.Checked value
}
}
}
Is DataList2 directly inside DataList1 or is it inside a child component of DataList1?
The FindControl method does not do a deep search for controls.
I wrote a method to do this a while ago, I'll post it here incase it's of use:
public static IEnumerable<Control>
GetDeepControlsByType<T>(this Control control)
{
foreach(Control c in control.Controls)
{
if (c is T)
{
yield return c;
}
if(c.Controls.Count > 0)
{
foreach (var x in c.GetDeepControlsByType<T>())
{
yield return x;
}
}
}
}

How can I dynamically clear all controls in a user control?

Is it possible to dynamically (and generically) clear the state of all of a user control's child controls? (e.g., all of its TextBoxes, DropDrownLists, RadioButtons, DataGrids, Repeaters, etc -- basically anything that has ViewState)
I'm trying to avoid doing something like this:
foreach (Control c in myUserControl.Controls)
{
if (c is TextBox)
{
TextBox tb = (TextBox)c;
tb.Text = "";
}
else if (c is DropDownList)
{
DropDownList ddl = (DropDownList)c;
ddl.SelectedIndex = -1;
}
else if (c is DataGrid)
{
DataGrid dg = (DataGrid)c;
dg.Controls.Clear();
}
// etc.
}
I'm looking for something like this:
foreach (Control c in myUserControl.Controls)
c.Clear();
...but obviously that doesn't exist. Is there any easy way to accomplish this dynamically/generically?
I was going to suggest a solution similar to Task's except (as sixlettervariables points out) we need to implement it as 1 extension method and essentailly switch on the precise type of the control passed in (i.e. copy your logic that you posted in your question).
public static class ControlExtensions
{
public static void Clear( this Control c )
{
if(c == null) {
throw new ArgumentNullException("c");
}
if (c is TextBox)
{
TextBox tb = (TextBox)c;
tb.Text = "";
}
else if (c is DropDownList)
{
DropDownList ddl = (DropDownList)c;
ddl.SelectedIndex = -1;
}
else if (c is DataGrid)
{
DataGrid dg = (DataGrid)c;
dg.Controls.Clear();
}
// etc....
}
}
It is not particularly elegent looking method but your code in your page/control is now the more succinct
foreach (Control c in myUserControl.Controls) {
c.Clear();
}
and you can of course now call control.Clear() anywhere else in you code.
You can do
foreach (Control c in myUserControl.Controls) {
myUserControl.Controls.Remove(c);
}
Because Controls is just a list, you can call Remove() on it, passing it what you want to remove.
EDIT: Oh I'm sorry, I didn't read it correctly. I don't know of a way to do this, maybe someone here who is good with Reflection could make it where you could do like
foreach (Control c in myUserControl.Controls) {
c = new c.Type.GetConstructor().Invoke();
}
or something, to turn it into a freshly made component.
I haven't tested it, but clearing viewstate for the usercontrol may work. You could expose a custom method on the user control as well:
usercontrol:
public void Clear()
{
this.ViewState.Clear();
}
page:
myUserControlInstance.Clear();
Now again I haven't tested. It's possible this will only clear the StateBag for the UserControl container, and not its nested/child controls.. if the above doesn't work you could try using recursion to walk down the control tree to clear viewstate for all children:
usercontrol:
public void Clear()
{
ClearViewState(this.Controls);
}
private void ClearViewState(ControlCollection cc)
{
foreach(Control c in cc)
{
if(c.HasControls())
{
//clear the child controls first
ClearViewState(c.Controls);
}
//then clear the control itself
c.ViewState.Clear();
}
}
page:
myUserControlInstance.Clear();
Just an idea. I haven't tested it but I think in theory it could work. One implication would be to call Clear at the correct point in the page/controls lifecycle, otherwise it may not work.
Hope this helps!
myUserControl.Controls.ToList().ForEach(c => myUserControl.Controls.Remove(c));
However, be careful, because you modify the iterating list. This could lead to some strange behaviour.
Setting EnableViewState="false" on the individual controls might save you the work, if it doesn't cause other problems for you in this instance.
What about the Control.ClearChildViewState method?
MSDN states
Deletes the view-state information for all the server control's child controls.
I have never used this though. So I am unsure if it will help you. Sounds good though, I think :)
Why not do as you suggest:
foreach (Control c in myUserControl.Controls)
c.Clear();
And then implement Clear:
public static class UserController
{
public static void Clear( this Control c )
{
c.Controls.Clear();
}
public static void Clear( this TextBox c )
{
c.Text = String.Empty;
}
}
That should do it.

C# ControlCollection Extension GetAllTextboxes

How could I get only the texboxes in a ControlCollection ?
I try :
public static IEnumerable<TextBox> TextBoxes(this ControlCollection controlCollection)
{
return (IEnumerable<TextBox>)controlCollection.Cast<Control>().Where(c => c is TextBox);
}
But I got the following error :
Unable to cast object of type 'WhereEnumerableIterator`1[System.Web.UI.Control]' to type 'System.Collections.Generic.IEnumerable`1[System.Web.UI.WebControls.TextBox]'.
I Use Asp.Net 3.5 with C#
You don't actually need a new extension method - there's already one for you that will get this:
controlCollection.OfType<TextBox>();
The OfType method returns a sequence (IEnumerable<T>) subset of the sequence provided. If the type isn't convertible, it's left out. Unlike most of the LINQ extension methods, OfType is available on sequences that aren't strongly-typed:
This method is one of the few standard query operator methods that can be applied to a collection that has a non-parameterized type, such as an ArrayList. This is because OfType<(Of <(TResult>)>) extends the type IEnumerable.
Or if you do want to wrap it in an extension method, it's of course quite simple:
public static IEnumerable<TextBox> TextBoxes(this ControlCollection controls)
{
return controls.OfType<TextBox>();
}
You want OfType():
public static IEnumerable<TextBox> TextBoxes(this ControlCollection controlCollection)
{
return controlCollection.OfType<TextBox>();
}
Here is a recursive extension method to get the Control objects that descend from the specified type, including those that are nested in the control hierarchy.
public static class ControlCollectionExtensions
{
public static IEnumerable<T> OfTypeRecursive<T>(this ControlCollection controls) where T : Control
{
foreach (Control c in controls)
{
T ct = c as T;
if (ct != null)
yield return ct;
foreach (T cc in OfTypeRecursive<T>(c.Controls))
yield return cc;
}
}
}
(For Windows Forms instead of ASP.NET, substitute Control.ControlCollection for ControlCollection.)
foreach (TextBox tBox in controls)
{
}
Example:
public static void HideControls<T>(Form pForm)
{
foreach (T cont in pForm.Controls)
{
cont.Visible = false;
}
}
HideControls<TextBox>(this);
HideControls<CheckedListBox>(this);

Cannot Access the Controls inside an UpdatePanel

What I am trying to do is accessing Page Controls at Page_Load, and make a database query, and make controls visible or not visible.
Here is the Code:
foreach (Control thiscontrol in ContentPlaceHolderBody.Controls) {
try {
if (thiscontrol.ID.Contains("TextBox") || thiscontrol.ID.Contains("Label")) {
string dummy = thiscontrol.ID;
bool IsValid = db.Roles.Any(a => a.controlName == dummy);
if (IsValid == false)
thiscontrol.Visible = false;
}
else if (thiscontrol.ID.Contains("UpdatePanel")) {
foreach (Control UPcontrols in ((UpdatePanel)thiscontrol).ContentTemplateContainer.Controls) {
if (UPcontrols.ID.Contains("TextBox") || UPcontrols.ID.Contains("DropDownList")) {
bool UPIsValid = db.Roles.Any(a => a.controlName == UPcontrols.ID);
if (UPIsValid == false)
UPcontrols.Visible = false;
}
}
}
}
catch { }
}
My Problem is with the UPcontrols! It should retrieve the controls within the UpdatePanel, but the thing is it doesn't do its job, except in the debug mode!
When I add a breakpoint, everything is OK, but when I run the web application, it doesn't find any components within the UpdatePanel...
Try this one:
ControlCollection cbb = updatepanel1.Controls;
ControlCollection cb = cbb[0].Controls;
initialize_Controls(cb);
public void initialize_Controls(ControlCollection objcontrls)
{
foreach (Control tb in objcontrls) {
if (tb is TextBox)
((TextBox)tb).Text = "";
if (tb is Panel) {
ControlCollection cbcll = tb.Controls;
foreach (Control tbb in cbcll) {
if (tbb is TextBox)
((TextBox)tbb).Text = "";
}
}
}
}
First find controls from updatepanel i.e ContentTemplate, then find controls from contentTemplate which contain all controls in it.
This seems like a very bizarre design. That is, using control IDs for such purposes is rather unusual.
Nevertheless, you need a recursive method here to do a deep walk of every control on the page. Your method will not work if the UpdatePanel is contained within another control.
Have a check on this article
http://www.codeproject.com/Articles/24178/The-magical-effects-of-the-UpdatePanel-control-in

Resources