I have a rather complex page. The ASPX page loads a user control which in turn loads a child User control.
Parent Control
protected override void OnInit(EventArgs e)
{
//Loads child control
}
In the child user control, I use custom control that inherits from System.Web.UI.HtmlControls.HtmlSelect
ASCX:
<cust:CustDropDownList id="ctlDdl" runat="server"/>
ASCX.CS
protected void Page_Load(object sender, EventArgs e)
{
//Binds CtlDdl here
}
When the user clicks on the Save button, the controls get user controls get dynamically reloaded, but Iose the value the user has selected in the dropdown. I run into the chicken and egg problem here.
I think I need to bind the ctlDdl only on if its not a postback, but that results in the dropdown not getting populated.
If I bind it everytime, then i lose user's selection
EDIT:
Can someone respond to my comment to Jonathan's answer? Thanks
With custom controls, you have to manage the state. There is a sort of bubble-up effect where the state is passed along. If you don't deal with it, you don't get state.
This link will get you started: Server Control Custom State Management
Look for
Protected Overrides Sub LoadViewState( _
ByVal savedState As Object)
Dim p As Pair = TryCast(savedState, Pair)
If p IsNot Nothing Then
MyBase.LoadViewState(p.First)
CType(Author, IStateManager).LoadViewState(p.Second)
Return
End If
MyBase.LoadViewState(savedState)
End Sub
at Custom Property State Management Example
Just override the OnInit method in the user control and load the custom control from that method. ASP.NET sets viewstate tracking after OnInit and before PageLoad. Therefore the defaults that are being set when you load the control are being treated as changes by the viewstate engine and are being reset on the postback.
See this article on viewstate for a detailed explanation:
http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/truly-understanding-viewstate.aspx
Related
I have a user control that I dynamically add to a Panel. I also have a couple of other functionality in the aspx page like search which is inside an update panel that causes the page to post back. From what I have read so far is that dynamic control needs to be bound on each page load. This works fine but the issue is that the user control takes a bit of time (like 3s) and thus all request operations takes longer because the user control is being bound every time.
If I load the user control inside the Page.IsPostBack condition and load it only on page load then user control is visible on post back but all the associated events in the user control is not fires.
So, Is there a way in which I can store the user control data in a session and then bind it if I know that the data is not going to change and hence reducing the 3s delay to load the user control.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadUserControl(); // Only loaded on page load.Faster, but user control
//functionalities break on post back. Maybe
// because it is not loaded again.
}
//LoadUserControl(); // User control loads fine. But even a totally unrelated
// post back causes the already loaded User Control to load
// again. 3s delay :(
}
The method used to load the user control is
private void LoadUserControl()
{
var control = LoadControl("A Dynamic ascx page");
control.ID = "contentControl";
panel1.Controls.Clear(); //panel1 is the placeholder in aspx page
panel1.Controls.Add(control);
Session["LoadedControls"] = control;
}
So I tried to save the loaded control in session and add in to the placeholder panel as such
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadUserControl(); // Only loaded on page load.Faster, but user control
//functionalities break on post back. Maybe
// because it is not loaded again.
}
if ((Control)Session["LoadedControls"] != null)
{
panel1.Controls.Clear();
panel1.Controls.Add((Control)Session["LoadedControls"]);
}
}
this does not work as expected either. I do not get any of the data present in the user control.
The user control has 2 RadGrid and 2 RadHTMLChart. I did not want to add it since this is already a huge post. the data for the controls in user controls is also being stored in session for binding again. But I am unable to find a way to add the user control dynamically with the bound data.
I am trying to modify a control on a page to reduce session dependence. I currently do not have access to the page itself, only the control which primarily consists of a DataGrid. I am trying to retrieve the DataGrid information on postback so I can manipulate the data and rebind the grid.
The issue is that the page is calling a databind on the control before I can retrieve the data. (actually it is calling the databind on the tab control where my control is located.) This call is happening on the OnLoad event of the page, before the OnLoad of the control is called. I saw that the is a PreLoad event that occurs after the viewstate is loaded but before the OnLoad is called. However I am having issues accessing this event from my control. Is there anyway I can access this event so I can retrieve the data before the page overwrites it?
Add the following code to your control instead of the OnLoad. (from here)
protected override void OnInit(System.EventArgs e)
{
// this assigns Page_PreLoad as the event handler
// for the PreLoad event of the Control's Page property
this.Page.PreLoad += Page_PreLoad;
base.OnInit(e);
}
private void Page_PreLoad(object sender, System.EventArgs e)
{
// do something here
}
This might help: MSDN - ASP.NET Page Life Cycle Overview
The image 1/3 of the way down sums it up.
I understand the order the events occur with page life cycle but it is not helping with my situation. I have a checkboxlist that is populated by a directory filled with forms. When I check a box next to the name of the form I would like it to dynamically create a wizard step and insert the form.
Order of events:
OnInit:
GatherForms() - Checks directory and loads all form names into checkbox
LoadForms() - Checks "Selected" Session and loads forms that were collected
CheckBoxList:SelectedIndexChanged #AutoPost = true#
PopulateForms() - Loops through the checkboxs and adds them to session state
When the user clicks the checkbox it does a postback and hits the OnInit which pulls from the session. The problem is that PopulateForms() was not ran yet so it populated nothing even though it is checked. If I click another item it will postback and appear. I cannot seem to be able to pull any kind of useful information from the checkbox before the refresh which means I cannot see the forms appear immediately. I have also tried looping the checkbox but unfortunately viewstate hasnt posted yet. sigh.
Any suggestions?
Thanks!
P.S: I cannot use Request.Form[] because I have to get all the selected items out of the checkbox. maybe i can but i cannot find a way :/
This is a common problem I wrestle with as I get better at ASP.NET.
With dynamic controls you have the problem of them not actually existing during OnInit.
What you can do is create all of the controls the user may see during OnInit, and hide the elements the user won't see in code. The page will load, all possible controls will be instantiated (in code - don't worry this appearing in your HTML and bloating it), then the event handler will fire, and then you can deal with setting the visibility of your wizard.
For example:
public void OnInit(object sender, EventArgs e)
{
GatherForms();
CreateWizardForm(); // creates a wizard and adds controls it will need
}
private void Checkbox_Checked(object sender, EventArgs e)
{
var checkBox = (CheckBox)sender;
// append checkBox.SelectedValue to session state object of checkboxes
}
protected override void OnPreRender(object sender, EventArgs e)
{
if (/* Session checkboxes contain values */)
{
this.WizardForm.Visible = true;
this.CheckboxList.Visible = false;
}
}
This works provided you know ahead of time which controls will be in the wizard form. You can change the values of those controls in the OnPreRender event, toggle their visibility, etc, but you can't go and add new controls (e.g. Controls.Add(new Button())) - that has to be done in the OnInit event.
Only add your dynamic controls in OnInit. Don't do any population/processing until PageLoad. You will retain your values this way.
A classic pitfall with dynamic controls.
Honestly the best solution is to just wield the Request values array to look for the information you need and ignore the lifecycle stuff for this particular situation.
In OnInit(), the posted values are there, they just haven't been dealt with by ViewState yet. So you can just do if (Request["myCheckBoxName"] == xxx) to deal with individual checkboxes, or if you can use Request["__EVENTTARGET"] to get the name of the control that caused the postback.
See my old answer here for a more thorough discussion of the issue.
I have a piece of code in a User controls that normally should be put in the Page_Load (initializes other components such DropDowns etc.) but I need this to happen before the Page_Load of the page that hosts this control.
I tried to put this in Page_Init:
protected void Page_Init(object sender, EventArgs e)
{
if (!IsPostBack)
{
Methods.PopulateWhatList(cboWhatList0, cboWhatList1, fldWhat, Request["WhatId"], true);
Methods.PopulateWhereList(cboWhereList0, cboWhereList1, fldWhere, Request["WhereId"], true);
Methods.PopulateWhoList(cboWho, true, Request["WhoId"]);
Methods.PopulateWhenList(cboWhen, true, Request["WhenId"]);
Methods.PopulatePriceRangeList(cboPriceRange, true, Request["PriceRangeId"]);
}
}
...but have experienced some problems. So where is the best place to but this type of code?
The problem I'm having (and might be unrelated) is that my:
protected override void Render(HtmlTextWriter writer)
{
Methods.EnableValidationWhereList(cboWhereList1, this.Page);
Methods.EnableValidationWhatList(cboWhatList1, this.Page);
base.Render(writer);
}
Isn't called on certain postbacks? (When pressing a LinkButton?)
I'll try a wild guess to what you're trying to do and suggest a solution:
In your Page_Init you're populating the contents of various controls on the page. You're dependent on URL parameters, hence the if(!IsPostBack) clause.
After Page_Init, some of your controls are left in a disabled state, hence the need to enable them in your Render method.
When doing a postback on the LinkButton, you don't see your dropdowns populated on the next page rendering.
What you're experiencing is that disabled controls doesn't get persisted to the ViewState. Since the SaveViewState is called before Render, you're enabling the controls too late in the page lifecycle.
If you instead move your Methods.EnableValidation... calls to a Pre_Render method on your page, control state will get persisted to the ViewState.
After that fix, you should move your code in the Page_Init method to the Page_Load method, where it belongs. That way your controls data will have been loaded from the ViewState if you're on a postback.
The page lifecycle is such that the page_load of the page runs before the page_load of the controls.
If you need to initialise your data sources prior to page load then you can put that initialisation in OnInit provided you ensure the base.OnInit() is called first. Ideally, you should keep your databind calls to the page_load mechanism to ensure you don't have issues with viewstate.
Regarding your render method. What purpose do the 'enable' methods serve in the overall page lifecycle?
Basically, I have a drop down list and a dynamically added user control. The user control loads a grid view depending on the choice that was made in the drop down list. The drop down list is not part of the user control.
Now, the question is, how do i simulate (isControlPostback = false) every time the user changes the selection in the drop down list? It looks like ViewState remembers the control.
Inside my user control I have:
protected bool IsUserControlPostBack
{
get
{
return this.ViewState["IsUserControlPostBack"] != null;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsUserControlPostBack)
{
ViewState.Add("IsUserControlPostBack", true);
//load stuff in the grid view and bind it
}
}
When the user changes the selection on the drop down list, i have a javascript confirm box, and the page posts back. So OnSelectedIndexChanged event for drop down list doesn't get triggered. I would like to remove to do something like this every time the selected index changes:
ViewState.Remove("IsUserControlPostBack");
You can make changes to the control in prerender event. When this event is fired all other actions are made.
Or you can do public property in user control and when setting required to value react on appropriately.
The ViewState you access in your user control is not the same one you access on the page. If you need your page to communicate with your user control, I suggest you add a public method on your user control for this purpose.
If, for some reason, you prefer
something similar to your ViewState
approach, you can try Context.Items.
Note that Context.Items is not
preserved between requests.
Add the control to the page sometime before OnLoad. E.g. OnInit. Between OnInit and OnLoad, the viewstate is loaded and postback events are run.
For anyone who is interested to know the answer:
I ended up implementing a public property inside user control and load the control inside the server drop down list SelectedIndexChanged event rather than OnInit. This eliminated the need for explicit Viewstate use.