I was hoping someone could provide some advice on what I am doing wrong here. I am attempting to dynamically add a series of DropDownList controls that will be populated from the database. I have many other DropDownLists added at design time that are populating from the database and maintaining state across postbacks.
I am only experiencing issues with the dynamically added controls. The issue is that the DropDown control is populating from the database when initially created and is returning empty on postback. (I have set autopostback to true for the DropDownList control so that postback occurs when selected item changes.)
I have read a number of posts and attempted a variety of strategies and nothing has worked so far.
Here is the relevant code:
I have a placeholder to attach the control to.
<asp:PlaceHolder ID="DynamicControlsHolder" runat="server" />
I use a session variable to hold the number of controls (for my test I am only adding 1 but will need to add several)
public int NumberOfControls
{
get { return (int)ViewState["NumControls"]; }
set { ViewState["NumControls"] = value; }
}
I call two different methods depending upon whether it is a postback or not.
if (!IsPostBack)
{
this.NumberOfControls = 0;
addSomeControls();
}
else
{
this.createControls();
}
protected void addSomeControls() //done in original page_load so bind data
{
DropDownList dl = new DropDownList();
//not using the following event handler yet, so I didn't post the code
dl.SelectedIndexChanged += new EventHandler(dlOnSelectedIndexChange);
using (I.DBEntities1 ctx = new I.IDBEntities1())
{
dl.EnableViewState = true;
dl.AutoPostBack = true;
dl.ID = "ControlID_" + NumberOfControls.ToString();
dl.DataSource = ctx.Lk_PersonPersonAssocType.OrderBy(c => c.Assoc_Type);
dl.DataValueField = "ID";
dl.DataTextField = "Assoc_Type";
dl.DataBind();
}
DynamicControlsHolder.Controls.Add(dl);
this.NumberOfControls++;
}
protected void createControls() //postback
{
int count = this.NumberOfControls;
for (int i = 0; i < count; i++)
{
DropDownList dl = new DropDownList();
dl.ID = "ControlID_" + NumberOfControls.ToString();
DynamicControlsHolder.Controls.Add(dl);
}
}
Any assistance would be greatly appreciated.
I now have things working and wanted to post the answer in the event this it will assist others. #dricdar, thanks for your help. Aspects of your feedback assist in solving the problem.
I moved the call to createControls() into OnInit.
I changed NumberOfControls to a session variable:
public int NumberOfControls
{
get { return (int)Session["NumControls"]; }
set { Session["NumControls"] = value; }
}
In OnInit, onPostBack I call createControls().
In Page_Load, !isPostBack, I set NumberOfControls to zero and call addSomeControls();
The methods now look as follows;
protected void addSomeControls() //done in original page_load so bind data
{
DropDownList dl = new DropDownList();
dl.SelectedIndexChanged += new EventHandler(dlOnSelectedIndexChange);
dl.EnableViewState = true;
dl.AutoPostBack = true;
this.NumberOfControls++;
dl.ID = "ControlID_" + this.NumberOfControls.ToString();
using (I.DBEntities1 ctx = new I.DBEntities1())
{
dl.DataSource = ctx.Lk_PersonPersonAssocType.OrderBy(c => c.Assoc_Type);
dl.DataValueField = "ID";
dl.DataTextField = "Assoc_Type";
dl.DataBind();
}
DynamicControlsHolder.Controls.Add(dl);
DynamicControlsHolder.EnableViewState = true;
}
protected void createControls() //postback, rely upon viewstate here
{
int count = this.NumberOfControls;
for (int i = 0; i < count; i++)
{
DropDownList dl = new DropDownList();
dl.SelectedIndexChanged += new EventHandler(dlOnSelectedIndexChange);
dl.AutoPostBack = true;
using (I.DBEntities1 ctx = new I.DBEntities1())
{
dl.DataSource = ctx.Lk_PersonPersonAssocType.OrderBy(c => c.Assoc_Type);
dl.DataValueField = "ID";
dl.DataTextField = "Assoc_Type";
dl.DataBind();
}
dl.ID = "ControlID_" + NumberOfControls.ToString();
DynamicControlsHolder.Controls.Add(dl);
}
}
Related
I am stuck in a problem I am creating a 100 question online test. Every question has four radio buttons (options) I want to know that when I select the options How can I make the browser to remember selected values of each question when I press submit button. I want to get values on submit button? I am using two properties liek "CurrentPageNo" and "IncrementCount" to set/get its value using viewstate.
Here is my code:
public void NavigateRecords()
{
//CurrentPageNo = 1;
PagedDataSource pds = new PagedDataSource();
pds.DataSource = dt.DefaultView;
pds.AllowPaging = true;
pds.PageSize = 1;
// Set the PagedDataSource's current page
pds.CurrentPageIndex = CurrentPageNo;
lblCurrentPage.Text = "Page No: " + (CurrentPageNo + 1).ToString() +
" of "
+ pds.PageCount.ToString();
btnPrev.Enabled = !pds.IsFirstPage;
btnNext.Enabled = !pds.IsLastPage;
btnFirst.Enabled = !pds.IsFirstPage;
btnLast.Enabled = !pds.IsLastPage;
btnSubmit.Visible = pds.IsLastPage;
repeaterItems.DataSource = pds;
repeaterItems.DataBind();
SelectedValue = rblOptions.SelectedValue;
Dictionary<string, string> values = new Dictionary<string, string>();
for (int j = 3; j < 7; j++)
{
values.Add(dt.Rows[IncrementCount][j].ToString(), dt.Columns[j].ToString());
}
//var options = repeaterItemsFindControl("rblOptions") as RadioButtonList;
//options = new RadioButtonList();
rblOptions.DataSource = values;
rblOptions.DataTextField = "Key";
rblOptions.DataValueField = "Value";
rblOptions.DataBind();
lblMsg.Text = rblOptions.SelectedValue;
}
You need to create a Session to store your answers. So create a List or class to put in that session.
Then on page load check if the session exists. If it does cast the session to it's original type.
List<string> Questions;
protected void Page_Load(object sender, EventArgs e)
{
if (Session["questions"] != null)
{
Questions = Session["questions"] as List<string>;
}
else
{
Questions = new List<string>();
Session["questions"] = Questions;
}
}
Now you can add an answer to that session on a Button Click.
protected void Button1_Click(object sender, EventArgs e)
{
Questions.Add("Answer 1");
Label1.Text = "There are " + Questions.Count() + " answers";
}
If you keep pressing Button1 you will see that the amount of answers increases.
I have dynamic drop down lists that are created based on what's selected in the list box.. When clicking confirm this is when the drop down lists are created. Clicking save is where I attempt to retrieve the values. However I am unable to retrieve that values that are in the drop down lists.
Code:
protected void btnConfirm_Click(object sender, EventArgs e)
{
int ID = 0;
foreach (string value in values)
{
MyStaticValues.alEdit.Add(value);
CreateEditForm(value, ID);
ID += 1;
}
if (values.count != 0)
{
btnSave.Visible = true;
btnConfirm.Enabled = false;
}
}//End of btnConfirm_Click
protected void CreateEditForm(string Value, int ID)
{//Creates an edit form for the value inserted.
string name = value;
//This part adds a header
phEditInventory.Controls.Add(new LiteralControl("<h2>" + name + "</h2>"));
phEditInventory.Controls.Add(new LiteralControl("<div class=\"clearfix\"></div>"));
//Create a label
Label lblName = new Label();
lblName.Text = "Name";
lblName.ID = "lblName" + ID;
lblName.CssClass = "control-label";
//Create a Drop Down List
DropDownList ddlName = new DropDownList();
ddlName.ID = "ddlName" + ID;
ddlName.CssClass = "form-control";
//Set default N/A Values For Drop Down List
ddlName.Items.Add(new ListItem("N/A", Convert.ToString("0")));
//The Rest of the Values are populated with the database
//Adds the controls to the placeholder
phEditInventory.Controls.Add(lblName);
phEditInventory.Controls.Add(ddlName);
phEditInventory.Controls.Add(new LiteralControl("<div class=\"clearfix\"></div>"));
} //End of CreateEditForm
protected void btnSave_Click(object sender, EventArgs e)
{
string name = "";
try
{
for (int i = 0; i < MyStaticValues.alEdit.Count; i++)
{
string nameID = "ddlName" + i.ToString();
DropDownList ddlName = (DropDownList)phEditInventory.FindControl(nameID);
name = ddlName.SelectedValue.ToString();
}
}
catch (Exception ex)
{
}
phEditInventory.Visible = false;
btnSave.Visible = false;
MyStaticValues.alEdit.Clear();
}//End of btnSave_Click Function
Your problem is that the dynamically created dropdown lists are not maintained on postback. When you click the Save button, a postback occurs, and the page is re-rendered without the dynamically created dropdowns. This link may help.
Maintain the state of dynamically added user control on postback?
I am using Gridview with AutoGenerateColumns="True", so gridview columns are generated dynamically. Now in case of edit, I am adding dropdownlist dynamically for one of the field in the gridview. Please see following code:
protected void grdViewConfig_RowEditing(object sender, GridViewEditEventArgs e)
{
grdViewConfig.EditIndex = e.NewEditIndex;
BindGridView();
clientBAL = new TMIWsBALClient();
var lstAppIds = clientBAL.GetDistinctApplicationIds();
GridViewRow grdRow = grdViewConfig.Rows[e.NewEditIndex];
for (int i = 0; i < grdRow.Cells.Count; i++)
{
if (grdRow.Cells[i].GetType().Equals(typeof(DataControlFieldCell)))
{
DataControlFieldCell dcField = (DataControlFieldCell )grdRow.Cells[i];
if (dcField.ContainingField.HeaderText.ToLower().Equals("applicationid"))
{
DropDownList drpDwnAppIds = new DropDownList();
drpDwnAppIds.ID = "drpDwnAppIds";
drpDwnAppIds.DataSource = lstAppIds;
drpDwnAppIds.DataBind();
var tb = dcField.GetAllControlsOfType<TextBox>(); ;// grdRow.Cells[i].GetAllControlsOfType<TextBox>();
TextBox firstTb = (TextBox)tb.First();
foreach (ListItem lstItem in drpDwnAppIds.Items)
{
if (firstTb.Text.Equals(lstItem.Text, StringComparison.CurrentCultureIgnoreCase))
{
lstItem.Selected = true;
}
}
dcField.Controls.Remove(firstTb);
dcField.Controls.Add(drpDwnAppIds);
}
}
}
}
Now in Gridview_RowUpdating event, I am trying to fetch the dropdownlist in similar way, but I am unable to get it. GetAllControlsOfType() is an extension method, which will return all the child controls under selected parent. In this case, parent is gridview cell and child control is dropdownlist. But it is returning null.
protected void grdViewConfig_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
strTableName = txtTable.Text.Trim();
string strAppId;
GridViewRow grdRow = grdViewConfig.Rows[grdViewConfig.EditIndex];
for (int i = 0; i < grdRow.Cells.Count; i++)
{
if (grdRow.Cells[i].GetType().Equals(typeof(DataControlFieldCell)))
{
DataControlFieldCell dcField = (DataControlFieldCell)grdRow.Cells[i];
if (dcField.ContainingField.HeaderText.ToLower().Equals("applicationid"))
{
var drpDwn = dcField.GetAllControlsOfType<DropDownList>();
DropDownList drpDwnAppIds = (DropDownList)drpDwn.First();
strAppId = drpDwnAppIds.SelectedValue;
}
}
}
}
What am I missing? Please help. Also let me know if more information is needed.
Thank you in advance.
Dynamically generated controls need to be recreated on every postback. In your case the DropDownList controls you created no longer exist when you hit the grdViewConfig_RowUpdating handler.
Generally in this sort of case you would set AutoGenerateColumns to false and manually define your columns which would allow you to define a TemplateField which contains an ItemTemplate for read only mode and an EditItemTemplate for edit mode which could then contain your DropDownList.
I have XtraTabControl with two pages, both of them has one LookUpEdit,
when page load which on the secondpage does not work,
void Frm1_Load(object sender, EventArgs e)
{
lookUpEditA.Properties.DataSource = datasource. . . . .
lookUpEditA.Properties.ValueMember = "ID";
lookUpEditA.Properties.DisplayMember = "xxxx";
lookUpEditA.Properties.PopulateColumns();
lookUpEditA.Properties.Columns["ID"].Visible = false;
lookUpEditB.Properties.DataSource = datasource. . . . .
lookUpEditB.Properties.ValueMember = "ID";
lookUpEditB.Properties.DisplayMember = "xxxx";
lookUpEditB.Properties.PopulateColumns();
lookUpEditB.Properties.Columns["ID"].Visible = false;
}
I can see the issue only with setting visibility of 'ID' column on second LookUpEdit.
The reason of this issue is that the LookUpEdit can't operate with datasource representation (perform populating columns, operating with column's visibility and etc.) until it's handle been created. The second LookUpEdit will create it's handle only when the second tab page has been shown.
To avoid the issue you can use the following approach:
if(!lookUpEditB.IsHandleCreated)
lookUpEditB.HandleCreated += lookUpEditB_HandleCreated;
else InitLookUpEditDataSource();
//...
void lookUpEditB_HandleCreated(object sender, EventArgs e) {
lookUpEditB.HandleCreated -= lookUpEditB_HandleCreated;
InitLookUpEditDataSource();
}
void InitLookUpEditDataSource() {
lookUpEditB.Properties.DataSource = this.categoriesBindingSource;
lookUpEditB.Properties.DisplayMember = "CategoryName";
lookUpEditB.Properties.ValueMember = "CategoryID";
lookUpEditB.Properties.PopulateColumns();
lookUpEditB.Properties.Columns["CategoryID"].Visible = false;
}
As #DmitryG said, you can not use lookUpEditB.Properties.PopulateColumns() statement until control's UI handlers are not created.
As per my understanding these are created only when the second tab page shown. Except creating conditional statement to create handlers etc, you can use the XtraTabControl.SelectedPageChanged Event where you can bind the data source of the lookUpEditB by checking condition that XtraTabControl.SelectedTabPage Property is set with Page2 which contain the lookUpEditB.
Check the tested Code Snippet below:
public partial class TabControlTest : Form
{
List<Category> dataSource = new List<Category>();
public TabControlTest()
{
InitializeComponent();
for (int i = 0; i < 10; i++)
{
dataSource.Add(new Category { ID = i + 1, Name = "Category" + (i + 1) });
}
}
private void TabControlTest_Load(object sender, EventArgs e)
{
lookUpEditA.Properties.DataSource = dataSource;
lookUpEditA.Properties.ValueMember = "ID";
lookUpEditA.Properties.DisplayMember = "Name";
lookUpEditA.Properties.PopulateColumns();
lookUpEditA.Properties.Columns["ID"].Visible = false;
}
private void xtraTabControl1_SelectedPageChanged(object sender, DevExpress.XtraTab.TabPageChangedEventArgs e)
{
if (xtraTabControl1.SelectedTabPage == xtraTabPage2)
{
lookUpEditB.Properties.DataSource = dataSource;
lookUpEditB.Properties.ValueMember = "ID";
lookUpEditB.Properties.DisplayMember = "Name";
lookUpEditB.Properties.PopulateColumns();
lookUpEditB.Properties.Columns["ID"].Visible = false;
}
}
}
Hope this help.
I have this code which adds a x number of views to a Multiview control. The Multiview control exist on the page with only one view. on page init I create x number of views with a GridView control added to each of these views when I come to loop through to find which one I want to show the Multiview control says it has only 1 view.
protected void variantRepeat_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandSource.GetType() == typeof(LinkButton))
{
string theID = ((LinkButton)e.CommandSource).CommandArgument.ToString();
ViewCollection views = prodView.Views; //this has only 1 view the one that has been added on the source view of the .aspx page
foreach (View toDisplay in views)
{
if (toDisplay.ID == theID)
prodView.SetActiveView(toDisplay);
}
}
}
The code above loops to find the view and display it. Why do all the views disappear, I've stepped through and at init and after it has finished processing database queries it has x number of views > the 1 in source view.
Why does this control "lose" the views?
Init code:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString == null || Request.QueryString.Count < 1)
Response.Redirect(Server.MapPath("~/Packages/"));
if (!Page.IsPostBack)
{
if (Request.QueryString["id"].ToString() == string.Empty)
Response.Redirect(Server.MapPath("~/Packages/"));
var id = Server.HtmlEncode(Request.QueryString["id"].ToString());
Guid packID = Guid.Parse(id);
using (var context = new GaleEntities())
{
var thePackage = context.PackageEnts.First(p => p.packageID == packID);
var theVariants = thePackage.Variants;
var mydatasource = new List<PackageEnt> { thePackage };
var myVariantDataSource = new List<Variant>();
foreach (Variant single in theVariants)
{
myVariantDataSource.Add(single);
}
packageForm.DataSource = mydatasource;
variantRepeat.DataSource = myVariantDataSource;
RenderProductGridviews(theVariants);
prodView.SetActiveView(prodView.Views[0]);
}
packageForm.DataBind();
variantRepeat.DataBind();
Page.DataBind();
}
}
protected void RenderProductGridviews(System.Data.Objects.DataClasses.EntityCollection<Variant> variantCol)
{
foreach (Variant packVar in variantCol)
{
View newView = new View();
GridView prodGrid = new GridView();
var myProdDataSource = new List<vw_VariantProduct>();
using (var context = new GaleEntities())
{
var singleProd1 = context.vw_VariantProduct.Where(vp => vp.variantID == packVar.variantID);
foreach (vw_VariantProduct extProd in singleProd1)
{
myProdDataSource.Add(extProd);
}
}
prodGrid.DataSource = myProdDataSource;
BoundField _column = new BoundField();
_column.DataField = "product_title";
_column.HeaderText = "Product";
prodGrid.Columns.Add(_column);
_column = new BoundField();
_column.DataField = "product_title";
_column.HeaderText = "Product";
prodGrid.Columns.Add(_column);
_column = new BoundField();
_column.DataField = "product_desc";
_column.HeaderText = "Description";
prodGrid.Columns.Add(_column);
_column = new BoundField();
_column.DataField = "product_time";
_column.HeaderText = "Time (mins)";
prodGrid.Columns.Add(_column);
_column = new BoundField();
_column.DataField = "quantity";
_column.HeaderText = "Quantity";
prodGrid.Columns.Add(_column);
prodGrid.DataBind();
newView.ID = packVar.variantID.ToString();
newView.Controls.Add(prodGrid);
prodView.Views.Add(newView);
}
}
Now, as usual almost all questions with the dynamic controls in ASP.Net words share the same common problem...
You need to create the dynamic controls on every post, you are only creating them the first time the page is loaded. Follow this simple rules:
Dynamic controls should be created in the PreInit event when you are not working with a master page, if you are, then create the controls in the Init event
Avoid setting properties that can be changed in each post in these events because when the view state is applied (in a post event) the properties will be overridden
Dynamic controls must be created every time the page is posted, avoid this if(!this.IsPostBack) this.CreatemyDynamicControls();
When you create the controls in the PreInit or Init events, their states will be automatically set in a post event, which means in the LoadComplete event your controls will contain their state back even when you create them again in each post and even when you did not explicitly set their state. Note this behavior is different when you are dealing with controls created at design time, in that case, the event where the state has been set is the Load event
Event subscription should occur before the PageLoadComplete or they will not be raised
I have posted several answers about dynamic controls:
https://stackoverflow.com/a/11127064/1268570
https://stackoverflow.com/a/11167765/1268570
Click events on Array of buttons
Button array disappears after click event
Dynamically create an ImageButton
This piece of code creates dynamic controls on-demand, and each control keeps its state on each post
public partial class DynamicControlsOnDemand : System.Web.UI.Page
{
public int NumberOfControls
{
get
{
if (this.ViewState["d"] == null)
{
return 0;
}
return int.Parse(this.ViewState["d"].ToString());
}
set
{
this.ViewState["d"] = value;
}
}
protected void Page_PreLoad(object sender, EventArgs e)
{
this.CreateDynamicControls();
}
protected void addControl_Click(object sender, EventArgs e)
{
this.NumberOfControls = this.NumberOfControls + 1;
this.myPanel.Controls.Add(this.CreateTextbox(this.NumberOfControls));
}
private void CreateDynamicControls()
{
for (int i = 0; i < this.NumberOfControls; i++)
{
var t = this.CreateTextbox(i + 1);
t.TextChanged += (x, y) => this.lblMessage.Text += "<br/>" + (x as TextBox).ID + " " + (x as TextBox).Text;
this.myPanel.Controls.Add(t);
}
}
private TextBox CreateTextbox(int index)
{
var t = new TextBox { ID = "myTextbox" + index.ToString(), Text = "de" };
return t;
}
}
ASPX
<asp:Panel runat="server" ID="myPanel">
</asp:Panel>
<asp:Button Text="Add Control" runat="server" ID="addControl" OnClick="addControl_Click" />
<asp:Label ID="lblMessage" runat="server" />
Output
You first code block where you are setting the active View is based on an ItemCommand event, so I'm assuming this fires during a PostBack?
Your code which adds the additional views within Page_Load is only running if not a post back. Try removing the conditional if(!Page.IsPostBack) statement. You need to add dynamic controls on every page lifecycle.