Custom WebControl Migration

This page demonstrates how Web Forms custom controls that use WebControl base class with RenderContents(HtmlTextWriter) work unchanged in Blazor through BWFC.


Demo 1: StatusBadge (TagKey + RenderContents + AddAttributesToRender)

A custom control that renders a badge with a data attribute and styled inner content.

Live Output:
Online Warning Offline
Component Code (unchanged from Web Forms):
public class StatusBadge : WebControl
{
    public string Status { get; set; }
    public string Label { get; set; }

    protected override HtmlTextWriterTag TagKey
        => HtmlTextWriterTag.Span;

    protected override void AddAttributesToRender(
        HtmlTextWriter writer)
    {
        base.AddAttributesToRender(writer);
        writer.AddAttribute("data-status",
            Status?.ToLowerInvariant() ?? "unknown");
    }

    protected override void RenderContents(
        HtmlTextWriter writer)
    {
        writer.RenderBeginTag(HtmlTextWriterTag.Strong);
        writer.Write(Label ?? Status);
        writer.RenderEndTag();
    }
}

Demo 2: InfoCard (Complex Nested RenderContents)

A custom control that renders a card with a header and body using nested HTML elements.

Live Output:

Migration Complete

Your custom WebControl works in Blazor without code changes!

Custom Header Style

The HeaderCssClass parameter controls the header styling.
Component Code (unchanged from Web Forms):
public class InfoCard : WebControl
{
    public string Title { get; set; }
    public string Body { get; set; }
    public string HeaderCssClass { get; set; }
        = "card-header";

    protected override HtmlTextWriterTag TagKey
        => HtmlTextWriterTag.Div;

    protected override void RenderContents(
        HtmlTextWriter writer)
    {
        // Header
        writer.AddAttribute(
            HtmlTextWriterAttribute.Class,
            HeaderCssClass);
        writer.RenderBeginTag(HtmlTextWriterTag.Div);
        writer.RenderBeginTag(HtmlTextWriterTag.H3);
        writer.Write(Title);
        writer.RenderEndTag(); // h3
        writer.RenderEndTag(); // header div

        // Body
        writer.AddAttribute(
            HtmlTextWriterAttribute.Class,
            "card-body");
        writer.RenderBeginTag(HtmlTextWriterTag.Div);
        writer.Write(Body);
        writer.RenderEndTag(); // body div
    }
}

How It Works

In Web Forms, custom controls inherit System.Web.UI.WebControls.WebControl and override:

  • TagKey — the outer HTML element
  • AddAttributesToRender(HtmlTextWriter) — attributes on the outer tag
  • RenderContents(HtmlTextWriter) — inner HTML content

In Blazor with BWFC, you change only the using statement:

// Before (Web Forms)
using System.Web.UI.WebControls;

// After (Blazor + BWFC)
using BlazorWebFormsComponents.CustomControls;

The BWFC WebControl class provides an HtmlTextWriter shim that captures all rendering calls and emits the result as raw HTML in Blazor's render tree. Your RenderContents code runs unchanged.