C# - find page control in the page - c#

I has aspx page as below. I want to find the control in the page using code behind.
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
</div>
</form>
</body>
</html>
Code Behind
protected void Button1_Click(object sender, EventArgs e)
{
string name;
foreach (Control ctrl in Controls)
{
if (ctrl.Controls.Count > 0)
{
name = ctrl.GetType().Name;
}
}
}
I can't get the button in the loop. Even i add textbox it also can't get. Any one has idea what wrong? Please help.

Try This.
protected void Button1_Click(object sender, EventArgs e)
{
string Name = "";
string Type = "";
string Id = "";
foreach (Control ctr in form1.Controls)
{
Name = ctr.GetType().Name;
Type = ctr.GetType().ToString(); ;
Id = ctr.ID; // If its server control
}
}

ASP.Net renders the page Hierarchically. This means that only top level controls are rendered directly. If any of these top level control contains some child controls, these top level control provide their own Controls property.
For example, in your case Form is the top level control that contains child controls such as Button. So on your button click call a method recursively.
protected void Button1_Click(object sender, EventArgs e)
{
DisplayControl(Page.Controls);
}
private void DisplayControls(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
Response.Write(ctrl.GetType().ToString() + " , ID::" + ctrl.ID + "<br />");
// check for child OR better to say nested controls
if (ctrl.Controls != null)
DisplayControls(ctrl.Controls);
}
}

This is because you're not getting all the controls in your page. You'd have to get controls recursively. Here's an extension method:
public static class ControlExtensions
{
public static List<Control> GetChildrenRecursive(this Control control)
{
var result = new List<Control>();
foreach (Control childControl in control.Controls)
{
result.Add(childControl);
if (childControl.Controls.Count > 0)
{
result.AddRange(GetChildrenRecursive(childControl));
}
}
return result;
}
}
Now you can rewrite your code as follows:
foreach (Control ctrl in this.Page.GetChildrenRecursive())
{
// Your button element is accessible now.
}

Related

Retrieve value from dynamically created control

I have two dropdown lists, one for days and one for nights. I also have two buttons, one button creates dynamic textboxes where the customer can enter what they want to do in day time and in place where they want to spend night.
e.g., if one customer selects 4 days and 4 nights, a textbox will be created on press of the first button.
When the user clicks the second button, I want to store all those values in database, but I noticed that on postback the fields are lost and I have no data to store.
How do I get the values from the controls created at runtime upon postback?
Here is how you can do it:
protected void Page_Load(object sender, EventArgs e)
{
if (Session["testTextBox"] != null)
{
Request.Form[Session["testTextBox"].ToString()].ToString()
}
}
protected void Button1_Click(object sender, EventArgs e)
{
TextBox t = new TextBox { ID = "testTextBox" };
this.Form.Controls.Add(t);
Session["testTextBox"] = t.UniqueID;
}
If you are adding the textbox via a client side call you don't need to store the UniqueID. Button1_Click is the postback method for your button for example.
Even though adding controls in code behind is "fine", it can lead to trouble later. It also isn't very good when editing your view, and you have to change a bunch of code in code-behind. A solution is to use a data bound control, for instance a Repeater control. That way, you can design your view in your aspx file, and leave the coding to the cs-file. It also takes care of holding information when using postbacks, since it's already setup to use the viewstate of the controls, meaning you don't have to do it.
So, using a repeater, your aspx can look something like this.
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="RepeaterPage.aspx.cs" Inherits="ASPTest.RepeaterPage" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h1>Test</h1>
<div>
<asp:Button Text="Add textbox" ID="Button1" OnClick="OnAddItem" runat="server" />
<asp:Button Text="Read values" ID="Button2" OnClick="OnReadValues" runat="server" />
</div>
<div>
<asp:Label ID="values" runat="server"></asp:Label>
</div>
<asp:Repeater ID="listofvalues" runat="server">
<ItemTemplate>
<asp:HiddenField ID="ID" Value='<%# Eval("ID") %>' runat="server" />
<asp:TextBox ID="ValueBox" Text='<%# Server.HtmlEncode((string)Eval("Value")) %>' runat="server" />
</ItemTemplate>
</asp:Repeater>
</asp:Content>
Notice that I'm using a master page, so the Content control only links to some ContentPlaceHolder in the master page. In this aspx page, you'll see that the itemtemplate in the repeater control sets up all the design needed to show the values. We'll see how this pans out in the code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ASPTest
{
public partial class RepeaterPage : Page
{
private List<Item> cache;
public List<Item> ItemValues
{
get
{
if (cache != null)
{
return cache;
}
// load values from database for instance
cache = Session["values"] as List<Item>;
if (cache != null)
{
return cache;
}
Session["values"] = cache = new List<Item>();
return cache;
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
listofvalues.DataBinding += bindValues;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsPostBack)
{
DataBind();
}
}
protected void OnAddItem(object sender, EventArgs e)
{
ItemValues.Add(new Item("some value"));
DataBind();
}
protected void OnReadValues(object sender, EventArgs e)
{
foreach (RepeaterItem repeateritem in listofvalues.Items)
{
TextBox box = (TextBox)repeateritem.FindControl("ValueBox");
HiddenField idfield = (HiddenField)repeateritem.FindControl("ID");
Item item = findItem(idfield.Value);
item.Value = box.Text;
}
StringBuilder builder = new StringBuilder();
foreach (Item item in ItemValues)
{
builder.Append(item.Value).Append(";");
}
values.Text = builder.ToString();
}
private Item findItem(string idvalue)
{
Guid guid = new Guid(idvalue);
foreach (Item item in ItemValues)
{
if (item.ID == guid)
{
return item;
}
}
return null;
}
private void bindValues(object sender, EventArgs e)
{
listofvalues.DataSource = ItemValues;
}
}
public class Item
{
private readonly Guid id;
private string value;
public Item(string value)
{
this.value = value;
id = Guid.NewGuid();
}
public Guid ID
{
get { return id; }
}
public string Value
{
get { return value; }
set { this.value = value; }
}
}
}
Sorry about the long code example, but I wanted it to be thorough. You'll see that I introduced a list of Items, in the ItemValues property. You can load the values from wherever you want. I used the Session collection, but you can load from database or some other place if you want.
Also, notice how the only thing we know about the view is the control type and control ID. There's no code describing how controls should be added or styled, we leave that to the aspx page. This creates an easy separation of concerns between the two.
Instead, when we want to add a TextBox, we instead add a new instance of the data item, an instance of the Item class. In the OnReadValues method we can update the data items values bound to the repeater, and then use either the values from the controls or the values from the items in our data list.
I hope this illustrates how one can use ASP.NET to create dynamic pages, without creating the controls in code behind, because it's not really needed if you do it like this.

Persistent dynamic control in ASP.Net

<asp:Button onclick="Some_event" Text="Add TextBox" ID="id1" runat="server" />
//once clicked:
<asp:TextBox ID="txt1" ......></asp:TextBox>
//when clicked again:
<asp:TextBox ID="txt1" ......></asp:TextBox>
<asp:TextBox ID="txt2" ......></asp:TextBox>
//and so on...
Is there a way to create dynamic controls which will persist even after the postback? In other words, when the user clicks on the button, a new textbox will be generated and when clicks again the first one will remain while a second one will be generated. How can I do this using asp.net ? I know that if I can create the controls in the page_init event then they will persist but I dont know if it possible to handle a button click before the page_init occurs, therefore there must be another way.
Yes, this is possible. One way to do this using purely ASP.NET (which seems like what you're asking for) would be to keep a count of the TextBox controls that you have added (storing that value in the ViewState) and recreate the TextBox controls in the Page_Load event. Of course, nowadays most people would probably use Javascript or jQuery to handle this task client side, but I put together a quick example to demonstrate how it works with postbacks:
Front page:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="DynamicControls.aspx.cs" Inherits="MyAspnetApp.DynamicControls" EnableViewState="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server"></head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnAddTextBox" runat="server" Text="Add" OnClick="btnAddTextBox_Click" />
<asp:Button ID="btnWriteValues" runat="server" Text="Write" OnClick="btnWriteValues_Click" />
<asp:PlaceHolder ID="phControls" runat="server" />
</div>
</form>
</body>
</html>
Code behind:
using System;
using System.Web.UI.WebControls;
namespace MyAspnetApp
{
public partial class DynamicControls : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//Recreate textbox controls
if(Page.IsPostBack)
{
for (var i = 0; i < TextBoxCount; i++)
AddTextBox(i);
}
}
private int TextBoxCount
{
get
{
var count = ViewState["txtBoxCount"];
return (count == null) ? 0 : (int) count;
}
set { ViewState["txtBoxCount"] = value; }
}
private void AddTextBox(int index)
{
var txt = new TextBox {ID = string.Concat("txtDynamic", index)};
txt.Style.Add("display", "block");
phControls.Controls.Add(txt);
}
protected void btnAddTextBox_Click(object sender, EventArgs e)
{
AddTextBox(TextBoxCount);
TextBoxCount++;
}
protected void btnWriteValues_Click(object sender, EventArgs e)
{
foreach(var control in phControls.Controls)
{
var textBox = control as TextBox;
if (textBox == null) continue;
Response.Write(string.Concat(textBox.Text, "<br />"));
}
}
}
}
Since you are recreating the controls on each postback, the values entered into the textboxes will be persisted across each postback. I added btnWriteValues_Click to quickly demonstrate how to read the values out of the textboxes.
EDIT
I updated the example to add a Panel containing a TextBox and a Remove Button. The trick here is that the Remove button does not delete the container Panel, it merely makes it not Visible. This is done so that all of the control IDs remain the same, so the data entered stays with each TextBox. If we were to remove the TextBox entirely, the data after the TextBox that was removed would shift down one TextBox on the next postback (just to explain this a little more clearly, if we have txt1, txt2 and txt3, and we remove txt2, on the next postback we'll create two textboxes, txt1 and txt2, and the value that was in txt3 would be lost).
public partial class DynamicControls : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
for (var i = 0; i < TextBoxCount; i++)
AddTextBox(i);
}
}
protected void btnAddTextBox_Click(object sender, EventArgs e)
{
AddTextBox(TextBoxCount);
TextBoxCount++;
}
protected void btnWriteValues_Click(object sender, EventArgs e)
{
foreach(var control in phControls.Controls)
{
var panel = control as Panel;
if (panel == null || !panel.Visible) continue;
foreach (var control2 in panel.Controls)
{
var textBox = control2 as TextBox;
if (textBox == null) continue;
Response.Write(string.Concat(textBox.Text, "<br />"));
}
}
}
private int TextBoxCount
{
get
{
var count = ViewState["txtBoxCount"];
return (count == null) ? 0 : (int) count;
}
set { ViewState["txtBoxCount"] = value; }
}
private void AddTextBox(int index)
{
var panel = new Panel();
panel.Controls.Add(new TextBox {ID = string.Concat("txtDynamic", index)});
var btn = new Button { Text="Remove" };
btn.Click += btnRemove_Click;
panel.Controls.Add(btn);
phControls.Controls.Add(panel);
}
private void btnRemove_Click(object sender, EventArgs e)
{
var btnRemove = sender as Button;
if (btnRemove == null) return;
btnRemove.Parent.Visible = false;
}
}
I read an article by Scott Mitchell that explains that ViewState only persists changed control state across post-back, and not the actual controls themselves. I did not have your exact scenario, but a project I was working on required dynamically added user controls and I had to add them on every postback. In that case, it is still useful to create them in Init so that they can retain their state. Here is the link: Understanding ASP.NET View State. Check section “View State and Dynamically Added Controls”.
You may have to keep track of all the controls that you are adding (in session state for example) and re-create them on post back. I just did a small test where I keep a List<string> of all the Textbox ids in Session. On postback, I recreate all the textboxes.

Find Textbox control

In my application i have 50 textboxes i want find all the textbox control using the code and i want to perform a color change in the textbox after doing certain validations. How can i acheive that? i used the following code but it doesnt work properly
foreach (Control cntrl in Page.Controls)
{
if (cntrl is TextBox)
{
//Do the operation
}
}
<%# Page Language="C#" MasterPageFile="~/HomePageMaster.master" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="Default" Title="Sample Page" %>
I've recently started doing this the 'modern' LINQ way. First you need an extension method to grab all the controls of the type you're interested in:
//Recursively get all the formControls
public static IEnumerable<Control> GetAllControls(this Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in control.GetAllControls())
{
yield return descendant;
}
}
}`
Then you can call it in your webform / control:
var formCtls = this.GetAllControls().OfType<Checkbox>();
foreach(Checkbox chbx in formCtls){
//do what you gotta do ;)
}
Regards,
5arx
protected void setColor(Control Ctl)
{
foreach (Control cntrl in Ctl.Controls)
{
if (cntrl.GetType().Name == "TextBox")
{
//Do Code
}
setColor(Control cntrl);
}
}
You can then call this with setColor(Page)
This one goes recursively, so it will run on all controls in the page.
Note that if you want it to go over the textboxes in the databound controls too, you should probably call it in the OnPreRender.
protected void Page_Load(object sender, EventArgs e)
{
ColorChange(this);
}
protected static void ColorChange(Control parent)
{
foreach (Control child in parent.Controls)
{
if (child is TextBox)
(child as TextBox).ForeColor = Color.Red;
ColorChange(child);
}
}
You will probably have to recursively go through each container. This article has one method of doing it.

ASP.Net - Loop all HTML components in the current page

I wish I could go all the elements of an ASP.Net page (HTML elements) and manipulate its text property / value. How could I do that?
I've been checking, and the property this.Page.Form.Controls apparently gets all the elements, but I would only page elements that caught the event. Does anyone have any idea.
I'll put an example of code that I'm currently studying and trying adpater for my needs.
Thanks
string x = String.Empty;
string y = String.Empty;
foreach (Control ctrl in this.Page.Form.Controls){
if (ctrl is TextBox){
x += ((TextBox)(ctrl)).ID + ((TextBox)(ctrl)).Parent + "\n";
} else {
y += ctrl.GetType().Name + ctrl.GetType().Namespace + "\n";
}
}
Obs.: I am using some components of the Telerik components.
The controls on a page are stored in a tree data structure. You could use a recursive method to do this:
private void SetText(ControlCollection controls, String textToSet)
{
foreach(Control c in controls)
{
if (c is ITextControl)
((ITextControl)c).Text = textToSet;
if (c.HasControls())
SetText(c.Controls, textToSet);
}
}
and you would call this method somewhere like let's say, in the OnPreRender Event (to make sure that you get all controls that have been added to the page) and pass the Page.Controls ControlCollection:
protected override void OnPreRenderComplete(EventArgs e)
{
base.OnPreRenderComplete(e);
SetText(Page.Controls, "new text");
}
I'll have a stab at an answer, although I'm not sure it's what you're after (see my comments to the question).
If what you want to do is to quickly handle all the TextBoxes of a form (say) in one manner, and all of its DropDownLists in another manner, then what you're looking for may be OfType
DoSomethingWithAllTextBoxes( Page.Form.Controls.OfType<TextBox>() );
DoSomethingWithAllDropDownLists( Page.Form.Controls.OfType<DropDownList>() );
private void DoSomethingWithAllTextBoxes( IEnumerable<TextBox> textboxes) {
foreach(TextBox txt in textboxes) {
txt.Text = "Modified";
}
}
If you're looking for event binding, you could run code like this one in OnInit
protected override void OnInit(EventArgs e) {
foreach(TextBox txt in Page.Form.Controls.OfType<TextBox>() ) {
txt.OnTextChanged += TextChangedEventListener;
}
base.OnInit(e);
}
Attach the elements you want to an event and then act of them when its fired. This example is for demonstration but you could define a custom event.
ASPX:
<asp:TextBox ID="TextBox1" AutoPostBack="true" runat="server"
ontextchanged="TextBox1_TextChanged"></asp:TextBox>
<asp:TextBox ID="TextBox2" AutoPostBack="true" runat="server"
ontextchanged="TextBox1_TextChanged"></asp:TextBox>
Code-Behind:
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
//do something
}

GetElementsByTagName functionality from .net code-behind page?

I am writing a webpage in C# .NET. In javascript there is a function called GetElementsByTagName... this is nice for javascript invoked from the .aspx page. My question is, is there any way I can have this kind of functionality from my C# code-behind?
--
The scenario for those curious: I used an asp:repeater to generate a lot of buttons, and now I'm essentially trying to make a button that clicks them all. I tried storing all the buttons in a list as I created them, but the list is getting cleared during every postback, so I thought I could try the above method.
Try this:
foreach (Control ctl in myRepeater.Controls)
{
if (ctl is Button)
{
((Button)ctl).Click();
}
}
HTH...
FindControl(), or iterate through the controls on the page...
For each ctl as Control in Me.Controls
If ctl.Name = whatYouWant Then
do stuff
Next 'ctl
--If you are creating the controls, you should be setting their ID's
Dim ctl as New Control()
ctl.ID = "blah1"
etc...
Well, you can find controls with the page's FindControl method, but Repeater elements have names generated by .net.
As an aside, if you really want to, you could store the list of buttons in your page's ViewState (or perhaps a list of their names).
I don't know exactly what you mean by clicks them all. But how would this following code work for you? I don't know, I haven't tested...
protected void Page_Load(object sender, EventArgs e)
{
foreach (Control control in GetControlsByType(this, typeof(TextBox)))
{
//Do something?
}
}
public static System.Collections.Generic.List<Control> GetControlsByType(Control ctrl, Type t)
{
System.Collections.Generic.List<Control> cntrls = new System.Collections.Generic.List<Control>();
foreach (Control child in ctrl.Controls)
{
if (t == child.GetType())
cntrls.Add(child);
cntrls.AddRange(GetControlsByType(child, t));
}
return cntrls;
}
Whenever you do any postback, everything is recreated, including your databound controls.
If your list is gone, so are the button controls. Unless, of course, you've recreated them, and in that case you should have recreated the list as well.
ASPX:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Repeater runat="server" ID="Repeater1">
<ItemTemplate>
<asp:Button runat="server" ID="Button1" Text="I was NOT changed" />
</ItemTemplate>
</asp:Repeater>
</form>
</body>
</html>
ASPX.CS:
using System;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(Object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("column"));
DataRow dr = null;
for (Int32 i = 0; i < 10; i++)
{
dr = dt.NewRow();
dr["column"] = "";
dt.Rows.Add(dr);
}
this.Repeater1.DataSource = dt;
this.Repeater1.DataBind();
foreach (RepeaterItem ri in this.Repeater1.Controls)
{
foreach (Control c in ri.Controls)
{
Button b = new Button();
try
{
b = (Button)c;
}
catch (Exception exc)
{
}
b.Text = "I was found and changed";
}
}
}
}
Or a variation of my own code, only changing the ASPX.CS:
using System;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(Object sender, EventArgs e)
{
#region Fill Repeater1 with some dummy data
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("column"));
DataRow dr = null;
for (Int32 i = 0; i < 10; i++)
{
dr = dt.NewRow();
dr["column"] = "";
dt.Rows.Add(dr);
}
this.Repeater1.DataSource = dt;
this.Repeater1.DataBind();
#endregion
foreach (Button b in this.FindButtonsInRepeater(ref this.Repeater1))
{
b.Text = "I was found and changed";
}
}
private List<Button> FindButtonsInRepeater(ref Repeater repeater)
{
List<Button> buttonsFound = new List<Button>();
foreach (RepeaterItem ri in repeater.Controls)
{
foreach (Control c in ri.Controls)
{
try
{
buttonsFound.Add((Button)c);
}
catch (Exception exc)
{
}
}
}
return buttonsFound;
}
}

Categories