Saturday, March 24, 2012

Invoking post back on page load (an easy question - but I couldnt find the answer!)

What I want to do is very easy, so I'm very frustated to have to ask this on the forum - but I've been searching the net for 2 days now and couldn't figure out the answer in a nice "Atlas" way of doing things.

The behaviour I want is exactly what pageflakes.com does when you go their home page: Display a "loading" message while the data is being loaded.

Icanput an UpdatePanel with my data controls in it.

Icanput an UpdateProgress control to show the "loading" message.

What Idon'tknow, is how to make it so that the client-side page will invoke a postback immidietly after loading, i.e. call the BindData() function on the server-side page.

I would prefer a declerative solution to this, or at least, a very short Javascript code (but I'm not too picky - first priority is to get the basic functionality :-) )

Little help?

Is the question unclear? or is it not as easy as I thought? Google does it, pageflakes does it, so I know it's possible...

This is what I have so far.

The javascript pageLoad() function runs properly after loading, and it does indeed call the server (and is capable of returning data to the page, I checked with js alerts)- but the label in the updatepanel doesn't change.

In addition, I received the wierdest error -must have <head runat="server"> You might notice I've added the runat=server to the header, though it still doesn't work.

Default2.aspx

<%

@.PageLanguage="VB"AutoEventWireup="false"CodeFile="Default2.aspx.vb"Inherits="Default2" %>

<

html>

<

headrunat="server"><title>ddd</title>

</

head>

<

body><formid="MainForm"runat="server"><atlas:ScriptManagerID="sm"runat="server"EnablePartialRendering="true"/><atlas:UpdatePanelID="UpdatePanel1"Mode="Always"runat="Server"><ContentTemplate><asp:LabelID="Label1"runat="server"Text="Label"></asp:Label></ContentTemplate></atlas:UpdatePanel></form><scriptlanguage="javascript">function pageLoad()

{

GetServerTime();

}

function GetServerTime()

{

var message ='';var context ='';

<%=sCallBackFunctionInvocation%>

}

function ShowServerTime(timeMessage, context) {

alert(

'The time on the server is:\n' + timeMessage);

}

function OnError(message, context) {

alert(

'An unhandled exception has occurred:\n' + message);

}

</script>

</

body>

</

html>

Default2.aspx.vb

Partial

Class Default2Inherits System.Web.UI.PageImplements ICallbackEventHandlerPublicFunction GetCallbackResult1()AsStringImplements System.Web.UI.ICallbackEventHandler.GetCallbackResultMe.Label1.Text ="Binded, at last"Return ("The server time is " &Date.Now.ToString)EndFunctionPublicSub RaiseCallbackEvent1(ByVal eventArgumentAsString)Implements System.Web.UI.ICallbackEventHandler.RaiseCallbackEventEndSubPublic sCallBackFunctionInvocationAsStringProtectedSub Page_Load(ByVal senderAsObject,ByVal eAs System.EventArgs)HandlesMe.Load

sCallBackFunctionInvocation = _

Me.ClientScript.GetCallbackEventReference(Me,"message","ShowServerTime","context","OnError",True)EndSub

End

Class
Sorry, I havent got an asnwer for you, but Im trying to do the same thing.

I have built a query screen that connects to one of my web services and nicely brings back a list of results using atlas, then when you click on one of the results it loads the details page which then shows you the full details of the selected item.

The thing is when the details page is being loaded I have to call 4 web services to get the required data back, and this can take anything upto 8seconds to do. During this time the first screen is just locked whilst the details page is being built in the background.

What i want is the details page to show straight away - but empty, and my UpdateProgress to show a nice ticking loading sign.

As you said, PageFlakes has a nice example of this working, but I cant seem to replicate it.

I have tried putting my server side code in the page load event, then setting the trigger for the updatepanels to page load, but it doesnt seem to like this.

if you find an answer let me know!

stuart

Eureka !Idea [Idea]

With the help ofMatt Gibbsfrom Microsoft (ScottGuthrie refered me to him) I managed to do it.

Matt's answer to my question was a little different from how I ended up doing it, so I'll show both.

First, my crude way... The trick was not to useICallbackEventHandlerbutIPostBackEventHandler (I didn't even know it existed before Matt told me).

ASPX page:

<%@. Page Language="VB" AutoEventWireup="false" CodeFile="Default4.aspx.vb" Inherits="Default4" %><html><head runat="server" > <title>ddd </title></head><body> <form id="MainForm" runat="server"> <atlas:ScriptManager ID="sm" runat="server" EnablePartialRendering ="true" /> <atlas:UpdatePanel ID="UpdatePanel1" Mode="Always" runat="Server"> <ContentTemplate> <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> </ContentTemplate> </atlas:UpdatePanel> <atlas:UpdateProgress ID ="UpdateProgress1" runat="server"> <ProgressTemplate> Loading... </ProgressTemplate> </atlas:UpdateProgress> </form> <script language="javascript"> function pageLoad() { GetServerTime(); } function GetServerTime() { var message = ''; var context = ''; <%=sCallBackFunctionInvocation%> } function ShowServerTime(timeMessage, context) { alert('The time on the server is:\n' + timeMessage); } function OnError(message, context) { alert('An unhandled exception has occurred:\n' + message); } </script> </body></html>


This is the code behind:
 PartialClass Default4Inherits System.Web.UI.Page'Inherits System.Web.UI.WebControls.WebControlImplements IPostBackEventHandlerPublic sCallBackFunctionInvocationAs String Protected Sub Page_Load(ByVal senderAs Object,ByVal eAs System.EventArgs)Handles Me.Load sCallBackFunctionInvocation = _Me.ClientScript.GetPostBackEventReference(Me,"message")' Me.ClientScript.GetCallbackEventReference(Me, "message", "ShowServerTime", "context", "OnError", True)End Sub Public Sub RaisePostBackEvent1(ByVal eventArgumentAs String)Implements IPostBackEventHandler.RaisePostBackEvent System.Threading.Thread.Sleep(2000)Me.Label1.Text ="Binded, at last"End SubEnd Class


Now, this is what Matt wrote, I see how it's better because it's easier to apply later to any application, but I couldn't create it:

1) create a custom control that inherits from WebControl, let's call it Loader

2) In the code for Loader, have it override the Render method and call GetPostBackEvent reference. It should render out clientside script for the pageLoad function that contains the result of the call to GetPostBackEventReference.

3) The Loader control should also implement the IPostbackEventHandler interface. In the RaisePostBackEvent method call DataBind on the page.

4) Now you can use the control on your page to have the UpdatePanel refreshed when the page loads.


Here is your sample packaged as a custom control.

The Page:

<%

@.PageLanguage="C#" %>
<%@.RegisterTagPrefix="my"Namespace="Samples" %>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<scriptrunat="server">
</script>
<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
<title>Untitled Page</title>
</head>
<body>
<formid="form1"runat="server">
<div>
<atlas:ScriptManagerID="sm"runat="server"EnablePartialRendering="true"/>
<atlas:UpdatePanelID="UpdatePanel1"Mode="Always"runat="Server">
<ContentTemplate>
<asp:LabelID="Label1"runat="server"Text="Label"></asp:Label>
</ContentTemplate>
</atlas:UpdatePanel>
<atlas:UpdateProgressID="UpdateProgress1"runat="server">
<ProgressTemplate>
Loading...
</ProgressTemplate>
</atlas:UpdateProgress>
<my:Loaderrunat="server"/>
</div>
</form>
</body>
</html>

And the custom control:

using

System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

namespace

Samples {
publicclassLoader : System.Web.UI.WebControls.WebControl,IPostBackEventHandler {public Loader() {
}protectedoverridevoid Render(HtmlTextWriter writer) {
writer.Write("<script type=\"text/javascript\" >\r\n");
writer.Write("function pageLoad() {\r\n");
writer.Write("alert(\"waiting\");\r\n");
writer.Write(this.Page.GetPostBackEventReference(this));
writer.Write("}\r\n");
writer.Write("</script>\r\n");
}publicvoid RaisePostBackEvent(string eventArgument) {
Label label =this.Page.FindControl("Label1")asLabel;
if (label !=null) {
label.Text ="updated at last";
}
}
}
}
Well done - that has helped me a lot. I got it all working with the user control setup.

I just found out (late, huh?) that search engine spider bots will not be able to "see" the information on any page that uses this, because this uses post backs.

If there's no way to do this and still have the search engines find it, I'll have to give it up. Pitty - this was a very nice UI solution.


I just found this thread - how do I keep the page from posting back in a loop ? It does in fact do a postback after page_load has fired, but it keeps doing it...
It shouldn't be doing it. Go over the code again, see if you can debug it. If not, post it and we'll take a look.

@.BitShift:

The problem might be that you moved the Loader control within the UpdatePanel?? I did that for another reason and I got exactly what you described.

Here is the problem I'm facing:

Yes the sample code that Matt provided does work, however, I want to move my Loader control into the UpdatePanel, so that the first it renders it does the AsyncPostback, and then on the second render it display the real control that I want to load. This avoid the need for triggers and everything in centralized in my Loader control. I too thought this would be easy, but alas not. :(

here is what I have (simplified):

<asp:UpdatePanelID="UpdatePanel1"UpdateMode="Conditional"ChildrenAsTriggers="true"runat="Server">
<ContentTemplate>
<cc1:LoaderID="Loader1"ReportPath="/Analysis Reports Test/Report1"runat="server"/>
</ContentTemplate>
</asp:UpdatePanel>

CodeBehind of Loader.cs:

protectedoverridevoid Render(HtmlTextWriter writer)
{
if (!bPostback)
{
writer.WriteLine("Loading...");
writer.WriteLine("<script type=\"text/javascript\" >");
writer.WriteLine("Sys.Application.add_load(" +this.ClientID +"_Load);");
writer.WriteLine("function " +this.ClientID +"_Load() {");
writer.WriteLine(this.Page.ClientScript.GetPostBackEventReference(this,""));
writer.WriteLine("}");
writer.WriteLine("</script>");
}
else
{
this.Controls[0].RenderControl();
}
}bool bPostback =false;
publicvoid RaisePostBackEvent(string eventArgument)
{
System.Diagnostics.Debug.WriteLine("Postback from : " + eventArgument);
bPostback =true;
MyCustomControl c =new MyCustomControl();
this.Controls.Add(c);
}

When I do this, I lose the AsyncPostback of the UpdatePanel, it does a full page postback. I have to explicitly set the AsyncPostBackTrigger even though it is a child control and the ChildrenAsTriggers="true"?!?

Even at that I am still having another issue, that my page has mulitple of these loaders and all UpdatePanels should be triggered only once. Unfortunately this is not the case. I have the UpdateMode set to Conditional however with the AsyncTrigger it still appears to trigger multiple updates. With 2 UpdatePanels and Loader controls, I get 3 postbacks:
1) Loader2
2) Loader1
3) Loader2

Any helpwould be greatly appreciated.

Perry


I got this problem also, and it's been a long time this thread gone unanswered.

Could somebody gives me exact explanation about how the postback sequence in favor of MS AJAX? It seems to me it got mixed up and the order is different on web form without AJAX.

regards,

Eriawan

No comments:

Post a Comment