User and Custom Controls
|
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 offers two ways to build a reusable piece of UI: a user control (markup-based, fast to author) and a custom server control (code-based, compiled, distributable as a standalone assembly). This page covers both, plus Web Parts, the framework built on top of custom controls for end-user page personalization.
.ascx user controls
A user control is authored exactly like a page — markup plus code-behind — but has no <html>/<form> of
its own and is embedded into a hosting page via @Register:
<%-- Controls/AddressEditor.ascx --%>
<%@ Control Language="C#" CodeBehind="AddressEditor.ascx.cs" Inherits="WebFormsApp.Controls.AddressEditor" %>
<div class="address-editor">
<asp:TextBox ID="StreetBox" runat="server" />
<asp:TextBox ID="CityBox" runat="server" />
</div>
// Controls/AddressEditor.ascx.cs
public partial class AddressEditor : System.Web.UI.UserControl
{
public event EventHandler AddressChanged;
public string Street
{
get => StreetBox.Text;
set => StreetBox.Text = value;
}
protected void StreetBox_TextChanged(object sender, EventArgs e)
=> AddressChanged?.Invoke(this, EventArgs.Empty);
}
<%@ Register TagPrefix="uc" TagName="AddressEditor" Src="~/Controls/AddressEditor.ascx" %>
<uc:AddressEditor ID="ShippingAddress" runat="server" OnAddressChanged="ShippingAddress_AddressChanged" />
Public properties (Street above) and events (AddressChanged) are exactly how the host page communicates
with the control; @Reference on the host page (or Src= on @Register, as above) is what makes the
control’s strongly typed members visible at design time and compile time.
Custom server controls
A custom server control is a compiled class, not markup, and lives in an ordinary class library assembly referenced by any number of applications.
Control vs. WebControl
Deriving from System.Web.UI.Control gives the bare life-cycle/tree participation with no rendering
assumptions — appropriate for a control with no visual output of its own, or one whose output shape does not
fit WebControl’s single-root-tag model. Deriving from `System.Web.UI.WebControl (itself a Control) adds
the common styling API (CssClass, Font, BackColor, Style, …) and a default single-tag rendering
model (TagKey, defaulting to <span>).
Render/RenderContents and CreateChildControls
[ToolboxData("<{0}:RatingStars runat=server></{0}:RatingStars>")]
public class RatingStars : WebControl
{
public int Value { get; set; }
public int MaxStars { get; set; } = 5;
protected override HtmlTextWriterTag TagKey => HtmlTextWriterTag.Span;
protected override void RenderContents(HtmlTextWriter writer)
{
for (int i = 1; i <= MaxStars; i++)
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, i <= Value ? "star filled" : "star");
writer.RenderBeginTag(HtmlTextWriterTag.Span);
writer.Write(i <= Value ? "★" : "☆");
writer.RenderEndTag();
}
}
}
Render controls the entire output including the wrapping tag; overriding RenderContents instead (as
above) keeps WebControl’s own tag/attribute rendering and only customizes what goes inside it.
`CreateChildControls is where a composite control builds its own internal control tree (rather than
writing raw HTML), and is called lazily via EnsureChildControls() — typically overridden instead of
OnInit so the tree is (re)built consistently across the life cycle, including after ViewState is restored.
INamingContainer
Implementing the marker interface INamingContainer makes a control’s own ClientID/UniqueID become a
prefix for all of its children’s IDs, guaranteeing uniqueness when the control (and therefore its children) is
repeated multiple times on one page — essential for any composite or data-bound control that generates
children in a loop (a Repeater row, for instance, is itself a naming container).
Composite and templated controls
A templated control lets the page author supply the inner markup, rather than the control author baking
it in — the pattern every data-bound list control (Repeater, GridView via TemplateField) is built on:
[ParseChildren(true)]
public class Callout : Control, INamingContainer
{
private ITemplate _contentTemplate;
[TemplateContainer(typeof(Control)), PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ContentTemplate
{
get => _contentTemplate;
set => _contentTemplate = value;
}
protected override void CreateChildControls()
{
Controls.Clear();
var container = new Control();
_contentTemplate?.InstantiateIn(container);
Controls.Add(container);
}
}
<cc:Callout runat="server">
<ContentTemplate>
<p>Anything the page author wants, including <asp:Label runat="server" Text="other controls" />.</p>
</ContentTemplate>
</cc:Callout>
ITemplate.InstantiateIn(Control container) is called during CreateChildControls to build whatever markup
the page author supplied into the control’s own tree.
Designer attributes
[ToolboxData], [DefaultProperty], [Bindable], [Category], and [Description] (from
System.ComponentModel) drive how a control appears in the Visual Studio Toolbox and Properties window; none
affect runtime behavior.
Building a data-bound control
A minimal data-bound custom control implements IPostBackDataHandler/relies on WebControl plus a
DataSource property and overrides DataBind/PerformDataBinding to build its child controls from the bound
data during CreateChildControls, following the same DataBinding/bound-item events documented in
Data Binding and Data Controls.
Web Parts and personalization
The Web Parts framework (System.Web.UI.WebControls.WebParts) turns custom controls into end-user
reconfigurable page regions — Zones the user can drag parts between, minimize, close, and (per-user)
remember, which is the layer Vogel’s Professional Web Parts and Custom Controls with ASP.NET 2.0 is written
against:
<asp:WebPartManager ID="Manager1" runat="server" />
<asp:WebPartZone ID="LeftZone" runat="server">
<ZoneTemplate>
<uc:WeatherPart ID="Weather1" runat="server" Title="Weather" />
</ZoneTemplate>
</asp:WebPartZone>
<asp:WebPartZone ID="RightZone" runat="server" />
public class WeatherPart : UserControl, IWebPart
{
public string CatalogIconImageUrl { get; set; }
public string Description { get; set; }
public string Subtitle { get; set; }
public string Title { get; set; }
public string TitleUrl { get; set; }
public bool AllowClose { get; set; } = true;
public bool AllowHide { get; set; } = true;
public bool AllowMinimize { get; set; } = true;
public bool AllowZoneChange { get; set; } = true;
public string AuthorizationFilter { get; set; }
}
WebPartManager orchestrates zones, drag-and-drop layout, and mode switching (Browse/Design/Edit);
connections let two parts exchange data declared through [ConnectionProvider]/[ConnectionConsumer]
methods; personalization providers (the default SqlPersonalizationProvider, backed by the ASPNETDB
schema) persist each user’s per-page layout; and Profile properties (configured in web.config’s
`<profile> section) give strongly typed, per-user settings storage (Profile.FavoriteColor) available
anywhere in the application, independent of Web Parts specifically. See
ASP.NET Web Parts Overview and
ASP.NET Profile Properties
Overview.