WssBlazorControls 10.3.0
dotnet add package WssBlazorControls --version 10.3.0
NuGet\Install-Package WssBlazorControls -Version 10.3.0
<PackageReference Include="WssBlazorControls" Version="10.3.0" />
<PackageVersion Include="WssBlazorControls" Version="10.3.0" />
<PackageReference Include="WssBlazorControls" />
paket add WssBlazorControls --version 10.3.0
#r "nuget: WssBlazorControls, 10.3.0"
#:package WssBlazorControls@10.3.0
#addin nuget:?package=WssBlazorControls&version=10.3.0
#tool nuget:?package=WssBlazorControls&version=10.3.0
WssBlazorControls
A comprehensive library of form controls for Blazor applications providing consistent, feature-rich input components with built-in validation, accessibility support, and flexible styling options.
Features
- Rich Form Controls: String, Number, Date, Boolean, Select, Radio, Checkbox lists, and TextArea components
- Searchable & Multi-Select: AntDesign-style
EditSelectSearch/EditMultiSelect— type-to-search, tags, virtualized dropdown - AntDesign-style UI Kit: dependency-free Alert, Modal, Drawer, Table, Pagination, Popover, Popconfirm, Skeleton, and toasts
- Data Annotations Integration: Full support for validation attributes (Required, Range, MinLength, etc.)
- Accessibility First: ARIA attributes, screen reader support, and keyboard navigation
- Flexible Display Modes: Edit mode and read-only views for all controls
- Consistent Styling: CSS classes and customizable appearance
- TypeScript/JavaScript Interop: Enhanced client-side functionality
- Cross-Platform: Works with both Blazor Server and Blazor WebAssembly
Installation
Install the package via NuGet Package Manager:
dotnet add package WssBlazorControls
Or via Package Manager Console:
Install-Package WssBlazorControls
Quick Start
- Add the using statement to your
_Imports.razor:
@using Controls
- Include the CSS in your
App.razororindex.html:
<link href="_content/WssBlazorControls/edit-controls.css" rel="stylesheet" />
<link href="_content/WssBlazorControls/wss-controls.css" rel="stylesheet" />
- Use the controls in your Blazor components:
@using System.ComponentModel.DataAnnotations
<EditForm Model="@model" OnValidSubmit="@HandleSubmit">
<DataAnnotationsValidator />
<EditString @bind-Value="model.Name"
Field="@(() => model.Name)"
Label="Full Name"
IsRequired="true" />
<EditNumber @bind-Value="model.Age"
Field="@(() => model.Age)"
Label="Age"
IsRequired="true" />
<EditDate @bind-Value="model.BirthDate"
Field="@(() => model.BirthDate)"
Label="Birth Date" />
<EditBool @bind-Value="model.IsActive"
Field="@(() => model.IsActive)"
Label="Active Status" />
<button type="submit">Submit</button>
<ValidationSummary />
</EditForm>
@code {
private PersonModel model = new();
private void HandleSubmit()
{
// Handle form submission
}
public class PersonModel
{
[Required]
[StringLength(100)]
public string Name { get; set; } = "";
[Required]
[Range(1, 120)]
public int? Age { get; set; }
public DateTime? BirthDate { get; set; }
public bool IsActive { get; set; } = true;
}
}
Available Controls
Input Controls
EditString- Text input with masking and URL supportEditTextArea- Multi-line text inputEditNumber- Numeric input with validationEditDate- Date picker componentEditBool- Checkbox for boolean valuesEditBoolNullRadio- Three-state radio for nullable booleans
Selection Controls
EditSelect- Dropdown selection for objectsEditSelectEnum- Dropdown for enum valuesEditSelectString- Dropdown for string valuesEditSelectSearch- Searchable single-select (AntDesign-style: type-to-search, clear, virtualized)EditMultiSelect- Multiple / tags select bound to aList<T>(AntDesign-style)EditRadio- Radio buttons for objectsEditRadioEnum- Radio buttons for enumsEditRadioString- Radio buttons for strings
Multi-Selection Controls
EditCheckedStringList- Checkbox list for stringsEditCheckedEnumList- Checkbox list for enums
Support Components
FormLabel- Consistent labeling with tooltips and descriptionsFieldValidationDisplay- Validation message displayReadOnlyValue- Read-only value presentationEditDisplay- Static label+value pair (no model binding)
EditDisplay vs ReadOnlyValue
Both render text in the edit-readonly-value style, but their use cases are different:
EditDisplay |
ReadOnlyValue |
|
|---|---|---|
| When to use | Standalone label+value pair outside an Edit* control — e.g. a derived value like "15.3 oz / can" that's not bound to a model property |
Always — it's rendered by the Edit* controls in read-only mode, not typically used directly by consumers |
| Owns its label | Yes (Label, Description, Tooltip parameters) |
No — sits inside an Edit* control that owns the FormLabel |
| Model binding | None | None (reads Text after the parent has formatted the value) |
| Validation | None | None (the parent control's FieldValidationDisplay handles it) |
Reach for EditDisplay when you want the same visual treatment as a read-only EditString but without an EditForm / model property behind it.
UI Kit (non-form) controls
A set of dependency-free, AntDesign-style general UI widgets (ported from Standalone.Controls). Unlike the Edit* controls these are not form-bound — they're plain components. They use the wss- CSS prefix and --wss-* theme tokens shipped in wss-controls.css (link it as shown in Quick Start). No service registration is required.
Select<T>- The dropdown engine behindEditSelectSearch/EditMultiSelect; usable standalone (single / multiple / tags, search, virtualized)Alert- Contextual message banner (success / info / warning / error, closable, description)Skeleton- Loading placeholder with shimmer; announcesrole="status"/aria-busywith a visually-hiddenLoadingText(default"Loading") for screen readersPopover- Click-triggered popover (4 placements)Pagination- Controlled pagerModal- Dialog with@bind-Visible, footer, mask-closeDrawer- Slide-in panel (4 placements)Popconfirm- Inline confirm popoverTable<TItem>- Data table withColumn/PropertyColumn/ActionColumn, row selection, paging (pager placement viaPagerPosition= Top/Bottom/Both and alignment viaPagerAlign), and column sorting (Sortable="true"on aPropertyColumn— non-comparable types degrade to non-sortable; or aSortBycomparison on any column). Columns may be conditionally rendered (@if)- Toasts & notifications - two paths with identical rendering: scoped / Server-safe (
IMessageService/INotificationServiceviabuilder.Services.AddWssControlsToasts()+<MessageContainer />/<NotificationContainer />), or registration-free static for WASM (WasmMessageService/WasmNotificationService+<WasmMessageContainer />/<WasmNotificationContainer />). On Blazor Server use the scoped path — the staticWasm*services hold process-static state that would bleed across users.
Icon,Button,Checkbox, andTagare intentionally not part of this library.
Server-side paging (Table)
The Table's built-in pager (PageSize) is in-memory — it materializes the whole DataSource and slices it client-side, so it can't reflect a server-side total. For server-side paging, compose the Table with the standalone, fully-controlled Pagination: give the Table only the current page (omit PageSize so it renders exactly what you pass), and drive a Pagination yourself.
<Table TItem="Row" DataSource="_pageRows">
<PropertyColumn TItem="Row" TProp="int" Title="Id" Property="@(r => r.Id)" />
<PropertyColumn TItem="Row" TProp="string" Title="Name" Property="@(r => r.Name)" />
</Table>
<div style="display:flex; justify-content:flex-end; margin-top:16px;">
<Pagination Total="_total" PageSize="PageSize" Current="_page" CurrentChanged="GoToPageAsync" />
</div>
@code {
const int PageSize = 20;
List<Row> _pageRows = new();
int _total, _page = 1;
protected override Task OnInitializedAsync() => GoToPageAsync(1);
async Task GoToPageAsync(int page)
{
_page = page;
var result = await Api.GetRows(page, PageSize /*, sortField, sortDir */);
_pageRows = result.Items.ToList(); // a NEW reference — the Table only re-copies when DataSource changes ref
_total = result.TotalCount; // the server's overall count drives the pager
}
}
Pagination is a controlled component (Total / Current / PageSize + CurrentChanged), so it shows the correct page count from the server total and raises CurrentChanged when the user picks a page. Handle sorting the same way — pass the sort field/direction into your request rather than using the Table's built-in Sortable, which only orders the page already loaded. A runnable example (with a simulated server) is in the /uikit gallery.
Component Features
All form controls implement the IEditControl interface and provide:
- Identity Management:
Id,IdPrefixfor unique identification - Display Control:
IsEditMode,IsDisabled,IsHidden - Labeling:
Label,Descriptionwith markup support - Styling:
ContainerClassfor custom CSS - Validation:
IsRequiredintegration with DataAnnotations - Conditional Display:
Hidingmodes andHidingModeenum
Examples
Dropdown with Enum
<EditSelectEnum @bind-Value="model.Priority"
Field="@(() => model.Priority)"
Label="Priority Level"
IsRequired="true" />
@code {
public enum Priority
{
Low,
Medium,
High,
Critical
}
public class TaskModel
{
[Required]
public Priority? Priority { get; set; }
}
}
Radio Button Group
<EditRadioString @bind-Value="model.Department"
Field="@(() => model.Department)"
Label="Department"
Options="@departments" />
@code {
private List<string> departments = new()
{
"Engineering",
"Marketing",
"Sales",
"Support"
};
}
Checkbox List
<EditCheckedStringList @bind-Value="model.Skills"
Field="@(() => model.Skills)"
Label="Technical Skills"
Options="@skills" />
@code {
private List<string> skills = new()
{
"C#",
"JavaScript",
"Blazor",
"ASP.NET Core"
};
}
Styling and Customization
The library provides default styling through the included CSS file. You can customize the appearance by:
- Overriding CSS classes in your own stylesheets
- Using ContainerClass parameter for component-specific styling
- Applying custom CSS to the
.edit-control-wrapperclass
The AntDesign-style UI-kit controls (Alert, Modal, Table, Select, ...) are themed via --wss-* CSS custom properties in wss-controls.css. They default to the AntDesign 4.x look and bridge to your existing --color-primary / --color-danger / --border-color where those are defined, so they pick up your theme automatically. Override any --wss-* variable to re-theme.
<EditString @bind-Value="model.Name"
Field="@(() => model.Name)"
Label="Name"
ContainerClass="my-custom-style" />
Accessibility
WssBlazorControls is built with accessibility as a priority:
- ARIA attributes for screen readers
- Keyboard navigation support
- Focus management and indicators
- Semantic HTML structure
- High contrast color support
Browser Support
- Modern browsers with WebAssembly support
- Designed for both Blazor Server and Blazor WebAssembly scenarios
- Compatible with .NET 8.0+
Contributing
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Documentation: Check the demo applications in the repository
- Issues: Report bugs via GitHub Issues
- Feature Requests: Submit enhancement requests via GitHub Issues
Changelog
10.3.0
New: EditFile — multi-file upload control
EditFileis a new form control that binds aList<IBrowserFile>via the standardValue/ValueChanged/Fieldpattern, integrating withEditContextvalidation like every otherEdit*control.- Supports drag-and-drop and click-to-browse. An invisible
<InputFile>overlay covers the entire drop zone so both interactions work natively without extra JS. - Multiple files are supported. The drop zone stays visible until an optional
MaxFilescap is reached; files already chosen appear as a dismissible list below it (hover to reveal the remove button per file). AllowedExtensions(e.g.".pdf",".xlsx") filters by extension;MaxFileSizeBytescaps individual file size (default 10 MB). Validation errors from either check are shown inline below the drop zone.- The drop zone border turns red when there's a validation error from the format/size check or when the field fails
EditContextvalidation; the upload icon switches to its error (red) variant to match. - Read-only mode shows the selected filenames with a paperclip icon; empty renders a blank
ReadOnlyValueconsistent with the other controls. - Styled to match the Hatch / Spot drop-zone look: dashed
#b7b7b7border,#f3f3f3background, primary-color hover border. Tokens bridge to--color-primaryand--color-dangerso the control follows the consumer's theme. - Adds four inline-SVG icon classes to
edit-controls.css:.edit-icon-upload,.edit-icon-upload-error,.edit-icon-paperclip,.edit-icon-delete.
Table — robust dynamic columns + graceful sort
- Columns may now be conditionally rendered (
@if). The Table re-collects its columns in document order on each render, so a hidden column drops out and a re-shown one returns to its declared position — previously a removed column left a stale header and cells behind, and re-showing it produced a duplicate. Hiding the column that drives the active sort now clears the sort so the indicator and the row order can't disagree. - A
SortablePropertyColumnwhose property type isn't comparable no longer throws on the first header click (which on Blazor Server tore down the circuit) — the header simply isn't made sortable. Supply aSortBycomparison to sort any type. - A sortable column declared without a
Titlenow gives its sort<button>anaria-label="Sort", so it isn't an unnamed button for screen-reader users.
Accessibility
Skeletonannounces its loading state to screen readers:role="status"+aria-busy="true"and a visually-hiddenLoadingText(default"Loading"); the placeholder bars arearia-hidden. New.wss-sr-onlyutility class.- Toast (
Message) andNotificationcontainers route each toast by severity into two always-present live regions — a politerole="status"region and an assertiverole="alert"region — instead of flipping a single shared region's politeness when an error arrives (a change screen readers don't reliably re-announce, which could swallow the error). The regions aredisplay:contents, so the on-screen layout is unchanged (errors group below the polite toasts).
10.2.0
Headline release: debuts the dependency-free AntDesign-style UI-kit controls (Select, Alert, Modal, Drawer, Table, Pagination, Popover / Popconfirm, Skeleton, toasts) and the searchable form selects (EditSelectSearch / EditMultiSelect), alongside a library-wide accessibility & architecture overhaul (the EditControlBase refactor). Adds Table column sorting and configurable pager placement. Includes one breaking dependency change — see below.
New: Table column sorting
- Columns can now sort. Set
Sortable="true"on aPropertyColumn(the comparison is derived from itsPropertyviaComparer<T>.Default), or supply aSortBycomparison on anyColumnfor custom / template columns. Clicking a sortable header cycles ascending → descending → unsorted (restoring the originalDataSourceorder); the sort is stable (ties keep their original order). Headers exposearia-sort(ascending/descending/none) and a keyboard-focusable<button>so the feature is screen-reader- and keyboard-accessible. Sorting resets to page 1 and survives aDataSourceswap.
Table / Pagination polish
- The table pager is now configurable:
PagerPosition="Top | Bottom | Both"(defaultBottom) places it above, below, or both above and below the table, andPagerAlign="Left | Center | Right"(defaultRight, matching AntD) aligns it horizontally. WhenBoth, the two pagers stay synced to the same page. - The pager buttons now hold a consistent 32px square via a
min-heightfloor, so an aggressive consumer reset such asbutton { max-height: fit-content }can no longer collapse them to content height (which made the icon-only prev/next buttons render shorter than the numbered ones). - The
Tablenow renders its grid and pager inside a single root element, so a parent's flex/gridgapdoesn't stack on top of the pager's margin and inflate the space between the table and its pager.
Accessibility, theming & performance (audit follow-up)
- Grouped controls now surface validation state. The radio controls (
EditRadio,EditRadioEnum,EditRadioString,EditBoolNullRadio) exposearia-invalid/aria-required/aria-describedbyon arole="radiogroup"<fieldset>named by its legend (previously splatted onto<InputRadioGroup>, which renders no element — so they didn't reliably appear). The checkbox lists (EditCheckedStringList,EditCheckedEnumList) mark each checkboxaria-invalid(they had none). And because the list controls areComponentBase(notInputBase), they now subscribe to theEditContextso their invalid state updates live on validation — matching the scalar controls. This completes "aria-invalidon every editable control". aria-describedbyno longer dangles — it references only thedesc-/tooltip-ids that actually render, and is resolved once per control rather than re-interpolated on every render.aria-errormessageis emitted only while the field is invalid (per the ARIA spec).- Form controls are self-sufficient out of the box.
edit-controls.cssnow ships a:focus-visiblering for the editable elements (WCAG 2.4.7 — no longer dependent on the browser default the consumer may have reset) and an.invalidborder, so keyboard focus and the validation error state are visible without the consumer supplying their own styles. The validation X icon and the tooltip info icon usecurrentColordriven by--color-danger/--color-text, so they follow the consumer theme. wss-controls.css: theSelectsizing now uses the existing--wss-*tokens (overriding a token rescales the control as intended), and the classes the markup referenced but the stylesheet never defined (wss-popconfirm-title,wss-table-caption,wss-select-selection-item-rest, …) are now declared.- Fewer per-render allocations.
Selectcaches its visible tags andTablecaches the current page (it was materializing the page twice per render).Tablenow treatsDataSource/SelectedItemsas immutable parameters (reference-guarded) — reassign them to refresh rather than mutating in place. - Removed the unused
ReadOnlyValue.IsRequiredparameter (it wasrequiredbut never rendered). - Nullable enum selects can represent null.
EditSelectEnum<TEnum?>now renders a leading empty/placeholder option (label via the newNullOptionTextparameter) so a null value shows blank instead of silently displaying the first member, and the user can clear the field. Non-nullable enums are unchanged. - More ARIA correctness. All bool-bound ARIA booleans (
aria-expanded/aria-hidden/aria-disabled) now render lowercase"true"/"false";Alertannounces by severity (role/aria-live: error = assertive, otherwise polite) instead of alwaysrole="alert"; the radio<fieldset>itself is therole="radiogroup"(no nested double-group) with its id gated to edit mode so it doesn't collide with the read-only value; read-onlyaria-labelledbyis suppressed when the label is hidden; theSelectgets a focus ring before it opens; andEscapeclosesPopover/Popconfirmfrom inside the panel. - Correctness fixes.
Selectnow shows the selected label and clear button even when a single value equalsdefault(TValue)(e.g. a non-nullable enum's0member — previously mis-rendered as the empty placeholder);ValidationViewsummary links now target each control's actual id, honoringIdPrefix/ an explicitId(the resolved id is captured at field registration) instead of a recomputed guess; the checkbox lists no longer throw in read-only mode when the bound list isnull, and sanitize their read-only per-option ids viaToId(); a disabledPopconfirmtrigger is nowaria-disabledand removed from the tab order. - More correctness & a11y fixes.
EditRadioStringnow follows an externally-changed value (form reset, async-loaded model, programmatic set) instead of caching the selection once — and a custom initial value correctly resolves to the "Other" radio with its text box pre-filled;EditRadioEnum's "Other" free-text input gained an accessible name (aria-label), matching itsEditRadioStringsibling; theSelectclear button is now revealed on keyboard focus (:focus-within), not only on hover, so a keyboard user can see the control they've tabbed to; theTable's "select all" checkbox enters the nativeindeterminate(mixed) state when only some rows on the page are selected, so screen readers announce the partial selection; and the length-attribute helper takes the tighter (smaller) upper bound when both[StringLength]and[MaxLength]apply. - Checkbox-list validation links resolve.
EditCheckedStringList/EditCheckedEnumListnow render their resolved id on the<fieldset>in edit mode (gated like the radio groups), so aValidationViewsummary link for one of these fields actually jumps to the control — their checkboxes/label/error elements all carry decorated ids, so the bare id previously had nowhere to land, leaving the link dangling. - Visual & robustness fixes.
Paginationclamps an out-of-rangeCurrentto the valid range, so Previous/Next enable correctly instead of looking clickable but doing nothing; a longPopconfirmtitle now wraps inside the panel instead of overflowing it; and the loadingSkeletonshows a flat fill underprefers-reduced-motionrather than a frozen, off-centre shimmer band. - Overlay focus-trap & scroll-lock hardening. The Modal / Drawer focus trap no longer lets Shift+Tab escape when focus is on the panel itself (e.g. after clicking an empty area of the body) — focus is pulled back into the dialog. The body-scroll lock is now ref-counted, so stacked overlays don't unlock the page when the first-opened one closes, and the focus handle's disposal is idempotent.
New: AntDesign-style controls (ported from Standalone.Controls)
- Form selects:
EditSelectSearch<T>(searchable single-select) andEditMultiSelect<T>(multiple / tags, bindsList<T>) — fullEdit*controls (validation, label, read-only,FormOptions) backed by a new dependency-free, virtualized dropdown engine (Select<T>). They sit alongside the existingEditSelect/EditSelectEnum/EditSelectString, which are unchanged. - UI kit (non-form):
Select<T>,Alert,Skeleton,Popover,Pagination,Modal,Drawer,Popconfirm,Table<TItem>(+Column/PropertyColumn/ActionColumn), and toasts/notifications in two flavors — scoped/Server-safe (IMessageService/INotificationServiceviaAddWssControlsToasts()+MessageContainer/NotificationContainer) and registration-free static for WASM (WasmMessageService/WasmNotificationService+ their containers).Icon,Button,Checkbox, andTagwere intentionally excluded. - New stylesheet: these controls use the
wss-class prefix and--wss-*theme tokens shipped inwss-controls.css. Add a second link alongsideedit-controls.css:
Tokens default to the AntDesign 4.x look and bridge to your existing<link href="_content/WssBlazorControls/wss-controls.css" rel="stylesheet" />--color-*/--border-colorwhere present. The Select keyboard helper ships as an RCL JS module at_content/WssBlazorControls/wss-select.js(auto-imported, degrades gracefully). - No service registration required (consistent with the rest of the library).
Accessibility & correctness (library audit)
- Modal / Drawer: trap focus while open, restore focus to the trigger on close, close on
Escape, lock body scroll, and exposerole="dialog"+aria-modal="true"+aria-labelledby(the title). OK/confirm still never auto-closes — the caller decides. - Popover / Popconfirm: the trigger is a real focusable control (
role="button",tabindex="0",aria-haspopup,aria-expanded) operable from the keyboard —Enter/Spaceto open,Escapeto close. Both flip to the opposite side and shift along the cross axis to stay within the viewport, rendering hidden for one frame so the placement is never seen to jump. Select/EditSelectSearch/EditMultiSelect: full combobox ARIA (role="combobox"/listbox/option,aria-expanded,aria-controls,aria-activedescendant); the dropdown now opens upward when it would otherwise run off the bottom of the viewport.Pagination: rewritten as a semantic<nav aria-label="Pagination">of<button>s witharia-current="page"on the active page andaria-labels on the prev/next controls (was<ul>/<li>/<a>).- Toasts / notifications: the live region is announced via
role="status"+aria-live="polite". ReadOnlyValuenow HTML-encodes the value it displays instead of rendering it as raw markup — bound user data can no longer inject markup.EditDateread-only formats the bound value by its own type withDateFormat. The old code round-tripped through the editor string, which could shift the date across midnight in non-UTC zones and rendered aTimeOnlyas a date; an incompatible format now degrades to the value's ownToStringrather than throwing.EditCheckedEnumList/EditCheckedStringListbuild a new list when toggling instead of mutating the caller's bound collection in place.- The placement enum for
Popover/Popconfirmis namedPopupPlacement(it positions popups, not tooltips). The library builds with 0 warnings across net8 / net9 / net10.
Breaking dependency change
- Removed
Microsoft.AspNetCore.Components.DataAnnotations.Validation(3.2.0-rc1) from theWssBlazorControlspackage — the library itself never used it. Consumers who use<ObjectGraphDataAnnotationsValidator>or the[ValidateComplexType]attribute for nested-object validation must now add the package to their own project:
This eliminates the prerelease-dependency warning that previously bled through to consumer builds.dotnet add package Microsoft.AspNetCore.Components.DataAnnotations.Validation --version 3.2.0-rc1.20223.4
Behavior
- Validation messages now respect the
Labelparameter override on every control. Previously onlyEditCheckedStringListandEditCheckedEnumListpassedLabelthrough toFieldValidationDisplay; the other 12 controls would still derive the label from the model's attribute. Now if you set<EditString Label="Username" ... />, the validation message shows "Username is required" instead of falling back to the property name. EditSelectString<option>elements now render thetitletooltip (consistent withEditSelectEnum).- Cosmetic:
EditDate'sReadOnlyValuenow uses@_id/@_isRequiredlike every other control.
Build / packaging
<GeneratePackageOnBuild>is now scoped toConfiguration == Release. Dev / inner-loop builds no longer regenerate.nupkgfiles on every save —dotnet pack -c Release -o ./nupkgcontinues to produce them on demand.- Package now ships with a 128×128 icon (
icon.png, white "W" on Blazor purple). Visible in NuGet listings and Visual Studio's Manage NuGet Packages dialog.
New shared CSS class
.edit-inputis now applied to every editable element (<input>,<textarea>,<InputSelect>,<InputDate>) acrossEditString,EditNumber,EditDate,EditTextArea,EditSelect,EditSelectString,EditSelectEnum, plus the "Other" text inputs inEditRadioString/EditRadioEnum. The bundlededit-controls.cssships an empty rule — consumers can now style every editable element with one selector instead of writing per-element CSS forinput/textarea/selectseparately. Per-control classes (.edit-string-input,.edit-textarea-input,.edit-select-select, etc.) remain available for fine-tuning.
Internal
HidingMode: dropped the meaningless explicit= 1, 2, 3, 4, 5numeric values. Default is now0(None) which matches the?? HidingMode.Nonefallback already in every control. Consumers don't notice unless they were persisting the enum as an int — in which case existing values shift down by 1.ValidationHelper: replaced the brittlemessage.Split(' ')+ hardcoded array-index parsing of Range messages with a compiled regex. Now tolerates multi-word field names ("Order Total") and small format variations. Type-min/max sentinel detection moved intoHashSet<string>lookups instead of a long||chain.
Architecture: EditControlBase<TValue>
- 11 of 14 controls now inherit a single
EditControlBase<TValue> : InputBase<TValue>, IEditControlinstead of inheriting one of Microsoft's specializedInput*classes (InputText / InputNumber / InputDate / InputCheckbox / InputSelect / etc.). The base hoists every IEditControl parameter, both cascading parameters, the protected derived state (_id,_isRequired,_attributes,_fieldIdentifier), and theShowEditor/ShouldHideLabelchecks — so each derived control's.razor.csshrinks to just its component-specific parameters + parser + helpers. Net ~430 lines removed across the 11 controls. - The string-input/textarea/number/date/select parsing logic that Microsoft's
Input*classes used to provide is now ported into each control (typically a 5-15 lineTryParseValueFromStringoverride that delegates toBindConverter). Behavior is preserved — the new parsers route through the sameBindConverterMicrosoft uses internally. EditCheckedStringListandEditCheckedEnumListmigrated to a siblingEditControlListBase<TItem>(different shape — bindsList<TItem>instead of a scalar). TheSetAsync(item)rename toToggleAsync(item)is the only consumer-facing surface change.EditRadiois the one remaining control still on Microsoft'sInputRadioGroup<T>— it depends on the cascading-context plumbing that<InputRadio>children consume, and replacing the group requires also replacing the public<InputRadio>API. Intentional._Imports.razornow exposesMicrosoft.AspNetCore.Components.FormsandControls.Helpersso individual razor files no longer need per-file@usingdirectives for<InputRadioGroup>/<InputRadio>/.ToId()/ etc.
Tests
FormTesting/FormTesting.Client.Tests/(xUnit + bUnit, multi-targeted net8/9/10) — 270 tests (run once per TFM) covering the helpers (EnumHelperscache + attribute precedence,AttributesHelper.GetId/GetLabelText/GetMinAndMaxLengths,EditControlInit,ValidationHelperregex parsing), bUnit smoke tests for the form controls (rendered DOM, ARIA, edit/read-only switching), the AntDesign-style selects, and the UI-kit widgets (Table, dialogs, toasts) — plus regression tests for the audit fixes (ReadOnlyValueHTML-encoding,EditDateread-only formatting, checked-list immutability). Run withdotnet test FormTesting/FormTesting.Client.Tests/FormTesting.Client.Tests.csproj.FormTesting/FormTesting.Client.E2ETests/(xUnit + Playwright .NET, net10) — a 67-test end-to-end suite (one class perEdit*control plus the searchable selects and a driver for the/uikitgallery) with committed visual-regression baselines. Run withdotnet test FormTesting/FormTesting.Client.E2ETests/FormTesting.Client.E2ETests.csproj.
10.1.0
Behavioral changes (read before upgrading)
EditBool: read-only mode now rendersReadOnlyValuewith the newTrueText/FalseTextparameters (default"Yes"/"No"), matching every other control. SetRenderAsCheckboxWhenReadOnly="true"to keep the legacy disabled-checkbox display.EditString:aria-requirednow reflects the actual required state instead of being hard-coded to"true".
New CSS class — required for the invalid-icon overlay
.edit-input-with-iconwraps<input>/<textarea>/<InputDate>together with the optional red-X invalid icon inEditString,EditNumber,EditDate,EditTextArea. The bundlededit-controls.cssships an empty hook (the icon overlays the input via.edit-icon-invalid's negative margin and needs no positioning here). If you have your own stylesheet, no changes are required unless you want to adjust the input row's layout.
New parameters
EditBool.TrueText(default"Yes")EditBool.FalseText(default"No")EditBool.RenderAsCheckboxWhenReadOnly(defaultfalse)
New components / helpers
<InvalidIcon CssClass="..." />— reusable red-X SVG, conditional on the host'sCssClasscontaining"invalid".EditControlInit(inControls.Helpers) — static helper that consolidates theOnInitializedsetup and theShowEditor/ShouldHideLabelchecks every control was duplicating.
Markup consistency
EditSelectEnumswitched from@bind:get/@bind:setto<InputSelect @bind-Value=...>so it matchesEditSelect/EditSelectString.EditBoolNullRadioradio inputs now carryaria-required,aria-invalid,aria-describedby, andaria-errormessage. (Moved to a group-levelrole="radiogroup"container in the next release — see Unreleased.)aria-invalidis now rendered on every scalar editable control. (The grouped radio / checkbox-list controls are brought to parity in the next release — see Unreleased.).ToId()is now applied to enum optionids inEditSelectEnumandEditRadioEnum— fixes invalid HTML ids when an enum's display name contains spaces or punctuation.- The red-X invalid icon (previously only on
EditString) now appears onEditNumber,EditDate, andEditTextAreaas well.
Performance
EnumHelpers._nameCacheis now a thread-safeConcurrentDictionary<(Type, string), string>keyed by enum type — fixes potential cross-type collisions and removes a thread-safety hazard on pre-rendering.EnumHelpers.GetNamenow honors both[EnumDisplayName]and[Display(Name=...)]. Previously[Display]only affected sort order and[EnumDisplayName]only affected display, so the two could disagree.- The reflection-heavy enum sort blocks in
EditSelectEnum/EditRadioEnum/EditCheckedEnumListcollapsed toOrderBy(x => x.GetName())and benefit from the cache.
Bug fixes
- Fixed package description typo (
HierarchyAndEmployeeRecordprovidingartifact). - Removed stray
IsRequiredChangedparameter that existed only onEditRadioEnum. EditCheckedStringListwas silently dropping theIdPrefixparameter (nullwas being passed instead). Now consistent with every other control.EditBoolNullRadiofalse-radio'sclassattribute incorrectly used@ContainerClassinstead of@CssClass.focusFirstInvalidField(JS) now correctly handles invalid wrapper elements that aren't form fields, includes<select>, and guards.select()for input types that don't support it.
Refactoring (internal)
- All 14 controls now call
EditControlInit.Init(...)inOnInitializedinstead of duplicating the same 4 lines. - All controls use
EditControlInit.ShowEditor(...)andEditControlInit.ShouldHideLabel(...)for the visibility checks. - JavaScript helpers namespaced under
window.WssEditControls.*. Legacywindow.focusFirstInvalidField/window.log/ etc. are still exposed for back-compat — safe to migrate at your own pace. JsInteropEc.FocusFirstInvalidFieldusesTask.Yield()instead ofTask.Delay(1).FormLabel._isRequiredchanged fromstring("true"/"false") tobool.IEditControl.IsDisableddoc comment fixed (was"Not used"despite being used by every control).- Deleted dead
ExampleJsInterop.cstemplate code. - Removed unused
EditCheckedStringList.hasErrorandReadOnlyValue._emptyValuefields. - Build warnings reduced from 87 → 57.
10.0.7
- EditString: Add
Autocompleteparameter (defaults to"one-time-code") to prevent browser extensions and autofill from intercepting Blazor input events on fields with IDs containing keywords like "email"
10.0.2
- Support .net 8,9,10
10.0.1
- Upgrade to .net 10
- Add the ability to hide the required star within FormOptions
- Changed editControls.js to edit-controls.js
1.13.8
- Exposed xmldoc comments
1.13.7
- refactoring
1.13.6
- Move the star for non-legends to the left.
1.13.5
- Enable tooltips through markup
- Move the required star to the left of the label
1.13.4
- EditDate and other controls. Add a null value string to display when the value is null, such as a dash instead of blank space.
- IsRequired parameter on all controls. When set forces the “edit-label-required-star” to show up without being required in the DataAnnotations.
- Accessibility updates for EditCheckedStringList
1.13.3
- Current stable release
- Full feature set with comprehensive validation support
1.0.13.2
- EditCheckedListEnum
1.0.13.1
- Rename icons to have edit- in front of the current names
- .icon-eye ⇒ .edit-icon-eye
- Icon-invalid, icon-eye-invisible
- EditSelectEnum no longer requires specifying the type.
- Tooltips exist on the controls
- Only from attributes right now [Tooltip(“My cool tooltip”)
1.0.12.11
- Import js into application in App.razor or index.html
-
<script src="_content/WssBlazorControls/editControls.js"></script> - This is to add the functionality of “When submit is clicked, but invalid, scroll to the first input that is invalid.
- Use JsInteropEc to access js methods. Use JsInteropEc.FocusFirstInvalidField() when there are validation errors while submitting.
-
- EditCheckedStringList
- Error message shows up on each checkbox
1.0.12.10
- IsRequired parameter on all controls. When set forces the “edit-label-required-star” to show up without being required in the DataAnnotations.
- Accessibility updates for EditCheckedStringList
1.0.12.x
- moved away from utilizing bootstrap css classes such as form-group to using classes that start with edit- to avoid conflicts with other libraries
- New Features
- IsHidden to hide controls withougt wrapping them in an if statement
- Hiding allows hiding controls based on their own property for [Never, WhenReadonlyAndNull, WhenReadonly, etc.]
- This also exists within FormOptions, so the hiding can be controlled over a large group of controls.
- Control Changes
- EditRadio and EditCheckedList
- Change parameter from HasHorizontalButtons → IsHorizontal
- Removed the need for "Type" parameter, now uses the type of the value passed in.
- EditSelectEnum
- Removed the need for "Type" parameter, now uses the type of the value passed in.
- New Controls
- EditBoolNullRadio
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.AspNetCore.Components.Web (>= 10.0.0)
-
net8.0
- Microsoft.AspNetCore.Components.Web (>= 8.0.0)
-
net9.0
- Microsoft.AspNetCore.Components.Web (>= 9.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on WssBlazorControls:
| Package | Downloads |
|---|---|
|
WssBlazorControls.Demo
Demo components for WssBlazorControls, showcasing usage of each control with interactive examples. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.3.0 | 40 | 6/14/2026 |
| 10.0.7 | 238 | 3/16/2026 |
| 10.0.6 | 163 | 2/9/2026 |
| 10.0.5 | 77 | 2/9/2026 |
| 10.0.4 | 80 | 2/9/2026 |
| 10.0.3 | 74 | 2/9/2026 |
| 10.0.2 | 156 | 11/23/2025 |
| 10.0.1 | 167 | 11/23/2025 |
| 1.13.8 | 395 | 11/18/2025 |
| 1.13.7 | 271 | 10/29/2025 |
| 1.13.6 | 179 | 10/29/2025 |
| 1.13.5 | 193 | 10/28/2025 |
| 1.13.4 | 185 | 9/24/2025 |
| 1.0.13.3 | 442 | 9/18/2025 |
| 1.0.13.2 | 431 | 8/21/2025 |
| 1.0.13.1 | 187 | 8/19/2025 |
| 1.0.12.10 | 207 | 8/12/2025 |
| 1.0.12.9 | 172 | 8/12/2025 |
| 1.0.12.8 | 178 | 8/12/2025 |
10.3.0 - Adds EditFile (multi-file drag-and-drop upload control), Table dynamic-column robustness, graceful sort degradation, and accessibility improvements to Skeleton and toasts. See the README changelog for the full list.