Dynamically create buttons with onclick function - asp.net

I want to dynamically create buttons for each cell in a table, which works fine. The problem is that i now want to assign them server side click events, like this:
Button b = new Button();
b.Text = "Delete";
b.CssClass = "btnDelete";
b.Click += new EventHandler(this.deletePictures_Click);
While this would be my deletePictures_Click:
private void deletePictures_Click(object sender, EventArgs e)
{
test.Text = "hi";
}
But it won't fire. I did quite some research but couldn't find anything that helped me, yet. Do you guys know what's wrong?
Thanks in advance!
Edit:
Here's the rendered HTML
<td class="resultCell">
<img class="resultpicture" src="photos/DSC_101.jpg">
<input name="ctl00$cphContentBox$ctl02" value="Delete" class="btnDelete" type="submit"></td>
Edit:
I saved all the buttons in a List and then in the Session. In the OnInit I iterated the List, assigning the Button.OnClientClick to each of them. But the event still won't fire! Here's the code:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (Session["buttons"] != null)
{
buttons = (List<Button>)Session["buttons"];
for (int i = 0; i < buttons.Count; i++)
{
buttons[i].OnClientClick += new EventHandler(deletePictures_Click);
}
}
}

The easy idea is to use a grid-view instead a table.
In the above problem you need to recreate you element on pre_init evetn.
Here is a good link
http://social.msdn.microsoft.com/Forums/en-US/dbc12b8c-8796-4800-a45f-57f24b8ef72b/dynamically-created-button-event-is-not-working

you need to create an array of objects (i think a collection of them), then cycle them with a foreach construct
at school i did it in java, printing buttons in a table with foreach and collections

Related

ASP.NET: How to access a dynamically created control

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?

Add ASP Controls to a Table Dynamically

Right now I have an ASP Table. I can add rows and cells to this table just fine. What I would like to do, is instead of the cell just displaying text, I would like to add a control. For example a Button.
Right now, my first thought on how to do this would be just to put the <ASP:Button ... as the .Ttext attribute of the table cell. But my gut tells me this wont work. Further more, I probably couldn't add a function to handle the button click.
Can someone help point me in the right direction on how to achieve this?
You need to add the control to the table cell. Just call the Controls.Add method on the cell and add your control. Below is a brief sketch that should point you in the right direction.
Button b = new Button();
c.Controls.Add(b);
The following assumes you have a blank ASP:Table on your page with some defined rows (just for show really).
protected void Page_Init(object sender, EventArgs e)
{
foreach (TableRow row in this.Table1.Rows)
{
foreach (TableCell cell in row.Cells)
{
Button btn = new Button();
btn.Text = "Some Button";
btn.Click += new EventHandler(btn_Click);
cell.Controls.Add(btn);
}
}
}
void btn_Click(object sender, EventArgs e)
{
((Button)sender).Text = "Just Clicked";
}
The question hangs on what the source is for your controls. Bar far, the most effective way to make this happen is through data binding, even if your data source is just the Enumerable.Range() function.
Failing that, you need to create an instance of your controls and add them to the Control's collection of the table cell they will belong in. You can just use the += syntax for adding event handlers. The trick here is that the code to create and add the button will need to run again on every postback, and it will need to run before the page_load phase of the asp.net life cycle.

Retrieving and updating server control values in a ListView

In the past I've used jQuery Ajax to create a shopping cart. This time around I'm using the list view server control.
If I have a qty text box in each row and I want to update the quantity on a button click is this the most elegant way to achieve this?
protected void Button1_Click(object sender, EventArgs e)
{
foreach(ListViewItem item in ListViewCart.Items)
{
foreach (Control con in item.Controls)
{
if (con.GetType() == typeof(TextBox))
{
//Do Work.
}
}
}
}
I'm guessing that I would need to store the productID in a custom attribute for each textbox and use it when updating the database. (Or write more code to find that value somewhere else in the row.)
More importantly, is there a different server control I might want to use instead? I don't want to use the gridview.
I guess you could shorten it a little bit like this
foreach (TextBox txtBox in
ListViewCart.Items.SelectMany(item => item.Controls.OfType<TextBox>()))
{
//do work like - txtBox.Text = "foo";
}

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.

ASP.NET + jQuery + Dynamically Created HyperLink controls

I have an ASP.NET application that uses jQuery. My ASP.NET application dynamically generates some HyperLink elements based on some values in a database. When a user clicks one of these HyperLink elements, I want to display a jQuery dialog box that allows the user to edit the text of the HyperLink. I have this part working.
When a user clicks the "Save" button, I need to read the values of the HyperLink elements and save them back to the database. Currently, I get the initial values of the HyperLink elements. However, I cannot get any modified values. How do I get the values that were provided by the user? I have provided my .aspx and .aspx.cs code here:
test
Report:
<div id="recommendDialog" title="Number">
<table border="0" cellpadding="0" cellspacing="0">
<tr><td>Number</td></tr>
<tr><td><input id="optionName" type="text" /></td></tr>
</table>
</div>
<asp:Button ID="saveButton" runat="server" Text="Save" OnClick="saveButton_Click" />
</div>
</form>
<script type="text/javascript">
var editingID = null;
$("#recommendDialog").dialog({
autoOpen: false,
height: 200,
modal: true,
buttons: {
Cancel: function() {
$(this).dialog('close');
},
'OK': function() {
var newValue = $("#optionName").val();
if (editingID != null) {
$(editingID).attr("name", newValue);
$(editingID).html(newValue);
}
$(this).dialog('close');
}
},
close: function() {
}
});
function update_Click(link) {
editingID = "#" + link.id;
$("#optionName").val(link.name);
$('#recommendDialog').dialog('open');
}
</script>
Here is my code-behind:
public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
AddHyperlinks();
}
protected void Page_Load(object sender, EventArgs e)
{}
protected void saveButton_Click(object sender, EventArgs e)
{
foreach (TableCell cell in reportTable.Rows[0].Cells)
{
HyperLink h = (HyperLink)(cell.Controls[0]);
string newValue = h.Attributes["name"];
// Save value to database here. newValue does not show
// changed values.
Console.WriteLine(newValue);
}
}
private void AddHyperlinks()
{
TableRow row = new TableRow();
for (int i = 1; i < 11; i++)
{
HyperLink hyperlink = new HyperLink();
hyperlink.NavigateUrl = "#";
hyperlink.Text = i.ToString();
hyperlink.Attributes.Add("id", "h" + i);
hyperlink.Attributes.Add("name", i.ToString());
hyperlink.Attributes.Add("onclick", "update_Click(this);");
AddLinkButtonToRow(hyperlink, row);
}
reportTable.Rows.Add(row);
}
private void AddLinkButtonToRow(HyperLink linkButton, TableRow row)
{
TableCell cell = new TableCell();
cell.Controls.Add(linkButton);
row.Cells.Add(cell);
}
}
What you're trying to do isn't possible that way. You create links every time the page is created. Although you change the name of these links in JavaScript, these values are not posted back to you.
On Sumbit, only form elements get posted back to the server (<input>s, for example), not <a> elements, so your server doesn't "know" the changes were made.
Secondly, even if you'll change the <a>s to <input>s, you still have a problem: you won't be able to find these values in reportTable.Rows[0].Cells as you expect. Normally asp.net will fill these values correctly, even for dynamically generated controls, but not here - since you've changed their names! Asp.net cannot rebind these values.
So, what should you do? One option is to add an hidden field to every cell.
On AddLinkButtonToRow, add the following:
HtmlInputHidden hidden = new HtmlInputHidden();
hidden.ID = "hidden" + linkButton.ID;
hidden.Name = hidden.ID; //so it will be posted!
hidden.Style["display"] = "none"; //better to have a CssClass
Using jQuery, which you seem to know, change the values of these input fields, not their names (something like $(editingID).parent().find(":hidden")).
Next, you might not see the values on the controls, but you can find them at Request.Form["hiddenh1"] ... Request.Form["hiddenh11"] - All input fields will names will be posted, and you know their names this time.
I'm not sure where or what "ReportTable" and it's tablecells are, but I'm guessing your problem is that you're manipulating the value of some tags on the client using jQuery, and expecting them to be posted back to the server?
This won't work. The page got rendered with known values of your HyperLink controls in ViewState. Since tags are not input types, they will not post a value back to the server on a postback, and ViewState will always re-initialize them with their original values. Any manipulation must be done on the server side.
I would recommend doing what ScottE suggests and do your update with jquery ajax.

Resources