Auto-refresh an ASP.NET page every 10 seconds
Dashboards, queue monitors and score boards need fresh data without the user pressing F5. Reloading the whole page every ten seconds takes one line; refreshing only the part that changes is smoother and lighter, and Web Forms has a control for exactly that.
Whole page: Refresh header or meta tag
protected void Page_Load(object sender, EventArgs e)
{
Response.AppendHeader("Refresh", "10"); // reload this URL every 10 seconds
}
Or in the markup: <meta http-equiv="refresh" content="10">. Each reload is a fresh GET: the browser re-requests the page, scroll position may reset, and anything typed in a form is lost. It suits wall displays, not pages people interact with.
Part of the page: Timer and UpdatePanel
<asp:ScriptManager ID="sm" runat="server" />
<asp:UpdatePanel ID="upStatus" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Timer ID="tmrRefresh" runat="server" Interval="10000" OnTick="tmrRefresh_Tick" />
<p>Orders waiting: <asp:Label ID="lblWaiting" runat="server" /></p>
<p class="small">Updated <asp:Label ID="lblTime" runat="server" /></p>
</ContentTemplate>
</asp:UpdatePanel>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) LoadStatus();
}
protected void tmrRefresh_Tick(object sender, EventArgs e)
{
LoadStatus();
}
private void LoadStatus()
{
lblWaiting.Text = OrderQueue.CountWaiting().ToString();
lblTime.Text = DateTime.Now.ToString("HH:mm:ss");
}
The Timer control's Interval is in milliseconds. Each tick performs an asynchronous postback; because the timer sits inside the panel, only that panel is re-rendered. The rest of the page, including form fields being edited, is untouched.
To refresh a panel from a timer placed elsewhere, register it as a trigger: <Triggers><asp:AsyncPostBackTrigger ControlID="tmrRefresh" EventName="Tick" /></Triggers>. Set tmrRefresh.Enabled = false from code to stop refreshing, for example when a job has finished.
Keep in mind that a partial postback still sends the full form and view state and runs the full page life cycle on the server. On a heavy page, ten-second ticks from many users add up.
A lighter alternative: fetch JSON
When only a number or two change, ask the server for those values alone. A generic handler (Status.ashx) returns JSON:
public class Status : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Write("{\"waiting\":" + OrderQueue.CountWaiting() + "}");
}
public bool IsReusable => true;
}
async function refreshStatus() {
try {
const res = await fetch("Status.ashx", { cache: "no-store" });
if (res.ok) {
const data = await res.json();
document.getElementById("waiting").textContent = data.waiting;
}
} catch (e) { /* network hiccup: try again next time */ }
}
setInterval(refreshStatus, 10000);
Pause polling when the tab is hidden by checking document.visibilityState in the callback; nobody needs updates for a page they are not looking at.
Accessibility
Content that changes on its own can be missed by screen-reader users or disrupt them. Wrap the changing value in a container with aria-live="polite" so updates are announced without interrupting, and provide a way to pause automatic updates if they are more than a small status value (WCAG 2.2.2).
To leave the page for another one after a delay instead, see Redirect after a few seconds.
Official documentation
- Microsoft Learn: Timer class (System.Web.UI), UpdatePanel
- MDN: Fetch API, Document.visibilityState, aria-live
- W3C: Understanding SC 2.2.2 Pause, Stop, Hide