ASP.Net: Asynchronous Update Panel Loading with two update panels
I am trying to asynchronously display data in update panels on a website where the data retrieval tasks take different times. I would like to update each panel to show the data on the page after each task finishes.
However, no matter what I try, all Update Panels change their content after the last task has completed.
For example:
I have two tasks:
- One that tries to update a label in UpdatePanel1 after 5 seconds
- One that tries to update a label in UpdatePanel2 after 10 seconds
The expected result is to have only the label in UpdatePanel1 change after 5 seconds, however, both update panels update at the same time, at 10 seconds.
Both update panels are set to updatemode="Conditional" and they are told to postback from client javascript. Below is a complete listing of the example above.
What am I missing here? How do I get one update panel to load, and then the other, having both tasks run asynchronously?
Thanks,
TM
ASPX:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"
Inherits="_Default"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xh开发者_JAVA百科tml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body onload="partialPostback();">
<script language="JavaScript" type="text/javascript">
function partialPostback() {
__doPostBack('UpdatePanel1', '');
__doPostBack('UpdatePanel2', '');
}
</script>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"/>
5 sec:
<asp:UpdatePanel ID="UpdatePanel1" runat="server"
UpdateMode="Conditional" OnLoad="UpdatePanel1_Load">
<ContentTemplate>
<asp:Label ID="Label2" runat="server" Text="Label"/><br />
</ContentTemplate>
</asp:UpdatePanel><br />
10 sec:
<asp:UpdatePanel ID="UpdatePanel2" runat="server"
UpdateMode="Conditional" OnLoad="UpdatePanel2_Load">
<ContentTemplate>
<asp:Label ID="Label1" runat="server" Text="Label"/><br />
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
public partial class _Default : System.Web.UI.Page
{
Thread t1;
Thread t2;
protected override void OnPreRender(EventArgs e)
{
if (t1 != null)
{ t1.Join(); }
if (t2 != null)
{ t2.Join(); }
base.OnPreRender(e);
}
protected void Page_Load(object sender, EventArgs e)
{ }
protected void UpdatePanel1_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
ThreadStart tstart = new ThreadStart(DoWork1);
t1 = new Thread(tstart);
t1.IsBackground = true;
t1.Start();
}
}
protected void UpdatePanel2_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
ThreadStart tstart = new ThreadStart(DoWork2);
t2 = new Thread(tstart);
t2.IsBackground = true;
t2.Start();
}
}
private void DoWork1()
{
Thread.Sleep(5000);
this.Label2.Text = "Done in 5 sec!";
this.UpdatePanel1.Update();
}
private void DoWork2()
{
Thread.Sleep(10000);
this.Label1.Text = "Done in 10 sec!";
this.UpdatePanel2.Update();
}
}
Just put a Trigger tag inside each UpdatePanel pointing to a asp:Timer and set interval to 5000 and 10000 miliseconds.
Below is the solution that you ask, using UpdatePanel, but take care because each 5 and 10 seconds there is a PostBack fired for Timer:
I recommend using of javascript or jQuery to avoid PostBacks.
ASPX:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="WebApplication1.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server"/>
5 sec:
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick"/>
</Triggers>
<ContentTemplate>
<asp:Label ID="Label2" runat="server" Text="Label"/><br />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Timer ID="Timer1" runat="server" Interval="5000"
OnTick="Timer1_Tick"/><br />
10 sec:
<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer2" EventName="Tick"/>
</Triggers>
<ContentTemplate>
<asp:Label ID="Label1" runat="server" Text="Label"/><br />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Timer ID="Timer2" runat="server" Interval="10000"
OnTick="Timer2_Tick"/>
</form>
</body>
</html>
C#
using System;
namespace WebApplication1
{
public partial class Default : System.Web.UI.Page
{
protected void Timer1_Tick(object sender, EventArgs e)
{
this.Label2.Text = "Done in 5 sec!";
}
protected void Timer2_Tick(object sender, EventArgs e)
{
this.Label1.Text = "Done in 10 sec!";
}
}
}
I suspect that you aren't calling this as asynchronously as you think b/c of the thread joins. It looks like you're basically saying block OnPrerender until thread 2 terminates. Because both 1 and 2 are being called from the same method, you're blocked until both are done. If you throw in some writelines to see when things are being called, it might become a little easier to see what is happening.
I also suspect the text on 1 won't really update until prerender is complete from what I know. Traditional ASP.NET, nothing is sent back to the client until after prerender completes. But I don't know much about update panel, so I could be talking junk on that one and anticipating being marked down...
The joins in your pre-render handler are blocking the rendering from coming back to the client. Instead of what you have, I suspect that you could do something like this instead:
if (t1 != null) {
t1.join();
} else if (t2 != null) {
t2.join();
}
That code has the unfortunate side-effect of being dependent on knowing which thread will return first, though.
However, if you're just trying to figure out how to send events from the server to client (and html 5 is an option), I'd recommend looking into server-sent events (or web sockets if you require full duplex communication). If html 5 isn't an option, I believe you can use some javascript hacks to simulate web sockets. You can read about some of those here and here.
edit: added links for non-html 5 alternatives
Instead you can use 2 timer controls, one with interval of 5000 and another with interval of 10000
Are you able to use .NET 4.5 or are you restricted to earlier versions? v4.5 has alot of new features that make creating asynchronous methods pretty straight forward without having to worry about thread management. This link has a good explanation of how to implement an asynchronous method with the new Task
task and async
/await
operators: Working with Asynchronous Operations in ASP.NET 4.5 Web Forms
Essentially you'd just have to create a Task
for each method responsible for updating the updatepanel and fire it up on page load. At completion each task could sleep for 5 or 10seconds before calling itself to give you the effect you're after.
精彩评论