Lessons in this course (9)

2. Building a custom server control — rendering, properties and ViewState

Intermediate★ Must knowLesson 2 of 9
csharp
[DefaultProperty(nameof(Amount))]
[ToolboxData("<{0}:MoneyLabel runat=\"server\" />")]
public class MoneyLabel : WebControl                   // WebControl adds style properties (CssClass, Font, etc.)
{
    public MoneyLabel() : base(HtmlTextWriterTag.Span) { }

    [Category("Data"), DefaultValue(typeof(decimal), "0")]
    public decimal Amount
    {
        get => (decimal?)ViewState[nameof(Amount)] ?? 0m;   // ViewState-backed so it survives postbacks
        set => ViewState[nameof(Amount)] = value;
    }

    [Category("Appearance"), DefaultValue("en-IN")]
    public string Culture
    {
        get => (string?)ViewState[nameof(Culture)] ?? "en-IN";
        set => ViewState[nameof(Culture)] = value;
    }

    protected override void AddAttributesToRender(HtmlTextWriter writer)
    {
        base.AddAttributesToRender(writer);                  // id, class, style
        if (Amount < 0) writer.AddAttribute(HtmlTextWriterAttribute.Class, CssClass + " negative");
    }

    protected override void RenderContents(HtmlTextWriter writer)
        => writer.WriteEncodedText(Amount.ToString("C", CultureInfo.GetCultureInfo(Culture)));
}
aspx
<%@ Register Assembly="Shop.Controls" Namespace="Shop.Controls" TagPrefix="shop" %>
<shop:MoneyLabel ID="lblTotal" runat="server" Amount="4500" CssClass="price" />

Base classes:

BaseUse
ControlMinimal: no style properties; override Render
WebControlAdds CssClass, Style, Enabled, tag rendering (RenderBeginTag/RenderContents)
CompositeControlBuilds child controls (CreateChildControls) — reuse existing TextBoxes/Buttons
DataBoundControl / CompositeDataBoundControlBind to data sources (DataSource, DataBind)
Existing control (e.g. TextBox)Extend behaviour (a NumericTextBox)