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;
}
}
}
}
Related
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");
}
}
I am using a checkboxlist which I am populating from the database using Databind(). And i am having Dropdownlist in UpdatePanel to avoid postbacks which is also populated form database. Each Item in dropdownlist-> Associated to Many Items in Checkboxlist. Is there any way I can Make Associated listitems bold(to highlight them) on Dropdown Selected Index Changed Event so that user will know he has to select the highlighted checkboxes for the selected value in dropdownlist?
I have tried using the listitem Attributes as well but it's not working. See the code below.
protected void LOSDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
string selectedValue = LOSDropDownList.SelectedValue;
LOSQuestionsBOReadOnlyList QuestionList = LOSQuestionsBOReadOnlyList.GetLOSQuestionsBOReadOnlyListByLevelOfServiceId(Convert.ToInt32(selectedValue));
if (!selectedValue.Equals(""))
{
foreach (ListItem Item in QuestionCheckboxList.Items)
{
Item.Attributes.CssStyle.Clear();
if(QuestionList.FirstOrDefault(Val => (Val.ServiceLevelQuestion_ID == int.Parse(Item.Value.ToString()))) == null)
{
Item.Attributes.CssStyle.Add("font-weight", "bold");
}
else Item.Attributes.CssStyle.Add("font-weight", "normal");
}
}
}
catch (Exception ex)
{
ExceptionPolicy.HandleException(ex, "Event_File_Policy");
}
}
Your help will be appreciated
Thanks And Regards,
Chetan
Try this:
On the SelectedIndexChanged event of dropdownlist:
foreach(ListItem li in chkboxlist.Items)
{
//If dropdownlist selection matches with chklistbox (or whatever other logic you want)
if(li.Text == dropdownlist.SelectedText)
{
**//Here's how to set it to bold**
li.Attributes.CssStyle.Add("font-weight", "bold");
}
}
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.
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
In my code I need to loop through the controls in a GroupBox and process the control only if it a ComboBox. I am using the code:
foreach (System.Windows.Forms.Control grpbxChild in this.gpbx.Controls)
{
if (grpbxChild.GetType().Name.Trim() == "ComboBox")
{
// Process here
}
}
My question is: Instead of looping through all the controls and processing only the combo boxes is is possible to get only the combo boxes from the GroupBox? Something like this:
foreach (System.Windows.Forms.Control grpbxChild in this.gpbx.Controls.GetControlsOfType(ComboBox))
{
// Process here
}
Since you are using C# 2.0, you're pretty much out of luck. You could write a function yourself. In C# 3.0 you'd just do:
foreach (var control in groupBox.Controls.OfType<ComboBox>())
{
// ...
}
C# 2.0 solution:
public static IEnumerable<T> GetControlsOfType<T>(ControlCollection controls)
where T : Control
{
foreach(Control c in controls)
if (c is T)
yield return (T)c;
}
which you'd use like:
foreach (ComboBox c in GetControlsOfType<ComboBox>(groupBox.Controls))
{
// ...
}
Mehrdad is quite right, but your syntax (even if you are using C# 2.0) is overly complicated.
I find this to be simpler :
foreach (Control c in gpBx.Controls)
{
if (c is ComboBox)
{
// Do something.
}
}
foreach (System.Windows.Forms.Control grpbxChild in this.gpbx.Controls)
{
if (grpbxChild is ComboBox)
{
// Process here
}
}
if (!(grpbxChild is System.Windows.Forms.Combobox)) continue;
// do your processing goes here
grpbxChild.Text += " is GroupBox child";
foreach (Control items in this.Controls.OfType<GroupBox>())
{
foreach (ComboBox item in items.Controls.OfType<ComboBox>())
{
// your processing goes here
}
}