This guide walks you through migrating an ASP.NET Web Forms application that uses the Ajax Control Toolkit to Blazor using BlazorAjaxToolkitComponents (BWFC). The library provides drop-in Blazor replacements for 14 commonly used Ajax Control Toolkit extenders and containers, preserving the same property names, behavior, and rendered HTML so your existing CSS and JavaScript continue to work.
BlazorAjaxToolkitComponents is a companion NuGet package to BlazorWebFormsComponents. While the main BWFC package covers standard ASP.NET Web Forms controls (<asp:Button>, <asp:GridView>, etc.), this package covers the Ajax Control Toolkit extenders — the <ajaxToolkit:*> controls that added rich client-side behaviors to Web Forms pages.
Many Web Forms applications depend on Ajax Control Toolkit extenders for:
- Confirmation dialogs, input masks, and filtered text boxes
- Modal popups, collapsible panels, and hover menus
- Tabs, accordions, calendars, and autocomplete
- Sliders, numeric spinners, and toggle buttons
Without a migration path for these controls, developers would need to completely rewrite their UI behavior. BlazorAjaxToolkitComponents lets you keep the same markup (minus the ajaxToolkit: prefix) and the same property names, so migration is mechanical rather than creative.
| Control | Type | Description |
|---|---|---|
ConfirmButtonExtender |
Extender | Confirmation dialog on button click |
FilteredTextBoxExtender |
Extender | Character filtering for text input |
ModalPopupExtender |
Extender | Modal popup with overlay backdrop |
CollapsiblePanelExtender |
Extender | Collapsible/expandable panel |
Accordion / AccordionPane |
Container | Vertically stacked collapsible panes |
TabContainer / TabPanel |
Container | Tabbed content panels |
CalendarExtender |
Extender | Popup date picker |
AutoCompleteExtender |
Extender | Typeahead suggestions |
MaskedEditExtender |
Extender | Input mask formatting |
NumericUpDownExtender |
Extender | Numeric spinner buttons |
SliderExtender |
Extender | Range slider control |
ToggleButtonExtender |
Extender | Image-based checkbox toggle |
PopupControlExtender |
Extender | Click-triggered popup panel |
HoverMenuExtender |
Extender | Hover-triggered popup menu |
dotnet add package BlazorAjaxToolkitComponents!!! note BlazorAjaxToolkitComponents depends on BlazorWebFormsComponents, which will be installed automatically as a transitive dependency.
Add these lines to your project's _Imports.razor file:
@using BlazorAjaxToolkitComponents
@using BlazorAjaxToolkitComponents.EnumsAjax Toolkit extender components require InteractiveServer render mode because they depend on JavaScript interoperability. Add this to your App.razor or the pages that use extenders:
@rendermode InteractiveServerNo manual <script> reference is required. Extender components load their JavaScript modules automatically using ES module imports from the _content/BlazorAjaxToolkitComponents/js/ static assets path. Blazor's static web asset system handles this transparently.
Ensure your App.razor or host page includes the standard Blazor script:
<script src="_framework/blazor.web.js"></script>The <ajaxToolkit:ToolkitScriptManager> is not needed in Blazor. Blazor handles script loading and component lifecycle automatically. Simply delete it:
- <ajaxToolkit:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server" />All Ajax Control Toolkit controls use the ajaxToolkit: XML namespace prefix. In Blazor, components are referenced by their class name directly:
- <ajaxToolkit:ConfirmButtonExtender
+ <ConfirmButtonExtenderFor closing tags:
- </ajaxToolkit:Accordion>
+ </Accordion>Blazor components don't use runat="server". The extender's own ID attribute is also not needed (it was only used for server-side reference in Web Forms):
- <ajaxToolkit:ConfirmButtonExtender
- ID="cbe1"
- runat="server"
+ <ConfirmButtonExtender
TargetControlID="btnDelete"
ConfirmText="Are you sure?" />The vast majority of properties keep the same name and type. BWFC was designed for this:
| Web Forms Property | Blazor Property | Notes |
|---|---|---|
TargetControlID |
TargetControlID |
Same — ID of the target control |
ConfirmText |
ConfirmText |
Same |
PopupControlID |
PopupControlID |
Same |
BackgroundCssClass |
BackgroundCssClass |
Same |
Minimum / Maximum |
Minimum / Maximum |
Same |
FilterType |
FilterType |
Same — uses FilterType enum |
Mask |
Mask |
Same |
HeaderText |
HeaderText |
Same |
Enum values are also preserved. For example, FilterType="Numbers" in Web Forms becomes FilterType="FilterType.Numbers" in Blazor (using the C# enum syntax).
Web Forms uses server-side event handlers wired up declaratively. Blazor uses EventCallback:
Web Forms:
<ajaxToolkit:TabContainer ID="tabs" runat="server"
OnActiveTabChanged="tabs_ActiveTabChanged">Blazor:
<TabContainer ActiveTabIndex="0"
OnActiveTabChanged="HandleTabChanged">
@code {
void HandleTabChanged(int newIndex)
{
// Handle tab change
}
}Key differences:
- No
OnClickwiring on extenders — Extenders attach behavior to their target control. The target control'sOnClick(or equivalent) still works normally. - EventCallback instead of server events —
Accordion.SelectedIndexChangedis anEventCallback<int>,TabContainer.OnActiveTabChangedis anEventCallback<int>. - Client-side scripts remain strings — Properties like
OnOkScript,OnCancelScript, andOnClientActiveTabChangedaccept JavaScript code strings, just like in Web Forms.
Extender components require JavaScript interop, which is only available in InteractiveServer mode. In Static SSR mode, extenders silently skip initialization (no errors thrown, but no behavior is attached).
Displays a browser confirmation dialog when a button is clicked. If cancelled, the click is suppressed.
=== "Web Forms"
```html
<asp:Button ID="btnDelete" Text="Delete" OnClick="btnDelete_Click" runat="server" />
<ajaxToolkit:ConfirmButtonExtender
ID="cbe1"
runat="server"
TargetControlID="btnDelete"
ConfirmText="Are you sure you want to delete this?"
ConfirmOnFormSubmit="false" />
```
=== "Blazor"
```razor
<Button ID="btnDelete" Text="Delete" OnClick="HandleDelete" />
<ConfirmButtonExtender
TargetControlID="btnDelete"
ConfirmText="Are you sure you want to delete this?"
ConfirmOnFormSubmit="false" />
@code {
void HandleDelete() { /* Runs only if user clicks OK */ }
}
```
Properties: TargetControlID, ConfirmText, ConfirmOnFormSubmit, DisplayModalPopupID (reserved), BehaviorID, Enabled
Restricts input in a TextBox to specified character types, filtering keystrokes in real-time.
=== "Web Forms"
```html
<asp:TextBox ID="txtPhone" runat="server" />
<ajaxToolkit:FilteredTextBoxExtender
ID="fte1"
runat="server"
TargetControlID="txtPhone"
FilterType="Numbers"
ValidChars="-+()" />
```
=== "Blazor"
```razor
<TextBox ID="txtPhone" />
<FilteredTextBoxExtender
TargetControlID="txtPhone"
FilterType="FilterType.Numbers"
ValidChars="-+()" />
```
Properties: TargetControlID, FilterType, ValidChars, InvalidChars, FilterMode, FilterInterval, BehaviorID, Enabled
!!! tip
Enum values use C# syntax in Blazor: FilterType.Numbers, FilterType.Custom, FilterMode.ValidChars, etc.
Displays a target element as a modal popup with an overlay backdrop. Supports OK/Cancel actions, drag, and drop shadow.
=== "Web Forms"
```html
<asp:Button ID="btnOpen" Text="Open" runat="server" />
<div ID="myModal" style="display:none; background:white; padding:20px;">
<p>Modal content</p>
<button ID="btnOK">OK</button>
<button ID="btnCancel">Cancel</button>
</div>
<ajaxToolkit:ModalPopupExtender
ID="mpe1"
runat="server"
TargetControlID="btnOpen"
PopupControlID="myModal"
BackgroundCssClass="modalBackground"
OkControlID="btnOK"
CancelControlID="btnCancel"
DropShadow="true"
Drag="true"
PopupDragHandleControlID="dragHandle" />
```
=== "Blazor"
```razor
<Button ID="btnOpen" Text="Open" />
<div ID="myModal" style="display:none; background:white; padding:20px;">
<p>Modal content</p>
<button ID="btnOK">OK</button>
<button ID="btnCancel">Cancel</button>
</div>
<ModalPopupExtender
TargetControlID="btnOpen"
PopupControlID="myModal"
BackgroundCssClass="modalBackground"
OkControlID="btnOK"
CancelControlID="btnCancel"
DropShadow="true"
Drag="true"
PopupDragHandleControlID="dragHandle" />
```
Properties: TargetControlID, PopupControlID, BackgroundCssClass, OkControlID, CancelControlID, OnOkScript, OnCancelScript, DropShadow, Drag, PopupDragHandleControlID, BehaviorID, Enabled
Adds collapse/expand behavior to a panel with smooth CSS transitions.
=== "Web Forms"
```html
<asp:Panel ID="pnlContent" runat="server">
<p>Collapsible content here</p>
</asp:Panel>
<asp:Label ID="lblStatus" runat="server" />
<asp:LinkButton ID="lnkToggle" runat="server" Text="Toggle" />
<ajaxToolkit:CollapsiblePanelExtender
ID="cpe1"
runat="server"
TargetControlID="pnlContent"
CollapseControlID="lnkToggle"
ExpandControlID="lnkToggle"
Collapsed="true"
CollapsedText="Show Details"
ExpandedText="Hide Details"
TextLabelID="lblStatus"
ExpandDirection="Vertical"
CollapsedSize="0" />
```
=== "Blazor"
```razor
<Panel ID="pnlContent">
<p>Collapsible content here</p>
</Panel>
<Label ID="lblStatus" />
<LinkButton ID="lnkToggle" Text="Toggle" />
<CollapsiblePanelExtender
TargetControlID="pnlContent"
CollapseControlID="lnkToggle"
ExpandControlID="lnkToggle"
Collapsed="true"
CollapsedText="Show Details"
ExpandedText="Hide Details"
TextLabelID="lblStatus"
ExpandDirection="ExpandDirection.Vertical"
CollapsedSize="0" />
```
Properties: TargetControlID, CollapseControlID, ExpandControlID, Collapsed, CollapsedSize, ExpandedSize, CollapsedText, ExpandedText, TextLabelID, ExpandDirection, AutoCollapse, AutoExpand, ScrollContents, BehaviorID, Enabled
A container that displays collapsible content panes. Only one pane is expanded at a time.
=== "Web Forms"
```html
<ajaxToolkit:Accordion ID="acc1" runat="server"
SelectedIndex="0"
HeaderCssClass="accordionHeader"
ContentCssClass="accordionContent"
FadeTransitions="true"
TransitionDuration="300"
RequireOpenedPane="true"
SuppressHeaderPostbacks="true">
<Panes>
<ajaxToolkit:AccordionPane>
<Header>Section 1</Header>
<Content>
<p>Content for section 1</p>
</Content>
</ajaxToolkit:AccordionPane>
<ajaxToolkit:AccordionPane>
<Header>Section 2</Header>
<Content>
<p>Content for section 2</p>
</Content>
</ajaxToolkit:AccordionPane>
</Panes>
</ajaxToolkit:Accordion>
```
=== "Blazor"
```razor
<Accordion
SelectedIndex="0"
HeaderCssClass="accordionHeader"
ContentCssClass="accordionContent"
FadeTransitions="true"
TransitionDuration="300"
RequireOpenedPane="true"
SuppressHeaderPostbacks="true"
SelectedIndexChanged="HandleSelectionChanged">
<AccordionPane>
<Header>Section 1</Header>
<Content>
<p>Content for section 1</p>
</Content>
</AccordionPane>
<AccordionPane>
<Header>Section 2</Header>
<Content>
<p>Content for section 2</p>
</Content>
</AccordionPane>
</Accordion>
@code {
void HandleSelectionChanged(int index) { /* Handle pane change */ }
}
```
!!! note "Structural Difference"
In Web Forms, panes are wrapped in a <Panes> collection element. In Blazor, AccordionPane components are placed directly inside Accordion as child content — no <Panes> wrapper needed.
Accordion Properties: SelectedIndex, SelectedIndexChanged, FadeTransitions, TransitionDuration, HeaderCssClass, ContentCssClass, RequireOpenedPane, AutoSize, SuppressHeaderPostbacks, CssClass
AccordionPane Properties: Header (RenderFragment), Content (RenderFragment)
Displays content in tabbed panels with only the active tab visible.
=== "Web Forms"
```html
<ajaxToolkit:TabContainer ID="tabs1" runat="server"
ActiveTabIndex="0"
OnClientActiveTabChanged="onTabChanged"
CssClass="myTabs">
<ajaxToolkit:TabPanel ID="tab1" runat="server"
HeaderText="Tab 1">
<ContentTemplate>
<p>Content for tab 1</p>
</ContentTemplate>
</ajaxToolkit:TabPanel>
<ajaxToolkit:TabPanel ID="tab2" runat="server"
HeaderText="Tab 2">
<ContentTemplate>
<p>Content for tab 2</p>
</ContentTemplate>
</ajaxToolkit:TabPanel>
</ajaxToolkit:TabContainer>
```
=== "Blazor"
```razor
<TabContainer
ActiveTabIndex="0"
OnClientActiveTabChanged="onTabChanged"
CssClass="myTabs"
OnActiveTabChanged="HandleTabChanged">
<TabPanel HeaderText="Tab 1">
<ContentTemplate>
<p>Content for tab 1</p>
</ContentTemplate>
</TabPanel>
<TabPanel HeaderText="Tab 2">
<ContentTemplate>
<p>Content for tab 2</p>
</ContentTemplate>
</TabPanel>
</TabContainer>
@code {
void HandleTabChanged(int newIndex) { /* Handle tab change */ }
}
```
TabContainer Properties: ActiveTabIndex, OnActiveTabChanged, OnClientActiveTabChanged, ScrollBars, CssClass
TabPanel Properties: HeaderText, HeaderTemplate (RenderFragment), ContentTemplate (RenderFragment), Enabled
Attaches a popup calendar date picker to a TextBox.
=== "Web Forms"
```html
<asp:TextBox ID="txtDate" runat="server" />
<ajaxToolkit:CalendarExtender
ID="ce1"
runat="server"
TargetControlID="txtDate"
Format="MM/dd/yyyy"
PopupPosition="BottomLeft"
StartDate="2024-01-01"
EndDate="2025-12-31"
CssClass="calendarPopup" />
```
=== "Blazor"
```razor
<TextBox ID="txtDate" />
<CalendarExtender
TargetControlID="txtDate"
Format="MM/dd/yyyy"
PopupPosition="CalendarPosition.BottomLeft"
StartDate="@(new DateTime(2024, 1, 1))"
EndDate="@(new DateTime(2025, 12, 31))"
CssClass="calendarPopup" />
```
Properties: TargetControlID, Format, PopupPosition, DefaultView, StartDate, EndDate, SelectedDate, TodaysDate, CssClass, OnClientDateSelectionChanged, BehaviorID, Enabled
!!! tip
Date properties (StartDate, EndDate, SelectedDate, TodaysDate) are DateTime? in Blazor. Use C# expressions like @(new DateTime(2024, 1, 1)) instead of string literals.
Provides typeahead/autocomplete functionality for a TextBox.
=== "Web Forms"
```html
<asp:TextBox ID="txtSearch" runat="server" />
<ajaxToolkit:AutoCompleteExtender
ID="ace1"
runat="server"
TargetControlID="txtSearch"
ServicePath="~/AutoCompleteService.asmx"
ServiceMethod="GetCompletionList"
MinimumPrefixLength="2"
CompletionSetCount="10"
CompletionInterval="500"
CompletionListCssClass="autocomplete-list"
CompletionListItemCssClass="autocomplete-item"
CompletionListHighlightedItemCssClass="autocomplete-highlight"
FirstRowSelected="true" />
```
=== "Blazor"
```razor
<TextBox ID="txtSearch" />
<AutoCompleteExtender
TargetControlID="txtSearch"
ServicePath="/api/autocomplete"
ServiceMethod="GetCompletionList"
MinimumPrefixLength="2"
CompletionSetCount="10"
CompletionInterval="500"
CompletionListCssClass="autocomplete-list"
CompletionListItemCssClass="autocomplete-item"
CompletionListHighlightedItemCssClass="autocomplete-highlight"
FirstRowSelected="true"
CompletionDataRequested="HandleCompletionRequest" />
@code {
async Task HandleCompletionRequest(string prefix)
{
// Fetch suggestions server-side
}
}
```
Properties: TargetControlID, ServicePath, ServiceMethod, MinimumPrefixLength, CompletionSetCount, CompletionInterval, CompletionListCssClass, CompletionListItemCssClass, CompletionListHighlightedItemCssClass, DelimiterCharacters, FirstRowSelected, ShowOnlyCurrentWordInCompletionListItem, OnClientItemSelected, OnClientPopulating, OnClientPopulated, CompletionDataRequested, BehaviorID, Enabled
!!! note
The Blazor version adds CompletionDataRequested — an EventCallback<string> for fetching suggestions server-side, replacing the Web Forms ASMX service pattern.
Applies an input mask to a TextBox, formatting user input according to a pattern.
=== "Web Forms"
```html
<asp:TextBox ID="txtPhone" runat="server" />
<ajaxToolkit:MaskedEditExtender
ID="mee1"
runat="server"
TargetControlID="txtPhone"
Mask="(999) 999-9999"
MaskType="Number"
InputDirection="LeftToRight"
ClearMaskOnLostFocus="true"
PromptCharacter="_" />
```
=== "Blazor"
```razor
<TextBox ID="txtPhone" />
<MaskedEditExtender
TargetControlID="txtPhone"
Mask="(999) 999-9999"
MaskType="MaskType.Number"
InputDirection="InputDirection.LeftToRight"
ClearMaskOnLostFocus="true"
PromptCharacter="_" />
```
Properties: TargetControlID, Mask, MaskType, InputDirection, PromptCharacter, AutoComplete, AutoCompleteValue, Filtered, ClearMaskOnLostFocus, ClearTextOnInvalid, AcceptAMPM, AcceptNegative, ErrorTooltipEnabled, ErrorTooltipCssClass, BehaviorID, Enabled
Adds numeric up/down spinner buttons to a TextBox.
=== "Web Forms"
```html
<asp:TextBox ID="txtQuantity" runat="server" Text="1" />
<ajaxToolkit:NumericUpDownExtender
ID="nud1"
runat="server"
TargetControlID="txtQuantity"
Width="120"
Minimum="1"
Maximum="100"
Step="1" />
```
=== "Blazor"
```razor
<TextBox ID="txtQuantity" Text="1" />
<NumericUpDownExtender
TargetControlID="txtQuantity"
Width="120"
Minimum="1"
Maximum="100"
Step="1" />
```
Properties: TargetControlID, Width, Minimum, Maximum, Step, RefValues, ServiceUpPath, ServiceUpMethod, ServiceDownPath, ServiceDownMethod, Tag, BehaviorID, Enabled
Attaches range slider behavior to a target input element.
=== "Web Forms"
```html
<asp:TextBox ID="txtSlider" runat="server" />
<asp:Label ID="lblValue" runat="server" />
<ajaxToolkit:SliderExtender
ID="se1"
runat="server"
TargetControlID="txtSlider"
BoundControlID="lblValue"
Minimum="0"
Maximum="100"
Steps="10"
Orientation="Horizontal"
RailCssClass="sliderRail"
HandleCssClass="sliderHandle"
TooltipText="Value: {0}" />
```
=== "Blazor"
```razor
<TextBox ID="txtSlider" />
<Label ID="lblValue" />
<SliderExtender
TargetControlID="txtSlider"
BoundControlID="lblValue"
Minimum="0"
Maximum="100"
Steps="10"
Orientation="SliderOrientation.Horizontal"
RailCssClass="sliderRail"
HandleCssClass="sliderHandle"
TooltipText="Value: {0}" />
```
Properties: TargetControlID, Minimum, Maximum, Steps, Value, BoundControlID, Orientation, RailCssClass, HandleCssClass, HandleImageUrl, Length, Decimals, TooltipText, BehaviorID, Enabled
Replaces a checkbox with a clickable image that toggles between checked and unchecked states.
=== "Web Forms"
```html
<asp:CheckBox ID="chkAgree" runat="server" />
<ajaxToolkit:ToggleButtonExtender
ID="tbe1"
runat="server"
TargetControlID="chkAgree"
ImageWidth="24"
ImageHeight="24"
UncheckedImageUrl="~/images/unchecked.png"
CheckedImageUrl="~/images/checked.png"
UncheckedImageAlternateText="Not agreed"
CheckedImageAlternateText="Agreed"
CheckedImageOverUrl="~/images/checked-hover.png"
UncheckedImageOverUrl="~/images/unchecked-hover.png" />
```
=== "Blazor"
```razor
<CheckBox ID="chkAgree" />
<ToggleButtonExtender
TargetControlID="chkAgree"
ImageWidth="24"
ImageHeight="24"
UncheckedImageUrl="images/unchecked.png"
CheckedImageUrl="images/checked.png"
UncheckedImageAlternateText="Not agreed"
CheckedImageAlternateText="Agreed"
CheckedImageOverUrl="images/checked-hover.png"
UncheckedImageOverUrl="images/unchecked-hover.png" />
```
Properties: TargetControlID, ImageWidth, ImageHeight, UncheckedImageUrl, CheckedImageUrl, UncheckedImageAlternateText, CheckedImageAlternateText, CheckedImageOverUrl, UncheckedImageOverUrl, DisabledUncheckedImageUrl, DisabledCheckedImageUrl, BehaviorID, Enabled
!!! tip
Replace ~/ paths with relative paths in Blazor. Static assets go in wwwroot/.
Attaches a popup panel to a target control, displaying it on click. Lighter than ModalPopupExtender — no overlay or focus trap.
=== "Web Forms"
```html
<asp:TextBox ID="txtColor" runat="server" />
<asp:Panel ID="pnlColorPicker" runat="server" style="display:none;">
<!-- Color picker content -->
</asp:Panel>
<ajaxToolkit:PopupControlExtender
ID="pce1"
runat="server"
TargetControlID="txtColor"
PopupControlID="pnlColorPicker"
Position="Bottom"
OffsetX="0"
OffsetY="2"
CommitProperty="value"
CommitScript="onColorSelected()" />
```
=== "Blazor"
```razor
<TextBox ID="txtColor" />
<Panel ID="pnlColorPicker" style="display:none;">
@* Color picker content *@
</Panel>
<PopupControlExtender
TargetControlID="txtColor"
PopupControlID="pnlColorPicker"
Position="PopupPosition.Bottom"
OffsetX="0"
OffsetY="2"
CommitProperty="value"
CommitScript="onColorSelected()" />
```
Properties: TargetControlID, PopupControlID, Position, OffsetX, OffsetY, CommitProperty, CommitScript, ExtenderControlID, BehaviorID, Enabled
Displays a popup panel when the user hovers over a target control.
=== "Web Forms"
```html
<asp:Label ID="lblItem" runat="server" Text="Hover over me" />
<asp:Panel ID="pnlMenu" runat="server" CssClass="hoverMenu" style="display:none;">
<a href="#">Edit</a>
<a href="#">Delete</a>
</asp:Panel>
<ajaxToolkit:HoverMenuExtender
ID="hme1"
runat="server"
TargetControlID="lblItem"
PopupControlID="pnlMenu"
PopupPosition="Right"
OffsetX="5"
OffsetY="0"
PopDelay="200"
HoverDelay="500"
HoverCssClass="hoverHighlight" />
```
=== "Blazor"
```razor
<Label ID="lblItem" Text="Hover over me" />
<Panel ID="pnlMenu" CssClass="hoverMenu" style="display:none;">
<a href="#">Edit</a>
<a href="#">Delete</a>
</Panel>
<HoverMenuExtender
TargetControlID="lblItem"
PopupControlID="pnlMenu"
PopupPosition="PopupPosition.Right"
OffsetX="5"
OffsetY="0"
PopDelay="200"
HoverDelay="500"
HoverCssClass="hoverHighlight" />
```
Properties: TargetControlID, PopupControlID, PopupPosition, OffsetX, OffsetY, PopDelay, HoverDelay, HoverCssClass, BehaviorID, Enabled
The bwfc-migrate.ps1 script (located in migration-toolkit/scripts/) automates much of the Ajax Control Toolkit conversion as part of the L1 (Level 1) mechanical migration pass.
-
Removes
<ajaxToolkit:ToolkitScriptManager>— Both self-closing and block forms are stripped entirely since Blazor doesn't need them. -
Converts known
ajaxToolkit:prefixes — For all 16 supported components (the 14 controls listed above plus their companion types), the script strips theajaxToolkit:prefix from both opening and closing tags:<ajaxToolkit:Accordion>→<Accordion></ajaxToolkit:AccordionPane>→</AccordionPane>
-
Flags unrecognized controls — Any
ajaxToolkit:control not in the known list is replaced with aTODOcomment:@* TODO: Convert ajaxToolkit:RatingExtender — no BWFC equivalent yet *@ -
Adds
@usingstatements — When Ajax Toolkit controls are detected, the script ensures@using BlazorAjaxToolkitComponentsis added to_Imports.razor.
.\migration-toolkit\scripts\bwfc-migrate.ps1 -SourcePath "C:\MyWebFormsApp" -OutputPath "C:\MyBlazorApp"After the L1 script runs, you may need to:
- Convert enum string values to C# enum syntax (e.g.,
FilterType="Numbers"→FilterType="FilterType.Numbers") - Replace
~/asset paths with relative Blazor paths - Convert server-side event handlers to
EventCallbacksyntax - Remove the
<Panes>wrapper from Accordion content - Verify
TargetControlIDreferences match the migrated control IDs - Set
@rendermode InteractiveServeron pages that use extenders
| Symptom | Cause | Solution |
|---|---|---|
| No behavior on button click | Missing render mode | Add @rendermode InteractiveServer to the page |
| Extender initializes but no effect | Wrong TargetControlID |
Verify the ID matches the target control's ID attribute exactly (case-sensitive) |
| JS console error on import | Missing static assets | Ensure BlazorAjaxToolkitComponents NuGet package is installed and project is built |
| Works in dev, fails in production | Static asset path issue | Verify _content/BlazorAjaxToolkitComponents/ is being served |
Blazor requires C# enum syntax. String values from Web Forms markup need conversion:
- FilterType="Numbers"
+ FilterType="FilterType.Numbers"
- MaskType="Number"
+ MaskType="MaskType.Number"
- Orientation="Horizontal"
+ Orientation="SliderOrientation.Horizontal"- Ensure the target control renders before the extender in the component tree
- JavaScript resolves the target via
document.getElementById()— the element must exist in the DOM - If the target is inside a conditional
@ifblock, the extender may initialize before the target renders
- Remove the
<Panes>wrapper — Blazor uses direct child content - Ensure each
AccordionPaneis a direct child ofAccordion - Check that
HeaderandContentrender fragments are present
- Verify
OkControlIDandCancelControlIDmatch the buttonIDattributes inside the modal - Ensure buttons are inside the
PopupControlIDelement - Check that the popup element has
style="display:none;"initially
Date properties are DateTime? in Blazor, not strings:
- StartDate="2024-01-01"
+ StartDate="@(new DateTime(2024, 1, 1))"Extenders require JavaScript interop which is only available in InteractiveServer mode. In Static SSR, extenders silently skip initialization. Wrap extender usage in an interactive component:
@rendermode InteractiveServer
@* All extenders on this page will work *@The table below shows which Ajax Control Toolkit features are supported in BlazorAjaxToolkitComponents.
| Status | Meaning |
|---|---|
| ✅ Supported | Fully implemented with same behavior |
| Implemented with limitations — see notes | |
| 🔮 Planned | Not yet implemented — reserved for future versions |
| ❌ Not Planned | Will not be implemented |
| Ajax Control Toolkit Control | BWFC Status | Notes |
|---|---|---|
| ConfirmButtonExtender | ✅ Supported | DisplayModalPopupID reserved for future use |
| FilteredTextBoxExtender | ✅ Supported | All filter types and modes supported |
| ModalPopupExtender | ✅ Supported | Full support including drag, drop shadow, focus trap |
| CollapsiblePanelExtender | ✅ Supported | Vertical and horizontal expand directions |
| Accordion | ✅ Supported | Includes JS animation support |
| AccordionPane | ✅ Supported | Direct child content (no <Panes> wrapper) |
| TabContainer | ✅ Supported | Client callback, scrollbar support |
| TabPanel | ✅ Supported | HeaderText, HeaderTemplate, and Enabled |
| CalendarExtender | ✅ Supported | Date ranges, views, and format strings |
| AutoCompleteExtender | ✅ Supported | Adds CompletionDataRequested EventCallback |
| MaskedEditExtender | ✅ Supported | All mask types and input directions |
| NumericUpDownExtender | ✅ Supported | Step, range, and reference values |
| SliderExtender | ✅ Supported | Horizontal/vertical, bound control sync |
| ToggleButtonExtender | ✅ Supported | All image states including disabled |
| PopupControlExtender | ✅ Supported | Positional placement and commit support |
| HoverMenuExtender | ✅ Supported | Show/hide delays and hover CSS class |
| ToolkitScriptManager | ❌ Not Planned | Not needed — Blazor handles script loading |
| AlwaysVisibleControlExtender | 🔮 Planned | — |
| AnimationExtender | 🔮 Planned | — |
| CascadingDropDown | 🔮 Planned | — |
| ColorPickerExtender | 🔮 Planned | — |
| DragPanelExtender | 🔮 Planned | — |
| DropDownExtender | 🔮 Planned | — |
| DropShadowExtender | 🔮 Planned | — |
| DynamicPopulateExtender | 🔮 Planned | — |
| ListSearchExtender | 🔮 Planned | — |
| MutuallyExclusiveCheckBoxExtender | 🔮 Planned | — |
| PagingBulletedListExtender | 🔮 Planned | — |
| PasswordStrength | 🔮 Planned | — |
| Rating | 🔮 Planned | — |
| ReorderList | 🔮 Planned | — |
| ResizableControlExtender | 🔮 Planned | — |
| RoundedCornersExtender | 🔮 Planned | — |
| TextBoxWatermarkExtender | 🔮 Planned | — |
| UpdatePanelAnimationExtender | 🔮 Planned | — |
| ValidatorCalloutExtender | 🔮 Planned | — |
- Ajax Control Toolkit Overview — Component index, usage patterns, and common properties
- Accordion — Collapsible content panes
- AutoCompleteExtender — Typeahead suggestions
- CalendarExtender — Popup date picker
- CollapsiblePanelExtender — Panel collapse/expand
- ConfirmButtonExtender — Browser confirmation dialogs
- FilteredTextBoxExtender — Character filtering
- HoverMenuExtender — Hover-triggered popup
- MaskedEditExtender — Input mask formatting
- ModalPopupExtender — Modal popup dialogs
- NumericUpDownExtender — Numeric spinner
- PopupControlExtender — Click-triggered popup
- SliderExtender — Range slider
- TabContainer — Tabbed content panels
- ToggleButtonExtender — Image toggle for checkboxes
- Automated Migration Guide — Full bwfc-migrate.ps1 documentation