Web forms UserControls - LoadControl in pre-init or page_load? - asp.net

In the MSDN page lifecycle reference it states that the pre-init is used to "Create or re-create dynamic controls."
However, elsewhere on MSDN, an example implies that a dynamic user control should be loaded in Page_Load
Is this a contradiction? Or is pre-init used only for standard aspx controls?
What have I missed :)
Edit:
Either way works, however, there is presumably some benefit of one approach over the other.
(WebUserControl1 is a UserControl with a simple label property, SomeProperty)
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
WebUserControl1 wc1 = LoadControl(#"~\WebUserControl1.ascx") as WebUserControl1;
wc1.SomeProperty = "Hello World";
Controls.Add(wc1);
}
protected void Page_Load(object sender, EventArgs e)
{
WebUserControl1 wc1 = LoadControl(#"~\WebUserControl1.ascx") as WebUserControl1;
wc1.SomeProperty = "Hello World";
Controls.Add(wc1);
}

What exactly are you trying to do? Are you trying to pass information to the user control? If so you can pass the information to the user control in the page_load event from the parent page.

The answer is it depends on your custom control. If your user control doesn't need anything to happen before page_load, then you can go ahead and add your control during page load, if you have code in your user control that executes at an earlier time, then you should add your control at an earlier stage as the MSDN article suggests.

Related

asp.net dynamic user control button click issue

I made researching about this subject I could not find proper answer.
In my default.aspx page, I have a treeview. Codes are in default.aspx like below:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
Control ucont;
if (TreeView1.SelectedNode.Value == "Yeni Dönem")
{
ucont = LoadControl("usercontrols/yenidonem.ascx");
PlaceHolder1.Controls.Add(ucont);
}
else
{
ucont = LoadControl("usercontrols/tabloktar.ascx");
PlaceHolder1.Controls.Add(ucont);
}
}
I load user controls dnynmicaly. User controls are have button control. I can not fire user control's button click when I load it dynamcally. How can I solve this ?
Thanks.
First of all, I would not recommend adding control dynamically later than in Page_Load event. Other things to remember is that You should add it on each page load and assign unique ID value the control that does not change between postbacks.
In this case, the easiest way would be to always add both controls to the page and show appropriate one using Visibility property.
If that's not suitable for You, try to move the code from TreeView1_SelectedNodeChanged to the Page_Load event and load appropriate control on each postback until it should be changed to another one.
I haven't tested this, so if You have any issues when using thise answer, let me know in the comments and I'll try to help.

Events between an ASPX and ASCX

i'm a beginner in .NET, and search since yesterday morning to resolve my problem without finding the solution.
Here is my problem :
I create dynamically some User Controls by this way, because I need to give parameters :
List<ANNOUNCEMENT> listAnnouncement = getAnnoucements();
foreach(ANNOUNCEMENT ann in listAnnouncement)
{
if(ann.IS_CURRENT_ANNOUNCEMENT && currentAnnouncement == null)
{
currentAnnouncement = ann;
}
List<Object> listParams = new List<Object>();
listParams.Add(ann);
AnnouncementPresentation ap = (AnnouncementPresentation)(Controller.LoadControl(Page, "~/UserControls/AnnouncementPresentation.ascx", listParams.ToArray()));
/* important for the end of the method */
ap.modifyAnnouncementButtonClick += new EventHandler(modifyAnnouncementButtonClick);
pnl_announcements.Controls.Add(ap);
}
In this ASCX, I have a button, and when user will click on it, I want to call a method contained in my ASPX, so I do this in the ASCX :
public event EventHandler modifyAnnouncementButtonClick;
protected void btn_modify_announcement_Click(object sender, EventArgs e)
{
PageAdminAnnonces.currentAnnouncement = annonce;
modifyAnnouncementButtonClick(sender, e);
}
And this in the ASPX :
protected void modifyAnnouncementButtonClick(object sender, EventArgs e)
{
initListOfAnnouncement();
lbl_errors.Text = currentAnnouncement.TITLE;
}
I think everything works, but there is the problem : It works once, and at the end of the method, I delete my ASCX as you can see, and create new ASCX. But they don't have the methods, and when I click again, nothing works, so the ASPX is reloaded. After reloading, it works again.
Do i do something wrong?
According to the information in the comments, I suppose that your solution does not work because you are recreating the controls in the Click event handling method, which is very late in the page's lifecycle and should not be used for adding controls.
As mentioned in the comments, I suggest you to create the controls in Page_Init or Page_Load and not recreate them in the button's Click handling method. You should also assign a unique ID to each of them. Then, in the Click handler, you can use FindControl method to acces the created controls. Alternatively you can just save the references to the controls upon creation, so you can access them later easily.
Useful links:
http://msdn.microsoft.com/en-us/library/ms178472.aspx
http://visualstudiomagazine.com/articles/2010/10/11/more-on-adding-controls-dynamically.aspx

ASP.NET Login Controls, page postback and rendering on logout

I'm a bit puzzled by the behavior of the default ASP.NET Authentication controls, by its lifecycle to be precise.
In my MasterPage, I added a LoginView Control which displays the nice [Login] or [Logout] links. When I am logged in and click on [logout], I set up the control to perform a redirection to the homepage of the application.
Internally, when a click on "logout" happens, a postback is triggered. The following steps happen (among others of course):
The page that fired the postback is reinitialized
The page that fired the postback is reloaded
The LoggingOut event is fired
The LoggedOut event is fired
The page that fired the postback is PreRendered
The redirection happens
The target page is loaded (LoggedOut.aspx in my case)
On most of the pages, this works fine. But some pages expect some data to be initialized for their rendering to happens correctly. When this loggout postback occurs, the data isn't correctly initialized, but the page is still PreRendered which leads to some... "unexpected behavior" >_<
My question is thus twofold:
Why does this rendering step happens since the page won't be displayed at all?
Is there a way to prevent the rendering to happen?
Thanks a lot.
Tim
PS: here's a small VS2010 sample project showing you the call sequence & page lifecycle if you want to try it out for yourself http://dl.dropbox.com/u/11764136/LoginTest.7z
There is a way to prevent the actual rendering of the page.
Stop processing the current request when you redirect the page. This can be done by giving a true parameter to the Response.Redirect method:
Response.Redirect("http://somewhere", true);
You can also do this manually by calling Response.Close();
Are you using if(!isPostBack) test to control what should be rendered/re-initialized and what shouldn't?
Venemo's answer gave me an idea that seems to be working.
Instead of relying on the LoginStatus component to perform the redirection, I registered the MasterPage hosting the LoginStatus components to the LoginStatus.LoggedOut event and fire the redirection "per hand" before the PreRender step can be called.
protected void Page_Load(object sender, EventArgs e)
{
MasterLoginStatus.LoggedOut += new EventHandler(OnUserLoggedOut);
}
private void OnUserLoggedOut(object sender, EventArgs e)
{
Response.Redirect("~/LoggedOut.aspx", true);
}
I was concerned the LoginStatus component might remain dirty by doing this but sofar I haven't found any issue with it e.g. "works until proven otherwise".
Remains the question of "why the component behaves like this" but I guess it was a design decision that will remain unanswered.
Edit: this works fine until you get the same problem for the "login" action. Haven't found a way around this one yet.
I've got a serious problem with a DevExpress control (The report DocumentMap) which sometimes requests the whole report, bypassing the caching mechanism, when the end-user clicks on the logout link on the LoginStatus control. I've tried a lot of approaches to stop dead the "logout" postback so that the report won't get generated (some reports took 5 minutes to render, so the logout action sometimes took that long). I think this is similar to your problem: you don't want to do any heavy processing if the user is logging out. So I've tried a different approach: why didn't I recognize that the postback is indeed a logging out postback? All my pages inherit from a base page, so I've set this code in the base page:
public bool IsLoggingOut { get; private set; }
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
var eventTarget = Request.Params.Get("__EVENTTARGET");
IsLoggingOut = eventTarget != null && eventTarget.Contains("HeadLoginView$HeadLoginStatus");
}
Now all I need to do in my page is to surround any heavy processing with a test of !IsLoggingOut... You could even do you redirect to the LoggedOut page without having to handle any events, just like this:
protected override void OnLoad(EventArgs e)
{
if (IsLoggingOut)
Response.Redirect("~/LoggedOut.aspx", true);
}
Even if you prefer to use the event handler to do that redirect, being able to know that the postback is indeed due to a logout click is a nice thing!
In my case, I was having this problem with the LoginStatus control. I can't see why it is a useful design to post back and Render the page when the user has clicked "logout". Through some tests, I found that I had to let the page go through its entire lifecycle, so Reponse.End() and Response.Transfer() did not work.
My solution was to add event handlers for the LoginStatus LoggedOut event, and then override the Render() method in the master page to do nothing if the user has logged out. I actually had the LoginStatus nested inside a user control that was then in the master page, so I had to bubble the event.
In my user control containing the LoginStatus control, I added an event handler for the LoggedOut event. In the UserStatus.aspx file:
<asp:LoginStatus runat="server" ID="loginStatusDefault" OnLoggedOut="loginStatusDefault_LoggedOut" ... />
Then in the code-behind:
public event EventHandler LoggedOut;
protected void loginStatusDefault_LoggedOut(object sender, EventArgs e)
{
if (this.LoggedOut != null)
this.LoggedOut(sender, e);
}
Now in the master page default.master, I have already included the UserStatus control:
<c:userstatus ID="ctlUserStatus" runat="server" />
and in the code-behind:
protected void Page_Init(object sender, EventArgs e)
{
ctlUserStatus.LoggedOut += ctlUserStatus_LoggedOut;
...
}
bool IsLoggedOut { get; set; }
void ctlUserStatus_LoggedOut(object sender, EventArgs e)
{
IsLoggedOut = true;
}
protected override void Render(HtmlTextWriter writer)
{
if (!IsLoggedOut)
base.Render(writer);
}
For me, the page rendering is what was bombing out when the user clicked "logout", so this took care of the problem for all pages.
What I did was have the logout link or onloggingout control just redirect to another page, "Logout.aspx" which then handles the log out code. Works great actually.
protected void LoginStatus1_LoggingOut(object sender, EventArgs e)
{
Response.Redirect("~/Logout.aspx");
}

User Control Page_Load event not reading variable passed from main page

I have problem passing a variable from a main page containing a user control to the user control itself. Although the passed variable is available generally in the code-behind of the user control the page_load event can't seem to read it.
My code -
In the main page code-behind:
protected void FindCCFsButton_Click(object sender, EventArgs e)
{
if (CustomerDropDown.SelectedIndex != 0)
{ SearchUcCCFList.SetCustID(CustomerDropDown.SelectedValue); }
}
(SearchUcCCFList is the instance of the user control in the main aspx page).
In the user control code behind:
public partial class ucCCFList : System.Web.UI.UserControl
{
public string srchCust { get; set; }
public void SetCustID(string custID)
{
srchCust = custID;
testCustLabel.Text = GetCustID(); //this works
}
public string GetCustID()
{
return srchCust;
}
protected void Page_Load(object sender, EventArgs e)
{
CCFGridView.DataSource = DAL.SearchCCFs(custID : GetCustID()); //doesn't work
CCFGridView.DataBind();
test2CustLabel.Text = GetCustID(); //doesn't work
}
In the Page_Load event GetCustId() doesn't return anything (so the records aren't filtered and all get returned) although it can be read in the methods outside the Page_Load.
I'm probably making a beginners error but any help would be appreciated.
Edit - following Alan's suggestion in the comments I stepped through the page loading sequence & it appears that the user control's Page_Load event is running BEFORE the code in the main page's button click so the variable is not yet available. The sequence after clicking the button is:
User control Page_Load runs
Code in button event on main page
Other code (outside Page_Load) in user control runs hence variable is available here.
This seems a bit weird, is there another way to pass the variable into the user controls Page_Load?
In this case, your click handling even on the main page is called after the user control page load call. Your variable is being set, but not until after your data binding in the user control.
Either switch the user control to declarative binding which will handle calling methods in the correct order for you. Or the easier fix in this case is to change the user control data binding from Page_Load to Page_PreRender, which is called later in the life cycle, after the main page click handling call.
protected void Page_PreRender(object sender, EventArgs e)
{
CCFGridView.DataSource = DAL.SearchCCFs(custID : GetCustID()); // will work now
CCFGridView.DataBind();
test2CustLabel.Text = GetCustID(); // will work now
}
For a more thorough answer, read up on the ASP.NET page life cycle including the interaction with user controls' life cycle.

No LoadComplete for a MasterPage?

With AutoEventWireUp set to false, I would normally set a Page_Finalize like this:
Include(Self.LoadComplete, Self.Page_Finalize); //Delphi
LoadComplete however, doesn't seem to be available on a MasterPage and my Page_Finalize obviously doesn't get called.
What am I meant to do to free objects in the master page?
Thanks.
LoadComplete method is simply not a member of MasterPage, but of Page.
There are several reasons for this, including the fact that the orchestrator of page life cycle is Page class itself.
It has three event-related methods: PreLoad, Load, LoadComplete. During Load, the Load event of children controls is triggered.
While a master page contains (by means of layout) the contents of the page, the page contains the MasterPage by means of objects, since the Page is the IHttpHandler that responds to HTTP requests in ASP.NET.
Briefly, you cannot override (I don't know Delphi, is that some kind of override?) OnLoadComplete in MasterPage as it's not defined. Only OnLoad
A bit late, but adding a method to the Page.LoadComplete event from the master page seems to work.
Within the master page:
protected void Page_Load(object sender, EventArgs e) {
Page.LoadComplete += Page_LoadComplete;
}
protected void Page_LoadComplete(object sender, EventArgs e) {
// do stuff on LoadComplete in the master page
}

Resources