ASP.NET: How to access a dynamically created control - asp.net

I'm creating a bunch of Checkboxes dynamically:
CheckBox chkRead = new CheckBox();
chkRead.ID = "chk1";
chkRead.AutoPostBack = true;
chkRead.CheckedChanged += new EventHandler(CheckBox_CheckedChanged);
CheckBox chkPost = new CheckBox();
chkRead.ID = "chk2";
chkPost.AutoPostBack = true;
chkPost.CheckedChanged += new EventHandler(CheckBox_CheckedChanged);
protected void CheckBox_CheckedChanged(object sender, EventArgs e)
{
CheckBox chk = (CheckBox)sender;
}
What I want to do is the following:
When I check the chkPost CheckBox I want the chkRead CheckBox to be checked as well
In the CheckBox_CheckedChanged event I only have access to the CheckBox that was clicked
but I don't know how to check the other checkbox from that event.

This is from memory, but you could do something like this:
protected void CheckBox_CheckedChanged(object sender, EventArgs e)
{
CheckBox chk = (CheckBox)sender;
CheckBox chkPost = (CheckBox) chk.NamingContainer.FindControl("chk2");
CheckBox chkRead = (CheckBox) chk.NamingContainer.FindControl("chk1");
if(chk == chkPost && chk.Checked)
{
chkRead.Checked = true;
}
}
This is assuming you want to do all this in code-behind, after postback. If you want to do it in javascript, that's a different question.
This also assumes that chk1 and chk2 are in the same naming container. If they aren't, things will get complicated.

Since it is your code that creates the checkboxes, you can store their references in a list or dictionary and retrieve them by id when needed.

If you want to do it dynamically you can add an attribute to the checkboxess you are interested in-- you can then loop over the Page.Controls collection and test that the control you are looping over has that attribute and then you can check, or uncheck it.
some pseudo code:
foreach(var control in Page.Controls)
if(typeof(Control) is CheckBox and ((CheckBox)control).Attributes["myAttr"] != null)
//check or uncheck it
In reading your comment about nested controls-- this might be a bit of a hassle-- I tend to agree with Igor, that you should put the id's in a collection as they are being added dynamically.

Could you paste code where you are creating these checkboxes? Is it "OnInit" or somewhere else? Are you putting these checkboxes in container, do you store these controls as global variables or create them in method?

Related

Getting wrong sender in event handler for checkboxes

I have dynamically generated checkboxes and having EventHandler which takes care about check change of any of the checkbox. The issue is that if i uncheck any of the checkbox, it gets all other checkbox as sender and checkchange is called according to number of checkboxes number of times. If there are 3 checkboxes and 1 is unchecked than checkchange event handler is called 2 times. I dont understand what is happening.
CheckBox chkbox;
panelDynamicCheckbox.Controls.Clear();
foreach(string product in products)
{
chkbox = new CheckBox();
chkbox.ID = product;
chkbox.Text = product;
chkbox.AutoPostBack = true;
chkbox.CheckedChanged += new EventHandler(this.CheckChanged);
panelDynamicCheckbox.Controls.Add(chkbox);
}
protected void CheckChanged(object sender, EventArgs e)
{
CheckBox checkbox = (CheckBox)sender;
}
Ensure the code to add the dynamic checkbox and especially the event handler specification is done in the OnInit method in the page life-cycle. If you don't do this, these sort of unpredictable state behavior can be seen.

How can I add a new dynamic control on button click?

When I click btnGDynamicCont I want to load the first set of controls, then on each further click of that button, add a new control (textbox) alongside the other ones, so each time it is clicked I am adding a new textbox across state.
Do you know where I should add the creation of the new textbox in order to keep it after each postback?
{
protected void Page_Load(object sender, EventArgs e)
{
if (Convert.ToString(ViewState["Generated"]) == "true")
GenerateDynamicControls();
}
public void GenerateDynamicControls()
{
TextBox txtDynamic = new TextBox();
txtDynamic.ID = "txtDynamic";
txtDynamic.Text = "Dynamic TextBox";
Page.Form.Controls.Add(txtDynamic);
TextBox txtDynamic2 = new TextBox();
txtDynamic2.ID = "txtDynamic2";
txtDynamic2.Text = "Dynamic Textbox";
Page.Form.Controls.Add(txtDynamic2);
}
protected void btnGDynamicCont_Click1(object sender, EventArgs e)
{
if (Convert.ToString(ViewState["Generated"]) != "true")
{
GenerateDynamicControls();
ViewState["Generated"] = "true";
}
else
{
Response.Write("<h2>Controls are already exist in page</h2>");
}
}
}
}
Dynamic controls are usually recreated at the Page_Load method. For more information, please refer to the Dynamically Created Controls in ASP.NET article.
You can refer the below link where a very similar issue is addressed.
unable to add more than one server control dynamically in asp.net
Everytime a postback happens, you should recreate the already existing controls(dynamically added) in your page_load event and the new controls are to be created in the button_click event.
Use some logic to generate ids for the controls for the viewstate to be maintained. VIEWSTATE will be taken care automatically if the ids of the controls generated before and after postback are the same.
One way to keep track of the number of textboxes is to store the count in session.

checked checkbox answer from a dynamic created checkbox that had ben added to a panel from code behind

i am writing a web page in asp.net, in my aspx page i have a panel:
<asp:Panel ID="panel1" runat="server"/>
later in my program i add controls (checkbox) to the panel from the code behind.
like the example:
if(something)
{
checkbox cb = new checkbox();
cb.ID = "checkbox1";
panel1.Controls.Add(cb);
}
else
{
checkbox cb = new checkbox();
cb.ID = "checkbox2";
panel1.Controls.Add(cb);
}
Now my question is:
how can i catch if someone "checked" one of my check box (checkbox1 or checkbox2).
on my panel if the checkbox are created dynamically after the panel is created
thanks
On adding your checkboxes in the code behind you should also assign the associated event for checking/unchecking the CheckBoxes.
CheckBox cb = new CheckBox();
cb.ID = "checkbox1";
cb.CheckedChanged += Check1_Clicked;
panel1.Controls.Add(cb);
private void Check1_Clicked(object sender, EventArgs e)
{
// do whatever you need
}
In addition to what Dimi Toulakis said I think you also need to set the AutoPostBack property of the checkboxes that you add to True. That way when they are checked or unchecked the page will post back properly.

asp:GridView doesn't contain control added in OnRowDataBound

I have problem, I can't get control which I added in DataGrid. I am adding it in OnRowDataBound event like:
protected void RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowState == DataControlRowState.Edit || e.Row.RowState == (DataControlRowState.Alternate | DataControlRowState.Edit))
{
//int cindex = 0;
//for (cindex = 0; cindex < e.Row.Controls.Count; cindex++)
foreach (Control ctl in e.Row.Controls)
{
DataControlFieldCell dcctl = (DataControlFieldCell)ctl;
TableCell tcell = (TableCell)dcctl;
Label lblComment = new Label();
TextBox txtComment = new TextBox();
lblComment.Text = "<br>Comment: ";
dcctl.Controls.Add(lblComment);
dcctl.Controls.Add(txtComment);
//tcell.Controls.Add(lblComment);
//tcell.Controls.Add(txtComment);
//e.Row.Cells[cindex].Controls.Add(lblComment);
//e.Row.Cells[cindex].Controls.Add(txtComment);
What is happening here: there is already exist one TextBox in TableCell by default and I want to add another one TextBox and Label. After the bounding I can see 2 textboxes, I can input data into the both, but when I click Update button, then raises OnRowUpdating event where I can't get my TextBox!
protected void RowUpdating(object sender, GridViewUpdateEventArgs e)
{
grdView.EditIndex = -1;
int counter = 0;
for (counter = 0; counter < grdView.Rows[e.RowIndex].Cells.Count; counter++)
{
foreach (Control ctl in grdView.Rows[e.RowIndex].Cells[counter].Controls)
{
And here I will be getting only default one TextBox (with its value). But my TextBox is disappeared! :(
What could you suggest me here to do?
P.S. I can't use predifined columns, like asp:TemplateField in aspx file, because my table has different amount of rows every time. It is dynamic
The issue is that after you dynamically add a control to a page (or any of the page's child controls such as your datagrid) then you must recreate the controls on the server side on postback. If you don't recreate the controls on the server side, then when the runtime processes the postback it will have no idea where to put the contents of the form post.
So essentially when the page is processing the postback, it sees an HTML field called "gridView1_txtComment" (the actual HTML id is probably something else, I know). But the server side code model only has an instance of gridView1, there isn't an instance of a TextBox named txtComment unless you run the RowDataBound method again to create that control.
I think it has to do with ViewState. Make a templated column out of it, then add the second textbox to the template.
I did it!
Refused of dynamically adding controls in OnRowDataBound, and created dynamical TempalteField columns, which were containing needed to me 2 TextBoxes and Label. (With help of http://www.codeproject.com/KB/aspnet/create_template_columns.aspx)
But after my problem returned back.. On OnRowUpdating event still was not having my added TextBoxes. Finally I've found here notice http://forums.asp.net/p/1537632/3738331.aspx, that it is needed to implement TempalteField-s adding on Page_Load, that helped me to solve the problem!

How to programmatically create and use a list of checkboxes from ASP.NET?

I have a page with a table of stuff and I need to allow the user to select rows to process. I've figured out how to add a column of check boxes to the table but I can't seem to figure out how to test if they are checked when the form is submitted. If they were static elements, I'd be able to just check do this.theCheckBox but they are programaticly generated.
Also I'm not very happy with how I'm attaching my data to them (by stuffing it in there ID property).
I'm not sure if it's relevant but I'm looking at a bit of a catch-22 as I need to known which of the checkboxes that were created last time around were checked before I can re-run the code that created them.
Edit:
I've found an almost solution. By setting the AutoPostBack property and the CheckedChanged event:
checkbox.AutoPostBack = false;
checkbox.CheckedChanged += new EventHandler(checkbox_CheckedChanged);
I can get code to be called on a post back for any check box that has changed. However this has two problems:
The call back is processed after (or during, I'm not sure) Page_Load where I need to use this information
The call back is not called for check boxes that were checked when the page loaded and still are.
Edit 2:
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.
I'm going to assume you're using a DataList but this should work with and Control that can be templated. I'm also going to assume you're using DataBinding.
Code Front:
<asp:DataList ID="List" OnItemDataBound="List_ItemDataBound" runat="server">
<ItemTemplate>
<asp:CheckBox ID="DeleteMe" runat="server"/>
<a href="<%# DataBinder.Eval(Container, "DataItem.Url")%>" target="_blank">
<%# DataBinder.Eval(Container, "DataItem.Title")%></a>
</ItemTemplate>
</asp:DataList>
<asp:Button ID="DeleteListItem" runat="server" OnClick="DeleteListItem_Click" ></asp:Button>
Code Behind:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadList();
}
protected void DeleteListItem_Click(object sender, EventArgs e)
{
foreach (DataListItem li in List.Items)
{
CheckBox delMe = (CheckBox)li.FindControl("DeleteMe");
if (delMe != null && delMe.Checked)
//Do Something
}
}
LoadList();
}
protected void LoadList()
{
DataTable dt = //Something...
List.DataSource = dt;
List.DataBind();
}
protected void List_ItemDataBound(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
{
string id = DataBinder.Eval(e.Item.DataItem, "ID").ToString();
CheckBox delMe = (CheckBox)e.Item.FindControl("DeleteMe");
if (delMe != null)
delMe.Attributes.Add("value", id);
}
}
}
First, make sure that each Checkbox has an ID and that it's got the 'runat="server"' in the tag.
then use the FindControl() function to find it.
For example, if you're looping through all rows in a GridView..
foreach(GridViewRow r in Gridview1.Rows)
{
object cb = r.FindControl("MyCheckBoxId");
if(r != null)
{
CheckBox chk = (CheckBox)cb;
bool IsChecked = chk.Checked;
}
}
Postback data is restored between the InitComplete event and the PreLoad event. If your checkboxes are not created until later then the checkboxes will play "catch up" with their events and the data will be loaded into the control shortly after it is created.
If this is to late for you then you will have to do something like what you are already doing. That is you will have to access the post data before it is given to the control.
If you can save the UniqueId of each CheckBox that you create then can directly access the post data without having to given them a special prefix. You could do this by creating a list of strings which you save the ids in as you generate them and then saving them in the view state. Of course that requires the view state to be enabled and takes up more space in the viewstate.
foreach (string uniqueId in UniqueIds)
{
bool data = Convert.ToBoolean(Request.Form[uniqueId]);
//...
}
Your post is a little vague. It would help to see how you're adding controls to the table. Is it an ASP:Table or a regular HTML table (presumably with a runat="server" attribute since you've successfully added items to it)?
If you intend to let the user make a bunch of selections, then hit a "Submit" button, whereupon you'll process each row based on which row is checked, then you should not be handling the CheckChanged event. Otherwise, as you've noticed, you'll be causing a postback each time and it won't process any of the other checkboxes. So when you create the CheckBox do not set the eventhandler so it doesn't cause a postback.
In your submit button's eventhandler you would loop through each table row, cell, then determine whether the cell's children control contained a checkbox.
I would suggest not using a table. From what you're describing perhaps a GridView or DataList is a better option.
EDIT: here's a simple example to demonstrate. You should be able to get this working in a new project to test out.
Markup
<form id="form1" runat="server">
<div>
<table id="tbl" runat="server"></table>
<asp:Button ID="btnSubmit" runat="server" Text="Submit"
onclick="btnSubmit_Click" />
</div>
</form>
Code-behind
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var row = new HtmlTableRow();
var cell = new HtmlTableCell();
cell.InnerText = "Row: " + i.ToString();
row.Cells.Add(cell);
cell = new HtmlTableCell();
CheckBox chk = new CheckBox() { ID = "chk" + i.ToString() };
cell.Controls.Add(chk);
row.Cells.Add(cell);
tbl.Rows.Add(row);
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
foreach (HtmlTableRow row in tbl.Rows)
{
foreach (HtmlTableCell cell in row.Cells)
{
foreach (Control c in cell.Controls)
{
if (c is CheckBox)
{
// do your processing here
CheckBox chk = c as CheckBox;
if (chk.Checked)
{
Response.Write(chk.ID + " was checked <br />");
}
}
}
}
}
}
What about using the CheckBoxList control? I have no Visual Studio open now, but as far as I remember it is a DataBound control, providing DataSource and DataBind() where you can provide a list at runtime. When the page does a postback you can traverse the list by calling something like myCheckBoxList.Items and check whether the current item is selected by calling ListItem.Selected method. This should work.
Add them in an override of the CreateChildControls method of the Page. Be sure to give them an ID! This way they get added to the control tree at the correct time.
IMHO The best way would be to use DataBound Templated Control though, i.e. something like a ListView (in .NET 3.5). then in pageload after postback traverse all items in the databound control and use item.FindControl to get at the actual checkbox.
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.

Resources