Context
DepartmentPortal has 7 code-only server controls (no .ascx file) in samples/DepartmentPortal/Code/Controls/*.cs. These controls inherit from CompositeControl, DataBoundControl, Control, WebControl, and IPostBackEventHandler — they are pure C# server controls with no markup file.
The CLI pipeline currently only discovers .ascx user controls. Code-only controls in Code/Controls/ are entirely invisible to the migration pipeline. They are referenced in page markup via local:ControlName tag prefix (registered as a namespace-level tag prefix in Web.config) but no scaffolding is emitted for them.
Jeff's directive: "these should migrate directly with our shim control classes — we need a migration class in the CLI"
The 7 Code-Only Controls Found
| Control |
Base Class(es) |
Used On |
SectionPanel |
Control, INamingContainer, [ParseChildren(true)], ITemplate props |
Announcements.aspx (×1), Resources.aspx (×3) |
EmployeeDataGrid |
DataBoundControl (overrides PerformDataBinding + RenderContents) |
Employees.aspx, Admin/ManageEmployees.aspx |
EmployeeCard |
CompositeControl (overrides CreateChildControls) |
EmployeeDetail.aspx |
PollQuestion |
Control, IPostBackEventHandler (implements RaisePostBackEvent); uses Page.ClientScript.GetPostBackEventReference |
Training.aspx |
StarRating |
WebControl (overrides RenderContents, TagKey) |
EmployeeDetail.aspx |
NotificationBell |
WebControl; raises NotificationClicked / NotificationDismissed events |
Defined only (not yet placed on a page) |
DepartmentBreadcrumb |
Control, IPostBackEventHandler; uses Page.ClientScript.GetPostBackEventReference |
Defined only (not yet placed on a page) |
Source Location
samples/DepartmentPortal/Code/Controls/
Proposed Approach
Create a CodeOnlyControlScaffolder class in src/BlazorWebFormsComponents.Cli/Scaffolding/ that:
- Scans C# files under the source project for classes that inherit from
WebControl, CompositeControl, DataBoundControl, or Control
- Skips known System.Web framework types (only emits stubs for user-defined controls)
- Emits a Blazor
.razor + .razor.cs component stub pair for each discovered control
- Maps Web Forms base classes to BWFC shim base classes:
| Web Forms Base |
BWFC Base Class |
WebControl |
BaseStyledComponent |
CompositeControl |
BaseWebFormsComponent |
DataBoundControl |
DataBoundComponent<T> |
Control |
BaseWebFormsComponent |
- Preserves public properties from the original class as
[Parameter] properties in the emitted .razor.cs
- Preserves public events from the original class as
EventCallback or EventCallback<T> parameters
The scaffolder should run as part of the MigrationPipeline before markup transforms so that discovered controls are available when LocalTagNamespaceResolutionTransform (see companion issue) resolves local: tags.
Acceptance Criteria
Notes
This is Tier 1 Critical Blocker P1 from the DepartmentPortal gap analysis. Without this, 7 controls used across multiple DepartmentPortal pages produce no output, and all local: markup references fail to resolve.
Context
DepartmentPortal has 7 code-only server controls (no
.ascxfile) insamples/DepartmentPortal/Code/Controls/*.cs. These controls inherit fromCompositeControl,DataBoundControl,Control,WebControl, andIPostBackEventHandler— they are pure C# server controls with no markup file.The CLI pipeline currently only discovers
.ascxuser controls. Code-only controls inCode/Controls/are entirely invisible to the migration pipeline. They are referenced in page markup vialocal:ControlNametag prefix (registered as a namespace-level tag prefix inWeb.config) but no scaffolding is emitted for them.Jeff's directive: "these should migrate directly with our shim control classes — we need a migration class in the CLI"
The 7 Code-Only Controls Found
SectionPanelControl,INamingContainer,[ParseChildren(true)],ITemplatepropsEmployeeDataGridDataBoundControl(overridesPerformDataBinding+RenderContents)EmployeeCardCompositeControl(overridesCreateChildControls)PollQuestionControl,IPostBackEventHandler(implementsRaisePostBackEvent); usesPage.ClientScript.GetPostBackEventReferenceStarRatingWebControl(overridesRenderContents,TagKey)NotificationBellWebControl; raisesNotificationClicked/NotificationDismissedeventsDepartmentBreadcrumbControl,IPostBackEventHandler; usesPage.ClientScript.GetPostBackEventReferenceSource Location
samples/DepartmentPortal/Code/Controls/Proposed Approach
Create a
CodeOnlyControlScaffolderclass insrc/BlazorWebFormsComponents.Cli/Scaffolding/that:WebControl,CompositeControl,DataBoundControl, orControl.razor+.razor.cscomponent stub pair for each discovered controlWebControlBaseStyledComponentCompositeControlBaseWebFormsComponentDataBoundControlDataBoundComponent<T>ControlBaseWebFormsComponent[Parameter]properties in the emitted.razor.csEventCallbackorEventCallback<T>parametersThe scaffolder should run as part of the
MigrationPipelinebefore markup transforms so that discovered controls are available whenLocalTagNamespaceResolutionTransform(see companion issue) resolveslocal:tags.Acceptance Criteria
Code/Controls/folder.razor+.razor.csstub pair is emitted for each discovered control[Parameter]in the stubEventCallbackorEventCallback<T>parametersMigrationPipelineand integrates with theLocalTagNamespaceResolutionTransformtests/BlazorWebFormsComponents.Cli.Tests/Notes
This is Tier 1 Critical Blocker P1 from the DepartmentPortal gap analysis. Without this, 7 controls used across multiple DepartmentPortal pages produce no output, and all
local:markup references fail to resolve.