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
Related
I extended the ASP.NET CheckBoxList web control to create a Bootstrap 5 layout version.
The control works fine, but on postback, it loses the checked state. Also, the control's SelectedItem property is null.
I created the same control for the RadioButtonList and it works perfectly. Using DotPeek, I see that both of those inherit the same controls and interfaces, so I can't figure out why the custom RadioButtonList maintains state but the CheckboxList doesn't.
Any ideas? The internet has no useable examples to speak of.
C#
public class Bootstrap5CheckBoxList : CheckBoxList {
protected override void Render(HtmlTextWriter writer) {
try {
var selected = false;
//var webControl = new WebControl(HtmlTextWriterTag.Div);
//webControl.ID = ClientID;
//webControl.RenderBeginTag(writer);
for (int index = 0; index < Items.Count; index++) {
var item = this.Items[index];
//div
writer.Indent++;
writer.WriteBeginTag($"div class='form-check {base.CssClass}'");
writer.Write('>');
writer.WriteLine();
//input
writer.Indent++;
writer.WriteBeginTag("input");
writer.WriteAttribute("id", $"{this.ID}_{index}");
writer.WriteAttribute("type", "checkbox");
writer.WriteAttribute("name", $"{this.UniqueID}_{index}");
var cssClass = "";
if (item.Attributes["class"] != null) {
cssClass = item.Attributes["class"];
}
writer.WriteAttribute("class", $"form-check-input {cssClass}");
writer.WriteAttribute("value", item.Value);
var clientID = this.ClientID;
if (item.Selected) {
if (selected) {
this.VerifyMultiSelect();
}
selected = true;
writer.WriteAttribute("checked", "checked");
}
if (item.Attributes.Count > 0) {
foreach (string key in item.Attributes.Keys) {
if (!"class".Equals(key)) {
writer.WriteAttribute(key, item.Attributes[key]);
}
}
}
if (!item.Enabled)
writer.WriteAttribute("disabled", "disabled");
if (this.Page != null) {
this.Page.ClientScript.RegisterForEventValidation(
this.UniqueID,
item.Value);
}
writer.Write('>');
writer.WriteEndTag("input");
writer.WriteLine();
//label
writer.WriteBeginTag("label");
writer.WriteAttribute("class", "form-check-label");
writer.WriteAttribute("for", $"{this.ID}_{index}");
writer.Write('>');
HttpUtility.HtmlEncode(item.Text, writer);
writer.WriteEndTag("label");
writer.Indent--;
writer.WriteLine();
//Close Div
writer.WriteEndTag("div");
writer.WriteLine();
writer.Indent--;
}
//webControl.RenderEndTag(writer);
} catch (Exception ex) {
throw new Exception(string.Format("{0}.{1}:{2} {3}", System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName, System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message, ex.StackTrace));
}
}
}
HTML
<%# Register TagPrefix="BSControls" Namespace="My.App.classes.custom_controls" Assembly="My.App" %>
<BSControls:Bootstrap5CheckBoxList ID="customCheckList" runat="server">
<asp:ListItem Value="1">Check 1</asp:ListItem>
<asp:ListItem Value="2">Check 2</asp:ListItem>
</BSControls:Bootstrap5CheckBoxList>
looks like you now using html controls, and they don't have automatic viewstate. you would be MUCH better to use a CheckBox list, and format that with bootstrap. And it also FAR better to include that checkbox list in the user control markup, and not write code to inject such controls if possible.
So, plain jane check box (input type = checkbox) as a general rule does not have automatic view state like asp.net controls. So, either drop in a check box list into your user control markup, or you may well have to add code to save/restore the values, since it looks much like you are injecting the "input" control as opposed to using a asp.net checkbox list.
After many trials, I was able to get this working and the answer is surprisingly, or maybe not, simple.
The 'name' attribute is the key and must be in the correct format.
Incorrect Format
writer.WriteAttribute("name", $"{this.UniqueID}_{index}");
Correct Format
writer.WriteAttribute("name", $"{this.UniqueID}${index}");
You must use the $ separator and not an underscore. On postback, the LoadPostData method in CheckBoxList iterates through a collection to retrieve the check state.
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");
}
}
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;
}
}
}
}
I have a control declared with PartialCaching attribute, like this:
[PartialCaching(60 * 60 * 12)]
public class MyControl : Control {
// control contents ...
}
but I create it in code, using new keyword. The problem is that if the control is in cache I must not create the control again the next time, but I need to add the control to the page hierarchy, otherwise nothing is going to be rendered. What I need in pseudo-code is something like this:
if (myControlIsCached) {
var ctl = ???; // something that represents the cached control
// e.g. could be: new LiteralControl( myControlCachedData )
this.Controls.Add( ctl );
}
else {
var ctl = new MyControl();
// setup control ...
this.Controls.Add( ctl );
}
What is the correct way of doing it?
Thanks people.
I believe you are looking to do something like this:
Control possiblyCachedControl = LoadControl("path to control");
MyControlType control = null;
if (possiblyCachedControl is MyControlType)
{
//control wasn't cached
control = possiblyCachedControl as MyControlType;
}
else if (possiblyCachedControl is PartialCachingControl && ((PartialCachingControl)possiblyCachedControl).CachedControl != null)
{
//control was cached
control = (MyControlType)((PartialCachingControl)possiblyCachedControl).CachedControl;
}
if (control != null)
{
//use the 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.