dropdownchanged event not firing in usercontrol in asp.net - asp.net

I have a dropdownlist in a usercontrol as shown below
<asp:dropdownlist id="ddlLanguage" runat="server" AutoPostBack="true" EnableViewState="true" onselectedindexchanged="ddlLanguage_SelectedIndexChanged">
</asp:dropdownlist>
my selectedchanged event is not getting fired even once
in code behind
if (!IsPostBack)
{
//dt - is list of languages availbale in DB
//value[0]-contains lang currently to be binded to dropdownlist based
//remaining values (values [1]) to are to be populated to textbox
LoadModuleInfo(dt,values)
}
private void LoadModuleInfo(System.Data.DataTable dtLanguages, string[] values)
{
this.txbxModuleName.Text = values[1];
this.ddlLanguage.DataSource = dtLanguages;
this.ddlLanguage.DataTextField = "language_description";
this.ddlLanguage.DataValueField = "language";
this.ddlLanguage.DataBind();
// set up selections on the screen
this.ddlLanguage.SelectedIndex = this.getIndex(dtLanguages, values[0]);
}
protected void ddlLanguage_SelectedIndexChanged(object sender, System.EventArgs e)
{
//get new values ( values[0] and values[1])
LoadModuleInfo(dtLanguages, values);
}
protected int getIndex(DataTable dt, string recordId)
{
int intCt = 0;
foreach (System.Data.DataRow dr in dt.Rows)
{
if (dr[0].ToString() == recordId)
{
break;
}
else
{
intCt++;
}
}
return intCt;
}
i have wriiten the above code, but selectedchanged event is not fired for dropdownlist control available in USERCONTROL.
Please help.

If you page not refreshed at all .. most probably you have a javascript error in the page

Kindly remove below line from your code and try
this.ddlLanguage.DataValueField = "language";
or
change this too language to language_description

Related

Creating dynamic controls in Page_PreRender based on viewstate causes button OnClick event to not work

I realize that dynamic controls should be created within Page_Load and Page_Init in order for them to be registered in the control tree.
I have created a custom control that requires the use of ViewState in a button OnClick event. This ViewState is then used to dynamically create controls.
Since the life-cycle will go: Page Load -> Button Click -> Page PreRender. The view-state will not be updated until "Button Click", thus I am creating my dynamic controls in Page PreRender. However, creating a button and programatically assigning the OnClick EventHandler in Page_PreRender does not work.
Does anyone know how I can get this to work?
btn_DeleteTableRow_Click will not fire. This is setup in CreatePartRows()
Here is my example:
<asp:UpdatePanel ID="up_RMAPart" runat="server" UpdateMode="Conditional" EnableViewState="true" ChildrenAsTriggers="true">
<ContentTemplate>
<div class="button" style="width: 54px; margin: 0px; float: right;">
<asp:Button ID="btn_AddPart" runat="server" Text="Add" OnClick="btn_AddPart_Click" />
</div>
<asp:Table ID="Table_Parts" runat="server" CssClass="hor-zebra">
</asp:Table>
<div class="clear"></div>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btn_AddPart" EventName="Click" />
</Triggers>
Code Behind:
[Serializable]
public struct Part
{
public string PartName;
public int Quantity;
public int PartID;
public Part(string sPartName, int iQuantity, int iPartID)
{
PartName = sPartName;
Quantity = iQuantity;
PartID = iPartID;
}
}
public partial class RMAPart : System.Web.UI.UserControl
{
private Dictionary<string,Part> m_RMAParts;
private int m_RowNumber = 0;
public Dictionary<string, Part> RMAParts
{
get
{
if (ViewState["m_RMAParts"] != null)
return (Dictionary<string, Part>)ViewState["m_RMAParts"];
else
return null;
}
set
{
ViewState["m_RMAParts"] = value;
}
}
public int RowNumber
{
get
{
if (ViewState["m_RowNumber"] != null)
return Convert.ToInt32(ViewState["m_RowNumber"]);
else
return 0;
}
set
{
ViewState["m_RowNumber"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
RMAParts = new Dictionary<string, Part>();
RowNumber = 0;
RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
RowNumber = 1;
CreatePartRows();
}
}
protected void Page_PreRender(object sender, EventArgs e)
{
CreatePartRows();
}
private void CreatePartRows()
{
Table_Parts.Controls.Clear();
TableHeaderRow thr = new TableHeaderRow();
TableHeaderCell thc1 = new TableHeaderCell();
thc1.Controls.Add(new LiteralControl("Part"));
thr.Cells.Add(thc1);
TableHeaderCell thc2 = new TableHeaderCell();
thc2.Controls.Add(new LiteralControl("Quantity"));
thr.Cells.Add(thc2);
TableHeaderCell thc3 = new TableHeaderCell();
thc3.Controls.Add(new LiteralControl(""));
thr.Cells.Add(thc3);
Table_Parts.Rows.Add(thr);
foreach (KeyValuePair<string, Part> kvp in RMAParts)
{
string[] sKey = kvp.Key.Split('_');
TableRow tr = new TableRow();
tr.ID = kvp.Key;
TableCell tc1 = new TableCell();
TextBox tb_Part = new TextBox();
tb_Part.ID = "tb_Part_" + sKey[1];
tb_Part.CssClass = "textbox1";
tc1.Controls.Add(tb_Part);
tr.Cells.Add(tc1);
TableCell tc2 = new TableCell();
TextBox tb_Quantity = new TextBox();
tb_Quantity.ID = "tb_Quanitty_" + sKey[1];
tb_Quantity.CssClass = "textbox1";
tc2.Controls.Add(tb_Quantity);
tr.Cells.Add(tc2);
TableCell tc3 = new TableCell();
Button btn_Delete = new Button();
btn_Delete.ID = "btn_Delete_" + sKey[1];
btn_Delete.CommandArgument = tr.ID;
btn_Delete.Click += new EventHandler(btn_DeleteTableRow_Click);
btn_Delete.Text = "Remove";
tc3.Controls.Add(btn_Delete);
tr.Cells.Add(tc3);
Table_Parts.Rows.Add(tr);
}
}
public void Reset()
{
Table_Parts.Controls.Clear();
RMAParts.Clear();
RowNumber = 0;
RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
RowNumber = 1;
CreatePartRows();
}
protected void btn_AddPart_Click(object sender, EventArgs e)
{
RMAParts.Add("PartRow_" + RowNumber.ToString(), new Part());
RowNumber++;
}
protected void btn_DeleteTableRow_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
TableRow tr = (TableRow)Table_Parts.FindControl(btn.CommandArgument);
Table_Parts.Rows.Remove(tr);
RMAParts.Remove(btn.CommandArgument);
}
}
To ensure that the values of input fields persist across postbacks and that server events are raised:
Use view state to keep track of dynamically created controls.
Re-create the controls with the same IDs in LoadViewState (not Load or PreRender, because then the values of input fields will be lost).
The rest of this answer details how I modified your code to get it to work.
RMAPart.ascx
Just for convenience, you can declare the header row in the .ascx:
<asp:Table ID="Table_Parts" runat="server" CssClass="hor-zebra">
<asp:TableRow>
<asp:TableHeaderCell Text="Part" />
<asp:TableHeaderCell Text="Quantity" />
<asp:TableHeaderCell />
</asp:TableRow>
</asp:Table>
RMAPart.ascx.cs
To keep track of dynamically created rows, maintain a list of row IDs in view state:
public partial class RMAPart : System.Web.UI.UserControl
{
private List<string> RowIDs
{
get { return (List<string>)ViewState["m_RowIDs"]; }
set { ViewState["m_RowIDs"] = value; }
}
In the btn_AddPart_Click handler, generate a new row ID and create the controls for the new row:
protected void btn_AddPart_Click(object sender, EventArgs e)
{
string id = GenerateRowID();
RowIDs.Add(id);
CreatePartRow(id);
}
private string GenerateRowID()
{
int id = (int)ViewState["m_NextRowID"];
ViewState["m_NextRowID"] = id + 1;
return id.ToString();
}
private void CreatePartRow(string id)
{
TableRow tr = new TableRow();
tr.ID = id;
TableCell tc1 = new TableCell();
TextBox tb_Part = new TextBox();
tb_Part.ID = "tb_Part_" + id;
tb_Part.CssClass = "textbox1";
tc1.Controls.Add(tb_Part);
tr.Cells.Add(tc1);
TableCell tc2 = new TableCell();
TextBox tb_Quantity = new TextBox();
tb_Quantity.ID = "tb_Quantity_" + id;
tb_Quantity.CssClass = "textbox1";
tc2.Controls.Add(tb_Quantity);
tr.Cells.Add(tc2);
TableCell tc3 = new TableCell();
Button btn_Delete = new Button();
btn_Delete.ID = "btn_Delete_" + id;
btn_Delete.CommandArgument = id;
btn_Delete.Click += btn_DeleteTableRow_Click;
btn_Delete.Text = "Remove";
tc3.Controls.Add(btn_Delete);
tr.Cells.Add(tc3);
Table_Parts.Rows.Add(tr);
}
In the btn_DeleteTableRow_Click handler, delete the clicked row and update view state:
protected void btn_DeleteTableRow_Click(object sender, EventArgs e)
{
Button btn = (Button)sender;
TableRow tr = (TableRow)Table_Parts.FindControl(btn.CommandArgument);
Table_Parts.Rows.Remove(tr);
RowIDs.Remove(btn.CommandArgument);
}
Hook Page_Load and start things off by creating the first row:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Reset();
}
}
public void Reset()
{
while (Table_Parts.Rows.Count > 1)
Table_Parts.Rows.RemoveAt(Table_Parts.Rows.Count - 1);
ViewState["m_NextRowID"] = 0;
string id = GenerateRowID();
RowIDs = new List<string> { id };
CreatePartRow(id);
}
Override LoadViewState and re-create the rows using the IDs stored in view state:
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
foreach (string id in RowIDs)
{
CreatePartRow(id);
}
}
}
Dealing with Parts
The code above doesn't use your Part structure at all. To actually move data between your business objects and the user control, you can add a public method that takes a Part collection and uses it to create rows and populate text boxes, and then add another public method that reads out the values of the text boxes into a Part collection.
The button click isn't being fired because control events are called right after the Load event. Your button isn't in the control hierarchy at the time that the asp.net lifecycle is trying to call your event, so it's being dropped. Remember, it's a round-trip and the control has to exist on postback before the LoadComplete event fires for its event handlers to get called.
Create your dynamic controls in the PreLoad or Load event and you should be OK (you will have access to the full viewstate at that time to make any decisions regarding whether or not you need to dynamically create your delete button for that row).
ASP.net Page Lifecycle Docs: http://msdn.microsoft.com/en-us/library/ms178472(v=vs.100).aspx
I think that Robert has the right answer, but needs to be more clear about WHICH Page.Load he is talking about. There are three page requests here.
Initial Page request, no initial button click yet.
Postback on button click. No processing in Page Load. PreRender call creates the new table rows and the new button and links up the button click event to the new button.
Postback after the client clicks on the new button. You need to re-create the dynamic button in Page Load so that the Click event of the dynamic button doesn't get dropped.
Agree with Robert and Bill.
But to add here, in my opinion only way that you would achieve this is by creating a custom control/web server control (inheriting WebControl class), where you override the CreateChildControls method and RenderContents methods. I think this is what you mean when you said, in one of your comments, you are going to code out a grid-view version.

ASP:Dropdownlist onselectedindexchanges function does not fire even when I set autopostback to true

I am using a wizard. and in step two the user can add information about a location that will be stored. He can add as many locations as he likes.
In step three I want to enter more information about the specific location.
To select which location to use I have ASP:dropdownlist that I populate when user enters stuff.
I want to change index but it just does not work It never goes into the function when I tried debugging. I am using a label to debug.
When I change the selected item the page reloads and the first item in the dropdown list is always selected even though I selected something else. I do not understand why that happens
here is what I have
ASPX File
Select location to enter data for:
<asp:DropDownList ID="s3_location_list" runat="server" AutoPostBack="true" OnSelectedIndexChanged="stepThree_location_list_SelectedIndexChanged">
</asp:DropDownList>
Current location index:
<asp:Label ID="Label3" runat="server" Text="Label"></asp:Label>
CS files
/*This is where I add data to the drop down list
protected void stepTwo_addLocationData(object Sender, System.EventArgs e)
{
//initialize a temporary string array to get all the data from the form
//Console.Write("AAAAA"+ Context.Items["IsRefreshed"]);
Boolean refreshed = (Boolean)Context.Items["IsRefreshed"];
if (!refreshed)
{
//if user is adding a new entry to the location table
LocationData new_location = new LocationData();
//add stuff to locationData
if (stepTwo_locationTableIndex == -1)
{
//add the new_location element into the location_data array
location_data.Add(new_location);
//redraw the table
DataRow dr = dt.NewRow();
dr["Location"] = new_location.City;
//dr["Name"] = loc;
dt.Rows.Add(dr);
}
else
{
location_data[stepTwo_locationTableIndex] = new_location;
dt.Rows[stepTwo_locationTableIndex]["Location"] = City.Text;
}
GridView1.DataSource = dt.DefaultView;
GridView1.DataBind();
///CreateTable();
stepFive_setLocationListOptions();
stepThree_setLocationListOptions();
stepTwo_resetForm();
}
/*this is the page load on step 3
protected void stepThree_load()
{
if (!IsPostBack && Session["s_s3_locationDropdownIndex"] == null)
{
stepThree_locationDropdownIndex = s3_location_list.SelectedIndex;
Session["s_s3_locationDropdownIndex"] = stepThree_locationDropdownIndex;
}
else
{
stepThree_locationDropdownIndex = (int) Session["s_s3_locationDropdownIndex"];
}
s3_location_list.SelectedIndex = stepThree_locationDropdownIndex;
Label3.Text = "" + s3_location_list.SelectedIndex;
}
/*this is my where I populate the list
protected void stepThree_setLocationListOptions()
{
s3_location_list.Items.Clear();
int i = 0;
foreach (LocationData item in location_data)
{
s3_location_list.Items.Add(new ListItem(item.City, "" + i));
}
s3_location_list.DataBind();
}
/* this is the function thats callled when selected index is changed.
protected void stepThree_location_list_SelectedIndexChanged(object sender, EventArgs e)
{
Label3.Text = "Hello";
}
I think the problem is the order of execution. You should only initialize a DropDownList once, otherwise it will overwrite your SelectedIndex. For example, use the OnInit event:
<asp:DropDownList ID="s3_location_list"
runat="server"
OnInit="s3_location_list_Init"/>
And write the event method like this:
protected void s3_location_list_Init(object sender, EventArgs e)
if (!IsPostBack) {
foreach (LocationData item in location_data)
{
s3_location_list.Items.Add(new ListItem(item.City, "" + i));
}
s3_location_list.DataBind();
}
}

Maintain selected item in DropDownList inside GridView after removing some ListItems

I have a GridView with a DropDownList in each row. (The items in the DropDownList are the same for each.) I have a DropDownList "ddlView" outside of the GridView that is used for filtering the available options in the other DropDownLists. The default selection for ddlView is no filter.
When a user selects a new value for ddlView any selected values in the other DropDownLists disappear if they are not one of the values present after the filter is applied. What I would like to happen in that case is the previously selected value still be present and selected.
What is the best way to accomplish this?
The previously selected values are available during postback but appear to be cleared once DataBind() is called on the GridView, so I am unable to determine their previous value in the method where they are populated (the RowDataBound event).
My best idea so far is to manually store that information into an object or collection during postback and reference it later during the databinding events.
Is there a better way?
I don't think there is a better way to accomplish this as when the GridView is bound all of the controls are recreated thus removing the selections.
The following works: (I store the selections on postback to retrieve again in the RowDataBound event)
Markup
<asp:Button ID="button1" runat="server" Text="Post Back" OnClick="button1_Click" />
<br />
<asp:GridView ID="gridView1" runat="server">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:DropDownList ID="gridViewDropDownList" runat="server">
<asp:ListItem Value="1">Item 1</asp:ListItem>
<asp:ListItem Value="2">Item 2</asp:ListItem>
<asp:ListItem Value="3">Item 3</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Code
public class GridViewDropDownSelections
{
public int RowIndex { get; set; }
public int SelectedIndex { get; set; }
}
...
private List<GridViewDropDownSelections> selectedDropDownListItems = new List<GridViewDropDownSelections>();
protected override void OnLoad(EventArgs e)
{
var selections = gridView1.Rows.Cast<GridViewRow>().Where(r => r.RowType == DataControlRowType.DataRow)
.Select(r => new GridViewDropDownSelections() { RowIndex = r.RowIndex, SelectedIndex = ((DropDownList)r.FindControl("gridViewDropDownList")).SelectedIndex }).ToList();
selectedDropDownListItems.AddRange(selections);
gridView1.RowDataBound += new GridViewRowEventHandler(gridView1_RowDataBound);
if (!IsPostBack)
{
BindDataGrid();
}
base.OnLoad(e);
}
protected void button1_Click(object sender, EventArgs e)
{
BindDataGrid();
}
private void BindDataGrid()
{
//Dummy data
string[] data = new string[] { "Item 1", "Item 2", "Item 3" };
gridView1.DataSource = data;
gridView1.DataBind();
}
void gridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var selection = selectedDropDownListItems.FirstOrDefault(i => i.RowIndex == e.Row.RowIndex);
if (selection != null)
{
try
{
DropDownList gridViewDropDownList = (DropDownList)e.Row.FindControl("gridViewDropDownList");
gridViewDropDownList.SelectedIndex = selection.SelectedIndex;
}
catch (Exception)
{
}
}
}
}
Hope it helps.
Instead of using DataBinding to apply the filter, you could add/remove the DropdownList items through the Items property, when another filter is selected. This way the selected value in the dropdowns should not be resetted.
You might be able to accomplish this with a simple condition. I don't know how you're filtering the items, but it would go something like this:
for (int itemIndex = 0; itemIndex < DropDownList1.Items.Count; itemIndex++)
{
ListItem item = DropDownList1.Items[itemIndex];
if (DropDownList1.Items.IndexOf(item) > ddlView.SelectedIndex)
{
if (!item.Selected)
{
DropDownList1.Items.Remove(item);
}
}
}
Not sure if this is what you're looking for, but hope it helps.
Use client side javascript
Search for all the appropriate select inputs and for each one found, go through the options and remove the one selected in the master ddl
function UpdateDropDowns (tbl) {
var controls = tbl.getElementsByTagName('select');
for (var i = 0; i < controls.length; i++) {
RemoveOption(controls[i], 'the value to remove');
}
}
function RemoveOption(ddl, val)
{
for (var i = ddl.options.length; i>=0; i--) {
if (ddl.options[i].value == val) {
ddl.remove(i);
}
}
}

Preserving CheckBox's Value in Dynamic GridView TemplateField

I am trying to create a GridView which will contain a list of users and permission levels that they have access to. Each row will have the following fields: User_ID, Username, Permission1, Permission2, ..., PermissionN, where the values of permissions field are "0" if the user does not have that permission and "1" if they do (note that columns returned by the DAL are not named Permission1, but rather are the actual name of the permission). I would like to represent the data by using CheckBoxes, to allow an admin to quickly grant or revoke permissions to a large number of users at once.
Since I will not know the number of permissions before-hand, I dynamically create TemplateFields and CheckBoxes, and this works fine; the GridView shows the current permission levels of all the users. I run into a problem when I try to update permissions based on the user checking and unchecking boxes.
Once the user is done changing permissions, I have an "Update" button, which of course causes a postback. Since the postback occurs before the OnClick event, by the time I reach OnClick all of the CheckBoxes have been reset to their initial values. Is there a way I can somehow grab the value of the CheckBoxes' Checked property before the postback occurs? Is there another (better) way of doing this? Thanks.
ASPX:
<asp:GridView ID="gvUsers" runat="server" AutoGenerateColumns="False"
EnableModelValidation="True" DataKeyNames="ID">
</asp:GridView>
<asp:ObjectDataSource ID="odsUsers" runat="server" SelectMethod="GET_USERS"
TypeName="oDAL">
</asp:ObjectDataSource>
<asp:Button ID="btnUpdate" runat="server" Text="Update Permissions" OnClick="btnUpdate_OnClick"/>
Code Behind:
private static string[] excludeCols = { "ID", "Username" };
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) // Removing the IsPostBack check refreshes the GridView when the Update button is clicked
bindGridViewWithHiddenID(gvUsers, ((DataView)odsUsers.Select()).Table, excludeCols);
}
public static bool bindGridViewWithHiddenID(GridView gv, DataTable dt, string[] excludeCols)
{
gv.Columns.Clear();
gv.DataBind();
if (gv != null && dt != null && dt.Rows.Count > 0)
{
DataControlField newField;
foreach (DataColumn column in dt.Columns)
{
if (excludeCols.Contains(column.ColumnName))
{
newField = new BoundField();
((BoundField)newField).DataField = column.ColumnName;
}
else
{
newField = new TemplateField();
((TemplateField)newField).ItemTemplate = new CustomTemplate(column.ColumnName);
}
newField.HeaderText = column.ColumnName;
if (column.ColumnName == "ID" || column.ColumnName.EndsWith("_ID"))
newField.Visible = false;
gv.Columns.Add(newField);
}
gv.DataSource = dt;
gv.DataBind();
return true;
}
return false;
}
// By this time execution reaches here the CheckBoxes have already been reset
protected void btnUpdate_Click(object sender, EventArgs e)
{
...
}
CustomTemplate class:
public class CustomTemplate : ITemplate
{
private string binding;
private static int count = 0;
public CustomTemplate(string colNameBinding)
{
binding = colNameBinding;
}
public void InstantiateIn(Control container)
{
CheckBox chk = new CheckBox();
chk.ID = "chk" + count++;
chk.DataBinding += new EventHandler(this.chk_OnDataBinding);
container.Controls.Add(chk);
}
public void chk_OnDataBinding(object sender, EventArgs e)
{
CheckBox chk = (CheckBox)sender;
GridViewRow namingContainer = (GridViewRow)chk.NamingContainer;
DataRowView dataRow = (DataRowView)namingContainer.DataItem;
if (dataRow[binding].ToString() == "1")
chk.Checked = true;
else
chk.Checked = false;
}
This behavior is expected and is normal. It has to do with the Page Life cycle and tracking of ViewState on dynamically added controls. I recommend you read this article from Scott Mitchell, specially the section titled: View State and Dynamically Added Controls.
I tried adding the GridView binding to OnInit() but since the CheckBoxes aren't actually instantiated until the GridView is DataBound, and since DataBinding will revert the GridView back to its original state, I seem to be stuck again.

Cannot find selected value of dynamically added controls in repeater

I am creating a survey page that has a list of questions and answers that can be radiobuttonlists, checkboxlists or textboxes. These controls are added dynamically to a Repeater in its ItemDataBound event using Controls.Add.
I've managed to render the page ok but when I submit the form and iterate over the controls in the repeater to get the selectedvalues of the radiobuttons and textbox values, FindControl returns null. What do I need to do to get get the selected values? I've tried iterating over the RepeaterItems but that returned null too. I've tried different types of FindControl but it never resolves the control types.
It works if I add a declarative DataBinder in the Repeater like this
<asp:Repeater ID="rptSurvey" runat="server" Visible="true" EnableViewState="true" >
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem, "Question") %>
</ItemTemplate>
</asp:Repeater>
However, I want want to dynamically add the controls but in doing this i cant get the selectedvalues when submitting. This is tha main structure of my code...
<html>
<asp:Repeater ID="rptSurvey" runat="server" Visible="true">
</asp:Repeater>
<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click" />
</html>
protected void Page_Load(object sender, EventArgs e)
{
...
if (!IsPostBack)
{
rptSurvey.DataSource = GetQuestions();
rptSurvey.DataBind();
}
...
}
protected void rptSurvey_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
string question = (DataBinder.Eval(e.Item.DataItem, "Question")).ToString();
litQuestion = new Literal();
litQuestion.Text = question;
RadioButtonList rblAnswer = (RadioButtonList)item;
rptSurvey.Controls.Add(rblAnswer);
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
...
Dictionary<int, string> answers = new Dictionary<int, string>();
try
{
var list = FindControls(rptSurvey, c => c is RadioButtonList || c is CheckBoxList || c is TextBox);
foreach (Control item in list)
{
QuestionId = int.Parse(Questions.Rows[list.IndexOf(item)][0].ToString());
if (item is TextBox)
{
TextBox txtAnswer = (TextBox)item;
answers.Add(QuestionId, txtAnswer.Text);
}
else if (item is RadioButtonList)
{
RadioButtonList rblAnswer = (RadioButtonList)item;
answers.Add(QuestionId, rblAnswer.SelectedItem.Text);
}
else if (item is CheckBoxList)
{
// Iterate through the Items collection of the CheckBoxList
string cblMultiAnswer = "";
for (int i = 0; i < cblAnswer.Items.Count; i++)
{
if (cblAnswer.Items[i].Selected)
{
cblMultiAnswer += cblAnswer.Items[i].Value + ",";
}
}
answers.Add(QuestionId, cblMultiAnswer);
}
}
bSurvey.BLInsertSurveyAnswers(answers, dateCreated, _userEmail);
}
}
public static List<Control> FindControls(Control parent, Predicate<Control> match)
{
var list = new List<Control>();
foreach (Control ctl in parent.Controls)
{
if (match(ctl))
list.Add(ctl);
list.AddRange(FindControls(ctl, match));
}
return list;
}
you have to create the control tree first (always - not only on non-postbacks). do it in the oninit or onpreload event.
look here: https://web.archive.org/web/20211020131055/https://www.4guysfromrolla.com/articles/081402-1.aspx

Resources