AJAX and Client-Side Integration
|
This section documents ASP.NET Web Forms on .NET Framework 4.8.1, the last and permanent version of Web Forms — the page life cycle and postback model, ViewState and control state, server controls, validation controls, master pages and themes, data-bound controls, and the provider-based security model — as described by the official documentation at Microsoft Learn and the ASP.NET previous-versions archive, which are the reference these pages are written and verified against. Web Forms receives security fixes only and has no forward path onto modern .NET (.NET Framework 4.8.1 is Microsoft’s last version of .NET Framework; Web Forms itself never shipped on .NET Core/.NET 5+). It remains supported for existing applications running on Windows but is not recommended for new development — see Choosing an ASP.NET Framework and Migrating to Modern ASP.NET for what that means in practice. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Web Forms predates the modern fetch/JSON client-side world, and its own "ASP.NET AJAX" layer (UpdatePanel
and friends) is best understood as partial-page postback dressed up to look asynchronous — genuinely useful
for retrofitting existing postback-based pages, but not a substitute for a real client-side JSON API when
building something new.
ScriptManager and ScriptManagerProxy
Exactly one ScriptManager per page (or per content page via master page) is required for any AJAX
functionality below to work; nested content pages that need to register additional scripts use
ScriptManagerProxy instead of a second ScriptManager:
<%-- Site.master --%>
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<%-- Default.aspx, which uses Site.master --%>
<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
<Scripts>
<asp:ScriptReference Path="~/Scripts/page-specific.js" />
</Scripts>
</asp:ScriptManagerProxy>
UpdatePanel, UpdateProgress, and Timer
UpdatePanel wraps a region of a page so that a postback originating inside it (or targeted at it via a
Trigger) refreshes only that region’s HTML, without a full-page navigation:
<asp:UpdatePanel ID="ResultsPanel" runat="server">
<ContentTemplate>
<asp:Label ID="ResultsLabel" runat="server" />
<asp:Button ID="RefreshButton" runat="server" Text="Refresh" OnClick="RefreshButton_Click" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdateProgress ID="Progress1" runat="server" AssociatedUpdatePanelID="ResultsPanel">
<ProgressTemplate><span>Loading...</span></ProgressTemplate>
</asp:UpdateProgress>
<asp:Timer ID="AutoRefreshTimer" runat="server" Interval="30000" OnTick="AutoRefreshTimer_Tick" />
UpdateProgress shows/hides its template automatically around an in-flight async postback for the panel it is
associated with; Timer raises a periodic async postback (OnTick) without any user action, commonly paired
with an UpdatePanel for polling-style auto-refresh.
UpdateMode/Triggers and the real cost of partial rendering
By default (UpdateMode="Always") an UpdatePanel refreshes on any postback anywhere on the page, not just
ones that originated inside it — often not what is wanted, and a common source of unnecessary re-render work.
UpdateMode="Conditional" combined with explicit <Triggers> scopes updates to specific controls/events:
<asp:UpdatePanel ID="ResultsPanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>...</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="SearchButton" EventName="Click" />
<asp:PostBackTrigger ControlID="ExportButton" /> <%-- forces a FULL postback, e.g. for a file download --%>
</Triggers>
</asp:UpdatePanel>
The critical point Esposito’s Programming Microsoft ASP.NET 4 makes explicit and that is easy to miss: an
UpdatePanel postback is not a lightweight AJAX call. The entire
page life cycle still runs server-side — full
ViewState load/save, every control’s Load/PreRender, the works — only the response is trimmed down to
the changed panel(s)' HTML (via a client-side script that patches the DOM) instead of a full-page document.
UpdatePanel reduces bandwidth and avoids a visible full-page reload; it does essentially nothing for
server-side CPU cost per request.
The ASP.NET AJAX client library and the AJAX Control Toolkit (historic)
Sys./Sys.Net. (the MicrosoftAjax.js/MicrosoftAjaxWebForms.js scripts ScriptManager injects
automatically) is the original client-side runtime UpdatePanel and the unobtrusive validators depend on. The
AJAX Control Toolkit was a large community/Microsoft-maintained library of additional UpdatePanel-style
controls (CalendarExtender, AutoCompleteExtender, ModalPopupExtender, …) built on top of it — still
functional (community-maintained on GitHub) but effectively historic; no new project should adopt it, and an
existing dependency on it is a signal that any client-side interactivity work is better done with plain
JavaScript/jQuery against a JSON endpoint instead of another extender.
Page methods and script services
Page methods are static methods on a page’s code-behind, marked [WebMethod], callable directly from
client script via a generated proxy, without the overhead of a full postback or UpdatePanel:
public partial class Search : System.Web.UI.Page
{
[System.Web.Services.WebMethod]
public static List<string> FindMatches(string query)
{
return _searchService.Find(query); // static: no access to Page/Session/ViewState
}
}
PageMethods.FindMatches(document.getElementById('QueryBox').value, function (results) {
// onSuccess callback; results is the JSON-deserialized List<string>
renderResults(results);
});
Page methods must be static (no implicit Page/control access) and require
<asp:ScriptManager EnablePageMethods="true">. A script service is the same idea generalized to a
standalone .asmx (below) marked [ScriptService], callable from any page that references it via
ScriptManager, rather than being tied to one specific page.
ASMX web services and WCF from script
.asmx ASMX web services are the original SOAP/XML web-service technology from ASP.NET 1.x, still
functional under Web Forms and callable from client script when decorated [ScriptService]:
[System.Web.Services.WebService(Namespace = "http://example.com/")]
[System.Web.Script.Services.ScriptService]
public class Catalog : System.Web.Services.WebService
{
[System.Web.Services.WebMethod]
public Product[] GetProducts(int categoryId) => _repository.ByCategory(categoryId);
}
WCF services can also be exposed to script via System.ServiceModel.Activation.WebScriptServiceHostFactory
and an .svc endpoint configured for JSON, offering roughly the same client-callable shape as a script-enabled
ASMX service but built on the newer (for the Web Forms era) WCF service model. Both predate ASP.NET Web API and
are the two REST/JSON-from-script options native to System.Web.
Using jQuery and plain fetch/XHR against .ashx handlers instead
For anything beyond the simplest partial-postback scenario, calling a
.ashx generic handler directly with
fetch or jQuery’s $.ajax sidesteps the page life cycle entirely and is both simpler to reason about and
considerably cheaper server-side than an UpdatePanel:
fetch('/Handlers/Search.ashx?q=' + encodeURIComponent(query))
.then(r => r.json())
.then(results => renderResults(results));
// Handlers/Search.ashx.cs
public class Search : IHttpHandler
{
public bool IsReusable => true;
public void ProcessRequest(HttpContext context)
{
var results = _searchService.Find(context.Request.QueryString["q"]);
context.Response.ContentType = "application/json";
context.Response.Write(new JavaScriptSerializer().Serialize(results));
}
}
This is the pattern most maintained Web Forms applications converge on for anything genuinely interactive, since it has none of `UpdatePanel’s full-life-cycle cost and none of ASMX/WCF’s SOAP-era ceremony.
ClientIDMode
Web Forms historically mangled a control’s rendered id attribute with its full naming-container path
(ctl00_MainContent_SearchBox), which made writing plain CSS/JavaScript selectors against server controls
awkward. ClientIDMode (4.0+) controls this:
| Value | Behavior |
|---|---|
|
The historic behavior — fully qualified, container-prefixed ID ( |
|
The control’s own |
|
Used for data-bound repeating controls; concatenates the naming container’s ID with the data
item’s index/key in a predictable, shorter pattern than |
|
Use whatever the parent/page specifies. |
<%@ Page ClientIDMode="Static" ... %>
Setting ClientIDMode="Static" page- or application-wide (<pages clientIDMode="Static" /> in web.config)
is the common choice in any Web Forms page written to be driven by hand-authored jQuery/CSS, since it makes
$('#SearchBox') reliable again.