I working with ASP.NET WebForms with C#. I have a button when clicking on it creates a dropdownlist. This list is created in the "createlist" method with parameters such as id and items. I also add a SelectedIndexChanged event handler.
The list is created successfully, but when I switch between the 3 options the handler is not firing, because the console never prints the message "CHANGE".
Here's my code:
namespace web
{
public partial class Default : System.Web.UI.Page
{
List<string> lstDivs = new List<string>();
protected void btn_Click(object sender, EventArgs e)
{
Control results = FindControl("results");
lstDivs.Add("One");
lstDivs.Add("Two");
lstDivs.Add("Three");
DropDownList lstTipo = createList("lstTipos", lstDivs);
results.Controls.Add(lstTipo);
}
public DropDownList createList(string id, List<string> lstStr)
{
DropDownList lst = new DropDownList();
lst.Attributes.Add("runat", "server");
lst.ID = id + "" + Variables.numDiv;
lst.SelectedIndexChanged += new EventHandler(change);
lst.AutoPostBack = true;
lst.Items.Add(new ListItem("Select...", "Select..."));
for (int i = 0; i < lstStr.Count; i++)
{
lst.Items.Add(new ListItem(lstStr[i], lstStr[i]));
lst.Items[i].Attributes.Add("id", lst.ID + "" + i.ToString());
}
return lst;
}
protected void change(object sender, EventArgs e)
{
Debug.Write("CHANGE\r\n");
}
}
}
Once a dynamic control is created and displayed to the user, it has to be recreated on a postback for its events to fire. Execution of events is triggered by the control itself, hence, if there is no control - there is nobody to detect the change and call your change method.
You have to persist the fact that the dropdown has been created (hidden field, ViewState, Session, database, ...) and create it again in Page_Load.
I'm not sure your control exists on the postback since it is dynamically created. I found this post which might be helpful:
Dynamically Added DropDownlists Are Not Firing SelectedIndexChanged Event
Also, I don't think you need to add the runat as an attribute. This should be done automatically.
Related
I am working on a page that accepts scanned input into a textbox. Once input is entered into the textbox, a user control that contains additional fields for the input item is dynamically generated via autopostback. The user control also contains a link button that is intended to delete the item (itself). Clicking this button generates a "delete" event that is supposed to bubble up to the parent.
Unfortunately this does not seem to happen - the deletion handling code is never reached. My understanding is that it is because the controls have not been generated yet at page load, so the event handlers have not been created yet. But since I don't know what controls need to be generated at page load (since the user input hasn't been processed yet), I can't move the code to generate the user controls to PageLoad.
How should this be handled? Am I going about this the wrong way? Most of my relevant code is below.
Thanks for any help!
page.aspx:
Enter SKU (tab to enter):
<asp:TextBox ID="EntryTextBox" CssClass="textbox" AutoPostBack="true" OnTextChanged="newItem" runat="server"></asp:TextBox>
<asp:Panel ID="itempanel" runat="server">
</asp:Panel>
page.aspx.cs:
protected void newItem(object sender, EventArgs e)
{
// we need to store item entries in ViewState so they comeback on postback;
// they can't be stored in the controls themselves as the controls will
// disappear
ViewState["skus"] += "\t" + EntryTextBox.Text;
ViewState["descs"] += "\t" + itemLookup(EntryTextBox.Text);
// ...more item descriptors...
updateItemPanel();
}
protected void updateItemPanel()
{
// generate a control for each item entered in ViewState
itempanel.Controls.Clear();
List<string> skus = new List<string>(ViewState["items"].ToString().Substring(1).Split('\t'));
List<string> descs = new List<string>(ViewState["descs"].ToString().Substring(1).Split('\t'));
// ...more item descriptors...
int i = 0;
foreach (string sku in skus)
{
item newitemctrl = (item)Page.LoadControl("~/item.ascx");
newitemctrl.line = (i + 1).ToString();
newitemctrl.sku = skus[i];
newitemctrl.description = descs[i];
// ...more item descriptors...
newitemctrl.deleteLinkClicked += new EventHandler(deleteClicked);
itempanel.Controls.Add(newitemctrl);
i++;
}
}
protected void deleteClicked(object sender, EventArgs e)
{
List<string> skus = new List<string>(ViewState["skus"].ToString().Substring(1).Split('\t'));
List<string> descs = new List<string>(ViewState["descs"].ToString().Substring(1).Split('\t'));
// ...more item descriptors...
item olditemctrl = (item)sender;
skus.RemoveAt(Convert.ToInt32(olditemctrl.number) - 1);
descs.RemoveAt(Convert.ToInt32(olditemctrl.number) - 1);
ViewState["skus"] = skus.ToString();
ViewState["descs"] = descs.ToString();
updateItemPanel();
}
item.ascx:
<asp:LinkButton ID="DeleteLinkButton" runat="server" onclick="DeleteLinkButton_Click">Delete</asp:LinkButton>
item.ascx.cs:
public event EventHandler deleteLinkClicked;
protected void DeleteLinkButton_Click(object sender, EventArgs e)
{
if (this.deleteLinkClicked != null)
{
this.deleteLinkClicked(new object(), new EventArgs());
}
}
You can dispatch a postback event in javascript by adding this as onclick event:
__doPostBack("<%= button.ClientID %>", "");
DoPostBack has two arguments, the first is the ID, the second is the event name.
I used this solution successfully.
You'll find more information here.
Note:
This itself does not fire the event automatically, but you can see what you want to happen in the Load Event of your page.
You can get the arguments like this: : Request.Form["__EVENTTARGET"]
You have full access to the form data, so you can also get the values from the dynamically created controls
I know its been asked before but I cannot get the event to fire even if Im creating the buttons and events each OnInit. Im doing a Sharepoint 2010 web part and fill a radiobuttonlist with values and add a submit button to the web part each time OnInit gets called. The Page_Load method is empty. I also tried putting the paintQnA call in an overridden CreateChildControls with the same result.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
...//Get data needed to create gui
paintQnA(dr, dt, currentRow);
}
void paintQnA(DataRow dr, DataTable dt, int currentRow)
{
Panel p = new Panel();
p.ID = currentRow.ToString();
Label lbl= new Label();
lbl.Text = dr["Title"].ToString();
RadioButtonList rbl = new RadioButtonList();
rbl.ID = currentRow.ToString() + "1";
HtmlButton submitBtn = new HtmlButton();
submitBtn.ID = currentRow.ToString();
submitBtn.InnerText = "Click";
submitBtn.ServerClick += new EventHandler(submitBtn_Click);
...//fill the radiobuttonlist with data
this.Controls.Add(p);
p.Controls.Add(lbl);
p.Controls.Add(rbl);
p.Controls.Add(submitBtn);
UpdatePanel1.ContentTemplateContainer.Controls.Add(p);
}
void submitBtn_Click(object sender, EventArgs e)
{
...//Should display another dynamically created panel but is never reached.
}
Found the error:
p.ID = currentRow.ToString();
submitBtn.ID = currentRow.ToString();
They have the same ID...they can't have the same ID.
In your code I can see you are assigning the same ID to Panel and submitBtn. So due to duplicate Id, your click event is not fired.
Try different IDs for Panel and Submitbutton then It should work fine!
I want to build a dynamic DropDownList and add some list item.
With below code I can do it.
protected void BuildDynamicDropDown()
{
DropDownList ddl = new DropDownList();
ddl.ID = "newDDL";
ddl.SelectedIndexChanged += dynamicDDL_SelectedIndexChanged;
ddl.Items.Add(new ListItem("stack1", "stack1"));
ddl.Items.Add(new ListItem("stack2", "stack2"));
ddl.Items.Add(new ListItem("stack3", "stack3"));
ddl.AutoPostBack = true;
Panel1.Controls.Add(ddl);
}
protected void dynamicDDL_SelectedIndexChanged(object sender, EventArgs e)
{
//this part of code should trig another dynamic dropdown
}
But I want to use that SelectedIndexChanged event in order to change another dynamic dropdown value.
Do you have any idea?
At this link how to create event handler for dynamic drop down list in c#
one solution likes below
ddlFilter.SelectedIndexChanged += new EventHandler(ddl2_SelectedIndexChanged);
ddlFilter.AutoPostBack = true;
void ddlFilter_SelectedIndexChanged(object sender, EventArgs e)
{
//your code
}
Shortly,
I want to build more than one dynamic dropdown and assume that 3 dropdown and I want to handle their selectedindexchange events in order to interact between themselves.
If i m getting your Query!!
You can use Items array of dropdownlist and loop through it and check the condition and do the changes in the items list of another dropdownlists on selected index changed.
I am trying to create a UI that creates rows that can be dynamically populated.
I am at the point where I can add a Panel to my list that contains a DropDownList and has an associated Remove Button.
The Remove Button has an OnClick event bound that allows me to remove that specific panel.
I am having an issue when trying to bind a SelectedIndexChanged EventHandler to my DropDownList it does not.
I suspect this had something to do with how I am recreating my controls after every Postback.
I am not asking for or expecting a straightforward "Add this or Change this in the code." I really want to have a understand where I am going wrong, sorry for asking so much! :s
protected void Page_Load(object sender, EventArgs e)
{
//Showing this for the the poster that asked.
if (!IsPostBack)
{
GetClustersFromDB(user);
BindGrid();
BindState();
}
if (Session["persistControls"] != null)
{
persistControls = (List<Panel>)Session["persistControls"];
int count = 0;
foreach (Panel dynamicControl in persistControls)
{
DropDownList list = new DropDownList();
list.ID = "list" + count;
list.SelectedIndexChanged += new EventHandler(list_SelectedIndexChanged);
list.AutoPostBack = true;
list.Items.Add(new ListItem("", "0"));
list.Items.Add(new ListItem("Title", "1"));
dynamicControl.ID = "panel" + count;
Button btnRemove = new Button();
btnRemove.Click += new EventHandler(btnDelete_Click);
btnRemove.Text = "Remove";
btnRemove.CommandArgument = count.ToString();
myPlaceholder.Controls.Add(dynamicControl);
myPlaceholder.Controls.Add(btnRemove);
count++;
}
}
}
protected void btnAdd_Click(object sender, EventArgs e)
{
try
{
DropDownList list = new DropDownList();
list.SelectedIndexChanged += new EventHandler(list_SelectedIndexChanged);
list.AutoPostBack = true;
list.Items.Add(new ListItem("", "0"));
list.Items.Add(new ListItem("Title", "1"));
Panel panelContainer = new Panel();
panelContainer.ID = "panel" + persistControls.Count;
panelContainer.Controls.Add(list);
Button btnRemove = new Button();
btnRemove.Click += new EventHandler(btnDelete_Click);
btnRemove.Text = "Remove";
btnRemove.CommandArgument = persistControls.Count.ToString();
myPlaceholder.Controls.Add(panelContainer); // Pushes the Panel to the page.
persistControls.Add(panelContainer);// Adds our Panel to the Control list
myPlaceholder.Controls.Add(btnRemove); // Pushes our Button to the page.
Session["persistControls"] = persistControls; // put it in the session
}
catch
{
throw;
}
}
protected void btnDelete_Click(object sender, EventArgs e)
{
try
{
int deleteThisOne = int.Parse(((Button)sender).CommandArgument);
persistControls.Remove(persistControls[deleteThisOne]);
Session["persistControls"] = persistControls;
Response.Redirect(Request.Url.ToString());
}
catch
{
throw;
}
}
protected void list_SelectedIndexChanged(object sender, EventArgs e)
{
throw new NotImplementedException();
}
//aspx File Snippets
<asp:Button ID="btnAdd" runat="server" Text="Add Control" onclick="btnAdd_Click" />
<asp:Button ID="btnClear" runat="server" Text="Reset" onclick="btnClear_Click"/>
<asp:PlaceHolder ID="myPlaceholder" runat="server"></asp:PlaceHolder>
Maintaining controls like this is a real pain. You could try an alternative approach:
Create a (small) class that encapsulates the data you want to display in each Panel.
Maintain a List of these objects in Session.
Use an <asp:Repeater> to display the items by data-binding it to your list.
In the repeater's ItemTemplate, you can write out your <asp:Panel> that can contain a <asp:DropDownList>. Use the ItemCommand property of the drop-down list to bind it to a server-side event handler. Similarly you can put an <asp:Button> in the template and bind the ItemCommand property of this to another server-side event handler.
When you click Add, you'll postback to the server, add to your list, save it in session, re-bind the repeater.
When you click a Remove, you'll postback to the server, remove the item from your list, save it in session, re-bind the repeater.
This way lets ASP.Net handle all the control creation for you. However, if you really want to do all that yourself, then this Infinities Loop blog post is required reading.
I don't think this will work. If you create the dropdownlists in the PageLoad event, the viewstate will never get bound to it, and you will never see the option selected by the user.
You should create the controls OnInit, then when the PostBack occurs the ViewState data will get bound to the control and you will be able to access it.
I have a GridView with a item template defined like:
public class ToolLogTemplate : ITemplate
{
public String DataField { get; set; }
public ToolLogTemplate(String column)
{
DataField = column;
}
public void InstantiateIn(Control container)
{
var textBox = new TextBox();
textBox.ClientIDMode = ClientIDMode.Predictable;
textBox.CssClass = "ToolLog";
textBox.AutoPostBack = true;
textBox.DataBinding += textBox_DataBinding;
container.Controls.Add(textBox);
}
void textBox_DataBinding(object sender, EventArgs e)
{
var textBox = (TextBox)sender;
var context = DataBinder.GetDataItem(textBox.NamingContainer);
textBox.Text = DataBinder.Eval(context, DataField).ToString();
}
}
The GridView is inside of a UpdatePanel defined like:
UpdatePanel updatePanel = new UpdatePanel();
updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
The TextChanged event of the TextBoxes in the GridView trigger a full refresh of the page. My understanding was that by wrapping the TextBoxes in a UpdatePanel it would trigger a partial refresh instead. Am I misunderstanding this?
Update in response to the newest comment on the question:
I have some javascript attached to the textboxes:
currentTextBox.Attributes.Add("onFocus", String.Format("document.getElementById('RowTextBox').value = {0}; document.getElementById('ColTextBox').value = {1}; this.style.backgroundColor='#ffeb9c';", i, j));
currentTextBox.Attributes.Add("onBlur", "this.style.backgroundColor='#ffffff'");
It just sets the colors of the textbox and saves where it is in the gridview. The updatepanel works as expected with the menu and button I have in it, it's just the textboxes that cause a full postback.
Upon seeing update code, I have revised my answer...
The UpdatePanel needs to be told which controls it should respond to. You can do this by adding Triggers. In your case, you have TextBox controls within a GridView. These Textboxes are set to autopostback. Since they are within the GridView, I believe the GridView is treating them like a RowCommand. Using your original code, I would recommend you add the following:
UpdatePanel updatePanel = new UpdatePanel();
updatePanel.UpdateMode = UpdatePanelUpdateMode.Conditional;
gridView.OnRowCommand = "GridViewRowCommand";
AsyncPostbackTrigger newTrigger = new AsyncPostbackTrigger();
newTrigger.ControlID = gridView.ControlID;
updatePanel.Triggers.Add(newTrigger);
Within your codebehind, you would need to do something like this:
protected void GridViewRowCommand(object sender, RowCommandEventArgs e)
{
var myTextBox = e.Row.FindControl("myTextBoxID");
// Do some work
}
Well unfortunately I was never able to make the model described here work. Instead I put an invisible button inside of the updatepanel and had javascript click it on the textboxes onchange event. I don't know why this method works and the TextChanged one doesn't, but that's how it ended up going down.